]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
hwmon: (spd5118) Add support for Renesas/ITD SPD5118 hub controllers
authorGuenter Roeck <linux@roeck-us.net>
Fri, 14 Jun 2024 15:27:21 +0000 (08:27 -0700)
committerGuenter Roeck <linux@roeck-us.net>
Wed, 19 Jun 2024 03:07:42 +0000 (20:07 -0700)
The SPD5118 specification says, in its documentation of the page bits
in the MR11 register:

"
This register only applies to non-volatile memory (1024) Bytes) access of
SPD5 Hub device.
For volatile memory access, this register must be programmed to '000'.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
"

Renesas/ITD SPD5118 hub controllers take this literally and disable access
to volatile memory if the page selected in MR11 is != 0. Since the BIOS or
ROMMON will access the non-volatile memory and likely select a page != 0,
this means that the driver will not instantiate since it can not identify
the chip. Even if the driver instantiates, access to volatile registers
is blocked after a nvram read operation which selects a page other than 0.

To solve the problem, add initialization code to select page 0 during
probe. Before doing that, use basic validation to ensure that this is
really a SPD5118 device and not some random EEPROM.

Cc: Sasha Kozachuk <skozachuk@google.com>
Cc: John Hamrick <johnham@google.com>
Cc: Chris Sarra <chrissarra@google.com>
Tested-by: Armin Wolf <W_Armin@gmx.de>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
drivers/hwmon/spd5118.c

index d405c5ef755db95ab7cd0cc65bb5c04e54ae1c23..fcbce5a01e5580afc54946dcc5eb168a2743473e 100644 (file)
@@ -513,6 +513,58 @@ static const struct regmap_config spd5118_regmap_config = {
        .num_ranges = ARRAY_SIZE(spd5118_regmap_range_cfg),
 };
 
+static int spd5118_init(struct i2c_client *client)
+{
+       struct i2c_adapter *adapter = client->adapter;
+       int err, regval, mode;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+                                    I2C_FUNC_SMBUS_WORD_DATA))
+               return -ENODEV;
+
+       regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
+       if (regval < 0 || (regval && regval != 0x5118))
+               return -ENODEV;
+
+       /*
+        * If the device type registers return 0, it is possible that the chip
+        * has a non-zero page selected and takes the specification literally,
+        * i.e. disables access to volatile registers besides the page register
+        * if the page is not 0. Try to identify such chips.
+        */
+       if (!regval) {
+               /* Vendor ID registers must also be 0 */
+               regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR);
+               if (regval)
+                       return -ENODEV;
+
+               /* The selected page in MR11 must not be 0 */
+               mode = i2c_smbus_read_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE);
+               if (mode < 0 || (mode & ~SPD5118_LEGACY_MODE_MASK) ||
+                   !(mode & SPD5118_LEGACY_PAGE_MASK))
+                       return -ENODEV;
+
+               err = i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE,
+                                               mode & SPD5118_LEGACY_MODE_ADDR);
+               if (err)
+                       return -ENODEV;
+
+               /*
+                * If the device type registers are still bad after selecting
+                * page 0, this is not a SPD5118 device. Restore original
+                * legacy mode register value and abort.
+                */
+               regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
+               if (regval != 0x5118) {
+                       i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE, mode);
+                       return -ENODEV;
+               }
+       }
+
+       /* We are reasonably sure that this is really a SPD5118 hub controller */
+       return 0;
+}
+
 static int spd5118_probe(struct i2c_client *client)
 {
        struct device *dev = &client->dev;
@@ -522,6 +574,10 @@ static int spd5118_probe(struct i2c_client *client)
        struct regmap *regmap;
        int err;
 
+       err = spd5118_init(client);
+       if (err)
+               return err;
+
        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;