i2c: mux: pca954x: verify the device id of the pca984x chips
authorPeter Rosin <peda@axentia.se>
Mon, 22 Jan 2018 07:40:02 +0000 (08:40 +0100)
committerPeter Rosin <peda@axentia.se>
Tue, 6 Mar 2018 09:57:59 +0000 (10:57 +0100)
Make sure to not disallow the chips on adapters that are not capable
of reading the device id, but also make sure to check the device id
before writing to the chip.

Tested-by: Adrian Fiergolski <adrian.fiergolski@cern.ch>
Signed-off-by: Peter Rosin <peda@axentia.se>
drivers/i2c/muxes/i2c-mux-pca954x.c

index fbb84c7ef282976a9a1d4a4f96185d448f60eeed..09bafd3e68faf8a5bd600cb0a8fd6464443985f3 100644 (file)
@@ -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 &&