| From 2938b2978a70d4cc10777ee71c9e512ffe4e0f4b Mon Sep 17 00:00:00 2001 |
| From: Paul Fertser <fercerpav@gmail.com> |
| Date: Fri, 24 Sep 2021 12:30:09 +0300 |
| Subject: hwmon: (tmp421) handle I2C errors |
| |
| From: Paul Fertser <fercerpav@gmail.com> |
| |
| commit 2938b2978a70d4cc10777ee71c9e512ffe4e0f4b upstream. |
| |
| Function i2c_smbus_read_byte_data() can return a negative error number |
| instead of the data read if I2C transaction failed for whatever reason. |
| |
| Lack of error checking can lead to serious issues on production |
| hardware, e.g. errors treated as temperatures produce spurious critical |
| temperature-crossed-threshold errors in BMC logs for OCP server |
| hardware. The patch was tested with Mellanox OCP Mezzanine card |
| emulating TMP421 protocol for temperature sensing which sometimes leads |
| to I2C protocol error during early boot up stage. |
| |
| Fixes: 9410700b881f ("hwmon: Add driver for Texas Instruments TMP421/422/423 sensor chips") |
| Cc: stable@vger.kernel.org |
| Signed-off-by: Paul Fertser <fercerpav@gmail.com> |
| Link: https://lore.kernel.org/r/20210924093011.26083-1-fercerpav@gmail.com |
| [groeck: dropped unnecessary line breaks] |
| Signed-off-by: Guenter Roeck <linux@roeck-us.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/hwmon/tmp421.c | 38 ++++++++++++++++++++++++++++---------- |
| 1 file changed, 28 insertions(+), 10 deletions(-) |
| |
| --- a/drivers/hwmon/tmp421.c |
| +++ b/drivers/hwmon/tmp421.c |
| @@ -119,38 +119,56 @@ static int temp_from_u16(u16 reg) |
| return (temp * 1000 + 128) / 256; |
| } |
| |
| -static struct tmp421_data *tmp421_update_device(struct device *dev) |
| +static int tmp421_update_device(struct tmp421_data *data) |
| { |
| - struct tmp421_data *data = dev_get_drvdata(dev); |
| struct i2c_client *client = data->client; |
| + int ret = 0; |
| int i; |
| |
| mutex_lock(&data->update_lock); |
| |
| if (time_after(jiffies, data->last_updated + (HZ / 2)) || |
| !data->valid) { |
| - data->config = i2c_smbus_read_byte_data(client, |
| - TMP421_CONFIG_REG_1); |
| + ret = i2c_smbus_read_byte_data(client, TMP421_CONFIG_REG_1); |
| + if (ret < 0) |
| + goto exit; |
| + data->config = ret; |
| |
| for (i = 0; i < data->channels; i++) { |
| - data->temp[i] = i2c_smbus_read_byte_data(client, |
| - TMP421_TEMP_MSB[i]) << 8; |
| - data->temp[i] |= i2c_smbus_read_byte_data(client, |
| - TMP421_TEMP_LSB[i]); |
| + ret = i2c_smbus_read_byte_data(client, TMP421_TEMP_MSB[i]); |
| + if (ret < 0) |
| + goto exit; |
| + data->temp[i] = ret << 8; |
| + |
| + ret = i2c_smbus_read_byte_data(client, TMP421_TEMP_LSB[i]); |
| + if (ret < 0) |
| + goto exit; |
| + data->temp[i] |= ret; |
| } |
| data->last_updated = jiffies; |
| data->valid = 1; |
| } |
| |
| +exit: |
| mutex_unlock(&data->update_lock); |
| |
| - return data; |
| + if (ret < 0) { |
| + data->valid = 0; |
| + return ret; |
| + } |
| + |
| + return 0; |
| } |
| |
| static int tmp421_read(struct device *dev, enum hwmon_sensor_types type, |
| u32 attr, int channel, long *val) |
| { |
| - struct tmp421_data *tmp421 = tmp421_update_device(dev); |
| + struct tmp421_data *tmp421 = dev_get_drvdata(dev); |
| + int ret = 0; |
| + |
| + ret = tmp421_update_device(tmp421); |
| + if (ret) |
| + return ret; |
| |
| switch (attr) { |
| case hwmon_temp_input: |