diff options
author | Wolfram Sang <wsa@the-dreams.de> | 2018-03-17 21:11:23 +0100 |
---|---|---|
committer | Wolfram Sang <wsa@the-dreams.de> | 2018-03-17 21:11:23 +0100 |
commit | 8efb11adeeb37a4701f0915d66eeb912cf97aa3e (patch) | |
tree | 40b704ed63f1e10b7a50fc14dd5ea19bce22b570 /drivers | |
parent | i2c: core: report OF style module alias for devices registered via OF (diff) | |
parent | i2c: mux: pca954x: verify the device id of the pca984x chips (diff) | |
download | linux-8efb11adeeb37a4701f0915d66eeb912cf97aa3e.tar.xz linux-8efb11adeeb37a4701f0915d66eeb912cf97aa3e.zip |
Merge branch 'i2c-mux/for-next' of https://github.com/peda-r/i2c-mux into i2c/for-4.17
"These patches verify the device id of the PCA984x mux chips using
standardized (but rarely implemented) i2c device identification."
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/i2c/i2c-core-base.c | 33 | ||||
-rw-r--r-- | drivers/i2c/muxes/i2c-mux-pca954x.c | 55 |
2 files changed, 82 insertions, 6 deletions
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index edfc23e49630..16a3b73375a6 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -58,6 +58,8 @@ #define I2C_ADDR_7BITS_MAX 0x77 #define I2C_ADDR_7BITS_COUNT (I2C_ADDR_7BITS_MAX + 1) +#define I2C_ADDR_DEVICE_ID 0x7c + /* * core_lock protects i2c_adapter_idr, and guarantees that device detection, * deletion of detected devices, and attach_adapter calls are serialized @@ -1976,6 +1978,37 @@ int i2c_transfer_buffer_flags(const struct i2c_client *client, char *buf, } EXPORT_SYMBOL(i2c_transfer_buffer_flags); +/** + * i2c_get_device_id - get manufacturer, part id and die revision of a device + * @client: The device to query + * @id: The queried information + * + * Returns negative errno on error, zero on success. + */ +int i2c_get_device_id(const struct i2c_client *client, + struct i2c_device_identity *id) +{ + struct i2c_adapter *adap = client->adapter; + union i2c_smbus_data raw_id; + int ret; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + return -EOPNOTSUPP; + + raw_id.block[0] = 3; + ret = i2c_smbus_xfer(adap, I2C_ADDR_DEVICE_ID, 0, + I2C_SMBUS_READ, client->addr << 1, + I2C_SMBUS_I2C_BLOCK_DATA, &raw_id); + if (ret) + return ret; + + id->manufacturer_id = (raw_id.block[1] << 4) | (raw_id.block[2] >> 4); + id->part_id = ((raw_id.block[2] & 0xf) << 5) | (raw_id.block[3] >> 3); + id->die_revision = raw_id.block[3] & 0x7; + return 0; +} +EXPORT_SYMBOL_GPL(i2c_get_device_id); + /* ---------------------------------------------------- * the i2c address scanning function * Will not work for 10-bit addresses! diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index fbb84c7ef282..09bafd3e68fa 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -77,6 +77,7 @@ struct chip_desc { pca954x_ismux = 0, pca954x_isswi } muxtype; + struct i2c_device_identity id; }; struct pca954x { @@ -97,59 +98,83 @@ static const struct chip_desc chips[] = { .nchans = 2, .enable = 0x4, .muxtype = pca954x_ismux, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, }, [pca_9542] = { .nchans = 2, .enable = 0x4, .has_irq = 1, .muxtype = pca954x_ismux, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, }, [pca_9543] = { .nchans = 2, .has_irq = 1, .muxtype = pca954x_isswi, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, }, [pca_9544] = { .nchans = 4, .enable = 0x4, .has_irq = 1, .muxtype = pca954x_ismux, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, }, [pca_9545] = { .nchans = 4, .has_irq = 1, .muxtype = pca954x_isswi, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, }, [pca_9546] = { .nchans = 4, .muxtype = pca954x_isswi, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, }, [pca_9547] = { .nchans = 8, .enable = 0x8, .muxtype = pca954x_ismux, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, }, [pca_9548] = { .nchans = 8, .muxtype = pca954x_isswi, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, }, [pca_9846] = { .nchans = 4, .muxtype = pca954x_isswi, + .id = { + .manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS, + .part_id = 0x10b, + }, }, [pca_9847] = { .nchans = 8, .enable = 0x8, .muxtype = pca954x_ismux, + .id = { + .manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS, + .part_id = 0x108, + }, }, [pca_9848] = { .nchans = 8, .muxtype = pca954x_isswi, + .id = { + .manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS, + .part_id = 0x10a, + }, }, [pca_9849] = { .nchans = 4, .enable = 0x4, .muxtype = pca954x_ismux, + .id = { + .manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS, + .part_id = 0x109, + }, }, }; @@ -369,6 +394,30 @@ static int pca954x_probe(struct i2c_client *client, if (IS_ERR(gpio)) return PTR_ERR(gpio); + match = of_match_device(of_match_ptr(pca954x_of_match), &client->dev); + if (match) + data->chip = of_device_get_match_data(&client->dev); + else + data->chip = &chips[id->driver_data]; + + if (data->chip->id.manufacturer_id != I2C_DEVICE_ID_NONE) { + struct i2c_device_identity id; + + ret = i2c_get_device_id(client, &id); + if (ret && ret != -EOPNOTSUPP) + return ret; + + if (!ret && + (id.manufacturer_id != data->chip->id.manufacturer_id || + id.part_id != data->chip->id.part_id)) { + dev_warn(&client->dev, + "unexpected device id %03x-%03x-%x\n", + id.manufacturer_id, id.part_id, + id.die_revision); + return -ENODEV; + } + } + /* Write the mux register at addr to verify * that the mux is in fact present. This also * initializes the mux to disconnected state. @@ -378,12 +427,6 @@ static int pca954x_probe(struct i2c_client *client, return -ENODEV; } - match = of_match_device(of_match_ptr(pca954x_of_match), &client->dev); - if (match) - data->chip = of_device_get_match_data(&client->dev); - else - data->chip = &chips[id->driver_data]; - data->last_chan = 0; /* force the first selection */ idle_disconnect_dt = of_node && |