summaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
authorWolfram Sang <wsa@the-dreams.de>2018-03-17 21:11:23 +0100
committerWolfram Sang <wsa@the-dreams.de>2018-03-17 21:11:23 +0100
commit8efb11adeeb37a4701f0915d66eeb912cf97aa3e (patch)
tree40b704ed63f1e10b7a50fc14dd5ea19bce22b570 /drivers/i2c
parentaf503716ac1444db61d80cb6d17cfe62929c21df (diff)
parent2d74187d5b4e29776e151ffcc09bdaedfd46e002 (diff)
downloadlinux-8efb11adeeb37a4701f0915d66eeb912cf97aa3e.tar.bz2
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/i2c')
-rw-r--r--drivers/i2c/i2c-core-base.c33
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca954x.c55
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 &&