* [PATCH v2 1/3] hwmon: (lm87) Propagate sysfs write errors
2026-10-29 16:40 ` [PATCH v2 0/3] hwmon: (lm87) Handle I2C read and " Baek Kyu, Kim
@ 2026-10-29 16:40 ` Baek Kyu, Kim
2026-10-29 16:44 ` sashiko-bot
2026-10-29 16:40 ` [PATCH v2 2/3] hwmon: (lm87) Propagate cached sensor read errors Baek Kyu, Kim
` (2 subsequent siblings)
3 siblings, 1 reply; 11+ messages in thread
From: Baek Kyu, Kim @ 2026-10-29 16:40 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-hwmon, linux-kernel
Check the I2C result before updating cached values for writable
attributes. Handle both writes in fan divider changes separately so a
failed second write does not hide the first one.
Signed-off-by: Baek Kyu, Kim <pocj8ur4in@gmail.com>
---
drivers/hwmon/lm87.c | 96 ++++++++++++++++++++++++++++----------------
1 file changed, 61 insertions(+), 35 deletions(-)
diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c
index c09074f44708..c118a7ff718b 100644
--- a/drivers/hwmon/lm87.c
+++ b/drivers/hwmon/lm87.c
@@ -305,6 +305,7 @@ static ssize_t in_min_store(struct device *dev, struct device_attribute *attr,
struct i2c_client *client = dev_get_drvdata(dev);
struct lm87_data *data = i2c_get_clientdata(client);
int nr = to_sensor_dev_attr(attr)->index;
+ u8 reg;
long val;
int err;
@@ -312,12 +313,14 @@ static ssize_t in_min_store(struct device *dev, struct device_attribute *attr,
if (err)
return err;
+ reg = IN_TO_REG(val, data->in_scale[nr]);
mutex_lock(&data->update_lock);
- data->in_min[nr] = IN_TO_REG(val, data->in_scale[nr]);
- lm87_write_value(client, nr < 6 ? LM87_REG_IN_MIN(nr) :
- LM87_REG_AIN_MIN(nr - 6), data->in_min[nr]);
+ err = lm87_write_value(client, nr < 6 ? LM87_REG_IN_MIN(nr) :
+ LM87_REG_AIN_MIN(nr - 6), reg);
+ if (!err)
+ data->in_min[nr] = reg;
mutex_unlock(&data->update_lock);
- return count;
+ return err < 0 ? err : count;
}
static ssize_t in_max_store(struct device *dev, struct device_attribute *attr,
@@ -326,6 +329,7 @@ static ssize_t in_max_store(struct device *dev, struct device_attribute *attr,
struct i2c_client *client = dev_get_drvdata(dev);
struct lm87_data *data = i2c_get_clientdata(client);
int nr = to_sensor_dev_attr(attr)->index;
+ u8 reg;
long val;
int err;
@@ -333,12 +337,14 @@ static ssize_t in_max_store(struct device *dev, struct device_attribute *attr,
if (err)
return err;
+ reg = IN_TO_REG(val, data->in_scale[nr]);
mutex_lock(&data->update_lock);
- data->in_max[nr] = IN_TO_REG(val, data->in_scale[nr]);
- lm87_write_value(client, nr < 6 ? LM87_REG_IN_MAX(nr) :
- LM87_REG_AIN_MAX(nr - 6), data->in_max[nr]);
+ err = lm87_write_value(client, nr < 6 ? LM87_REG_IN_MAX(nr) :
+ LM87_REG_AIN_MAX(nr - 6), reg);
+ if (!err)
+ data->in_max[nr] = reg;
mutex_unlock(&data->update_lock);
- return count;
+ return err < 0 ? err : count;
}
static SENSOR_DEVICE_ATTR_RO(in0_input, in_input, 0);
@@ -402,6 +408,7 @@ static ssize_t temp_low_store(struct device *dev,
struct i2c_client *client = dev_get_drvdata(dev);
struct lm87_data *data = i2c_get_clientdata(client);
int nr = to_sensor_dev_attr(attr)->index;
+ u8 reg;
long val;
int err;
@@ -409,11 +416,13 @@ static ssize_t temp_low_store(struct device *dev,
if (err)
return err;
+ reg = TEMP_TO_REG(val);
mutex_lock(&data->update_lock);
- data->temp_low[nr] = TEMP_TO_REG(val);
- lm87_write_value(client, LM87_REG_TEMP_LOW[nr], data->temp_low[nr]);
+ err = lm87_write_value(client, LM87_REG_TEMP_LOW[nr], reg);
+ if (!err)
+ data->temp_low[nr] = reg;
mutex_unlock(&data->update_lock);
- return count;
+ return err < 0 ? err : count;
}
static ssize_t temp_high_store(struct device *dev,
@@ -423,6 +432,7 @@ static ssize_t temp_high_store(struct device *dev,
struct i2c_client *client = dev_get_drvdata(dev);
struct lm87_data *data = i2c_get_clientdata(client);
int nr = to_sensor_dev_attr(attr)->index;
+ u8 reg;
long val;
int err;
@@ -430,11 +440,13 @@ static ssize_t temp_high_store(struct device *dev,
if (err)
return err;
+ reg = TEMP_TO_REG(val);
mutex_lock(&data->update_lock);
- data->temp_high[nr] = TEMP_TO_REG(val);
- lm87_write_value(client, LM87_REG_TEMP_HIGH[nr], data->temp_high[nr]);
+ err = lm87_write_value(client, LM87_REG_TEMP_HIGH[nr], reg);
+ if (!err)
+ data->temp_high[nr] = reg;
mutex_unlock(&data->update_lock);
- return count;
+ return err < 0 ? err : count;
}
static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_input, 0);
@@ -502,6 +514,7 @@ static ssize_t fan_min_store(struct device *dev,
struct i2c_client *client = dev_get_drvdata(dev);
struct lm87_data *data = i2c_get_clientdata(client);
int nr = to_sensor_dev_attr(attr)->index;
+ u8 reg;
long val;
int err;
@@ -510,11 +523,12 @@ static ssize_t fan_min_store(struct device *dev,
return err;
mutex_lock(&data->update_lock);
- data->fan_min[nr] = FAN_TO_REG(val,
- FAN_DIV_FROM_REG(data->fan_div[nr]));
- lm87_write_value(client, LM87_REG_FAN_MIN(nr), data->fan_min[nr]);
+ reg = FAN_TO_REG(val, FAN_DIV_FROM_REG(data->fan_div[nr]));
+ err = lm87_write_value(client, LM87_REG_FAN_MIN(nr), reg);
+ if (!err)
+ data->fan_min[nr] = reg;
mutex_unlock(&data->update_lock);
- return count;
+ return err < 0 ? err : count;
}
/*
@@ -533,7 +547,7 @@ static ssize_t fan_div_store(struct device *dev,
long val;
int err;
unsigned long min;
- u8 reg;
+ u8 reg, div, fan_min;
err = kstrtol(buf, 10, &val);
if (err)
@@ -545,39 +559,47 @@ static ssize_t fan_div_store(struct device *dev,
switch (val) {
case 1:
- data->fan_div[nr] = 0;
+ div = 0;
break;
case 2:
- data->fan_div[nr] = 1;
+ div = 1;
break;
case 4:
- data->fan_div[nr] = 2;
+ div = 2;
break;
case 8:
- data->fan_div[nr] = 3;
+ div = 3;
break;
default:
mutex_unlock(&data->update_lock);
return -EINVAL;
}
- reg = lm87_read_value(client, LM87_REG_VID_FAN_DIV);
+ err = lm87_read_value(client, LM87_REG_VID_FAN_DIV);
+ if (err < 0)
+ goto out;
+ reg = err;
switch (nr) {
case 0:
- reg = (reg & 0xCF) | (data->fan_div[0] << 4);
+ reg = (reg & 0xCF) | (div << 4);
break;
case 1:
- reg = (reg & 0x3F) | (data->fan_div[1] << 6);
+ reg = (reg & 0x3F) | (div << 6);
break;
}
- lm87_write_value(client, LM87_REG_VID_FAN_DIV, reg);
-
- data->fan_min[nr] = FAN_TO_REG(min, val);
- lm87_write_value(client, LM87_REG_FAN_MIN(nr),
- data->fan_min[nr]);
+ err = lm87_write_value(client, LM87_REG_VID_FAN_DIV, reg);
+ if (err < 0)
+ goto out;
+ data->fan_div[nr] = div;
+
+ fan_min = FAN_TO_REG(min, val);
+ err = lm87_write_value(client, LM87_REG_FAN_MIN(nr), fan_min);
+ if (!err)
+ data->fan_min[nr] = fan_min;
+out:
mutex_unlock(&data->update_lock);
- return count;
+ return err < 0 ? err : count;
}
static SENSOR_DEVICE_ATTR_RO(fan1_input, fan_input, 0);
@@ -640,6 +662,7 @@ static ssize_t aout_output_store(struct device *dev,
{
struct i2c_client *client = dev_get_drvdata(dev);
struct lm87_data *data = i2c_get_clientdata(client);
+ u8 reg;
long val;
int err;
@@ -647,11 +670,14 @@ static ssize_t aout_output_store(struct device *dev,
if (err)
return err;
+ reg = AOUT_TO_REG(val);
+
mutex_lock(&data->update_lock);
- data->aout = AOUT_TO_REG(val);
- lm87_write_value(client, LM87_REG_AOUT, data->aout);
+ err = lm87_write_value(client, LM87_REG_AOUT, reg);
+ if (!err)
+ data->aout = reg;
mutex_unlock(&data->update_lock);
- return count;
+ return err < 0 ? err : count;
}
static DEVICE_ATTR_RW(aout_output);
--
2.55.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v2 2/3] hwmon: (lm87) Propagate cached sensor read errors
2026-10-29 16:40 ` [PATCH v2 0/3] hwmon: (lm87) Handle I2C read and " Baek Kyu, Kim
2026-10-29 16:40 ` [PATCH v2 1/3] hwmon: (lm87) Propagate sysfs " Baek Kyu, Kim
@ 2026-10-29 16:40 ` Baek Kyu, Kim
2026-10-29 16:48 ` sashiko-bot
2026-10-29 16:40 ` [PATCH v2 3/3] hwmon: (lm87) Check I2C errors during detection and setup Baek Kyu, Kim
2026-10-29 19:46 ` [PATCH v2 0/3] hwmon: (lm87) Handle I2C read and write errors Guenter Roeck
3 siblings, 1 reply; 11+ messages in thread
From: Baek Kyu, Kim @ 2026-10-29 16:40 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-hwmon, linux-kernel
Abort cache refresh when an I2C read fails and pass the error to
sysfs readers instead of returning stale or invalid sensor values.
Read the alarm registers last, since reading them can clear latched
alarms. Preserve the first alarm byte if reading the second fails,
so a retry can still report the captured alarm.
Signed-off-by: Baek Kyu, Kim <pocj8ur4in@gmail.com>
---
drivers/hwmon/lm87.c | 202 +++++++++++++++++++++++++++++++++----------
1 file changed, 158 insertions(+), 44 deletions(-)
diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c
index c118a7ff718b..a66d486b314c 100644
--- a/drivers/hwmon/lm87.c
+++ b/drivers/hwmon/lm87.c
@@ -170,6 +170,7 @@ struct lm87_data {
u8 aout; /* register value */
u16 alarms; /* register values, combined */
+ u16 pending_alarms; /* alarms read before an I2C error */
u8 vid; /* register values, combined */
u8 vrm;
@@ -186,75 +187,138 @@ static inline int lm87_write_value(struct i2c_client *client, u8 reg, u8 value)
return i2c_smbus_write_byte_data(client, reg, value);
}
+static int lm87_read_reg(struct i2c_client *client, u8 reg, u8 *value)
+{
+ int ret = lm87_read_value(client, reg);
+
+ if (ret < 0)
+ return ret;
+ *value = ret;
+ return 0;
+}
+
+static int lm87_read_temp(struct i2c_client *client, u8 reg, s8 *value)
+{
+ int ret = lm87_read_value(client, reg);
+
+ if (ret < 0)
+ return ret;
+ *value = ret;
+ return 0;
+}
+
static struct lm87_data *lm87_update_device(struct device *dev)
{
struct i2c_client *client = dev_get_drvdata(dev);
struct lm87_data *data = i2c_get_clientdata(client);
+ int i, j, ret;
+ u8 first, second;
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
- int i, j;
-
dev_dbg(&client->dev, "Updating data.\n");
i = (data->channel & CHAN_TEMP3) ? 1 : 0;
j = (data->channel & CHAN_TEMP3) ? 5 : 6;
for (; i < j; i++) {
- data->in[i] = lm87_read_value(client,
- LM87_REG_IN(i));
- data->in_min[i] = lm87_read_value(client,
- LM87_REG_IN_MIN(i));
- data->in_max[i] = lm87_read_value(client,
- LM87_REG_IN_MAX(i));
+ ret = lm87_read_reg(client, LM87_REG_IN(i), &data->in[i]);
+ if (ret < 0)
+ goto error;
+ ret = lm87_read_reg(client, LM87_REG_IN_MIN(i),
+ &data->in_min[i]);
+ if (ret < 0)
+ goto error;
+ ret = lm87_read_reg(client, LM87_REG_IN_MAX(i),
+ &data->in_max[i]);
+ if (ret < 0)
+ goto error;
}
for (i = 0; i < 2; i++) {
if (data->channel & CHAN_NO_FAN(i)) {
- data->in[6+i] = lm87_read_value(client,
- LM87_REG_AIN(i));
- data->in_max[6+i] = lm87_read_value(client,
- LM87_REG_AIN_MAX(i));
- data->in_min[6+i] = lm87_read_value(client,
- LM87_REG_AIN_MIN(i));
-
+ ret = lm87_read_reg(client, LM87_REG_AIN(i),
+ &data->in[6 + i]);
+ if (ret < 0)
+ goto error;
+ ret = lm87_read_reg(client, LM87_REG_AIN_MAX(i),
+ &data->in_max[6 + i]);
+ if (ret < 0)
+ goto error;
+ ret = lm87_read_reg(client, LM87_REG_AIN_MIN(i),
+ &data->in_min[6 + i]);
+ if (ret < 0)
+ goto error;
} else {
- data->fan[i] = lm87_read_value(client,
- LM87_REG_FAN(i));
- data->fan_min[i] = lm87_read_value(client,
- LM87_REG_FAN_MIN(i));
+ ret = lm87_read_reg(client, LM87_REG_FAN(i),
+ &data->fan[i]);
+ if (ret < 0)
+ goto error;
+ ret = lm87_read_reg(client, LM87_REG_FAN_MIN(i),
+ &data->fan_min[i]);
+ if (ret < 0)
+ goto error;
}
}
j = (data->channel & CHAN_TEMP3) ? 3 : 2;
for (i = 0 ; i < j; i++) {
- data->temp[i] = lm87_read_value(client,
- LM87_REG_TEMP[i]);
- data->temp_high[i] = lm87_read_value(client,
- LM87_REG_TEMP_HIGH[i]);
- data->temp_low[i] = lm87_read_value(client,
- LM87_REG_TEMP_LOW[i]);
+ ret = lm87_read_temp(client, LM87_REG_TEMP[i],
+ &data->temp[i]);
+ if (ret < 0)
+ goto error;
+ ret = lm87_read_temp(client, LM87_REG_TEMP_HIGH[i],
+ &data->temp_high[i]);
+ if (ret < 0)
+ goto error;
+ ret = lm87_read_temp(client, LM87_REG_TEMP_LOW[i],
+ &data->temp_low[i]);
+ if (ret < 0)
+ goto error;
}
- i = lm87_read_value(client, LM87_REG_TEMP_HW_INT_LOCK);
- j = lm87_read_value(client, LM87_REG_TEMP_HW_INT);
- data->temp_crit_int = min(i, j);
-
- i = lm87_read_value(client, LM87_REG_TEMP_HW_EXT_LOCK);
- j = lm87_read_value(client, LM87_REG_TEMP_HW_EXT);
- data->temp_crit_ext = min(i, j);
-
- i = lm87_read_value(client, LM87_REG_VID_FAN_DIV);
- data->fan_div[0] = (i >> 4) & 0x03;
- data->fan_div[1] = (i >> 6) & 0x03;
- data->vid = (i & 0x0F)
- | (lm87_read_value(client, LM87_REG_VID4) & 0x01)
- << 4;
-
- data->alarms = lm87_read_value(client, LM87_REG_ALARMS1)
- | (lm87_read_value(client, LM87_REG_ALARMS2)
- << 8);
- data->aout = lm87_read_value(client, LM87_REG_AOUT);
+ ret = lm87_read_reg(client, LM87_REG_TEMP_HW_INT_LOCK,
+ &first);
+ if (ret < 0)
+ goto error;
+ ret = lm87_read_reg(client, LM87_REG_TEMP_HW_INT, &second);
+ if (ret < 0)
+ goto error;
+ data->temp_crit_int = min(first, second);
+
+ ret = lm87_read_reg(client, LM87_REG_TEMP_HW_EXT_LOCK,
+ &first);
+ if (ret < 0)
+ goto error;
+ ret = lm87_read_reg(client, LM87_REG_TEMP_HW_EXT, &second);
+ if (ret < 0)
+ goto error;
+ data->temp_crit_ext = min(first, second);
+
+ ret = lm87_read_reg(client, LM87_REG_VID_FAN_DIV, &first);
+ if (ret < 0)
+ goto error;
+ data->fan_div[0] = (first >> 4) & 0x03;
+ data->fan_div[1] = (first >> 6) & 0x03;
+ ret = lm87_read_reg(client, LM87_REG_VID4, &second);
+ if (ret < 0)
+ goto error;
+ data->vid = (first & 0x0F) | (second & 0x01) << 4;
+
+ ret = lm87_read_reg(client, LM87_REG_AOUT, &data->aout);
+ if (ret < 0)
+ goto error;
+
+ ret = lm87_read_reg(client, LM87_REG_ALARMS1, &first);
+ if (ret < 0)
+ goto error;
+ ret = lm87_read_reg(client, LM87_REG_ALARMS2, &second);
+ if (ret < 0) {
+ data->pending_alarms |= first;
+ goto error;
+ }
+ data->alarms = data->pending_alarms | first | second << 8;
+ data->pending_alarms = 0;
data->last_updated = jiffies;
data->valid = true;
@@ -263,6 +327,11 @@ static struct lm87_data *lm87_update_device(struct device *dev)
mutex_unlock(&data->update_lock);
return data;
+
+error:
+ data->valid = false;
+ mutex_unlock(&data->update_lock);
+ return ERR_PTR(ret);
}
/*
@@ -275,6 +344,9 @@ static ssize_t in_input_show(struct device *dev,
struct lm87_data *data = lm87_update_device(dev);
int nr = to_sensor_dev_attr(attr)->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%u\n", IN_FROM_REG(data->in[nr],
data->in_scale[nr]));
}
@@ -285,6 +357,9 @@ static ssize_t in_min_show(struct device *dev, struct device_attribute *attr,
struct lm87_data *data = lm87_update_device(dev);
int nr = to_sensor_dev_attr(attr)->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[nr],
data->in_scale[nr]));
}
@@ -295,6 +370,9 @@ static ssize_t in_max_show(struct device *dev, struct device_attribute *attr,
struct lm87_data *data = lm87_update_device(dev);
int nr = to_sensor_dev_attr(attr)->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[nr],
data->in_scale[nr]));
}
@@ -378,6 +456,9 @@ static ssize_t temp_input_show(struct device *dev,
struct lm87_data *data = lm87_update_device(dev);
int nr = to_sensor_dev_attr(attr)->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr]));
}
@@ -387,6 +468,9 @@ static ssize_t temp_low_show(struct device *dev,
struct lm87_data *data = lm87_update_device(dev);
int nr = to_sensor_dev_attr(attr)->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n",
TEMP_FROM_REG(data->temp_low[nr]));
}
@@ -397,6 +481,9 @@ static ssize_t temp_high_show(struct device *dev,
struct lm87_data *data = lm87_update_device(dev);
int nr = to_sensor_dev_attr(attr)->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n",
TEMP_FROM_REG(data->temp_high[nr]));
}
@@ -463,6 +550,9 @@ static ssize_t temp1_crit_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct lm87_data *data = lm87_update_device(dev);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_int));
}
@@ -470,6 +560,9 @@ static ssize_t temp2_crit_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct lm87_data *data = lm87_update_device(dev);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_ext));
}
@@ -483,6 +576,9 @@ static ssize_t fan_input_show(struct device *dev,
struct lm87_data *data = lm87_update_device(dev);
int nr = to_sensor_dev_attr(attr)->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", fan_from_reg(data->fan[nr],
FAN_DIV_FROM_REG(data->fan_div[nr])));
}
@@ -493,6 +589,9 @@ static ssize_t fan_min_show(struct device *dev, struct device_attribute *attr,
struct lm87_data *data = lm87_update_device(dev);
int nr = to_sensor_dev_attr(attr)->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", fan_from_reg(data->fan_min[nr],
FAN_DIV_FROM_REG(data->fan_div[nr])));
}
@@ -503,6 +602,9 @@ static ssize_t fan_div_show(struct device *dev, struct device_attribute *attr,
struct lm87_data *data = lm87_update_device(dev);
int nr = to_sensor_dev_attr(attr)->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n",
FAN_DIV_FROM_REG(data->fan_div[nr]));
}
@@ -613,6 +715,9 @@ static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct lm87_data *data = lm87_update_device(dev);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", data->alarms);
}
static DEVICE_ATTR_RO(alarms);
@@ -621,6 +726,9 @@ static ssize_t cpu0_vid_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct lm87_data *data = lm87_update_device(dev);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
}
static DEVICE_ATTR_RO(cpu0_vid);
@@ -654,6 +762,9 @@ static ssize_t aout_output_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct lm87_data *data = lm87_update_device(dev);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", AOUT_FROM_REG(data->aout));
}
static ssize_t aout_output_store(struct device *dev,
@@ -686,6 +797,9 @@ static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
{
struct lm87_data *data = lm87_update_device(dev);
int bitnr = to_sensor_dev_attr(attr)->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
}
static SENSOR_DEVICE_ATTR_RO(in0_alarm, alarm, 0);
--
2.55.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v2 3/3] hwmon: (lm87) Check I2C errors during detection and setup
2026-10-29 16:40 ` [PATCH v2 0/3] hwmon: (lm87) Handle I2C read and " Baek Kyu, Kim
2026-10-29 16:40 ` [PATCH v2 1/3] hwmon: (lm87) Propagate sysfs " Baek Kyu, Kim
2026-10-29 16:40 ` [PATCH v2 2/3] hwmon: (lm87) Propagate cached sensor read errors Baek Kyu, Kim
@ 2026-10-29 16:40 ` Baek Kyu, Kim
2026-10-29 16:50 ` sashiko-bot
2026-10-29 19:46 ` [PATCH v2 0/3] hwmon: (lm87) Handle I2C read and write errors Guenter Roeck
3 siblings, 1 reply; 11+ messages in thread
From: Baek Kyu, Kim @ 2026-10-29 16:40 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-hwmon, linux-kernel
Reject devices whose identification registers cannot be read, fail
initialization on transfer errors, and report configuration restore
failures.
Signed-off-by: Baek Kyu, Kim <pocj8ur4in@gmail.com>
---
drivers/hwmon/lm87.c | 86 +++++++++++++++++++++++++++++++++-----------
1 file changed, 65 insertions(+), 21 deletions(-)
diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c
index a66d486b314c..32ec44f583eb 100644
--- a/drivers/hwmon/lm87.c
+++ b/drivers/hwmon/lm87.c
@@ -955,17 +955,22 @@ static int lm87_detect(struct i2c_client *client, struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
const char *name;
- u8 cid, rev;
+ int cid, rev, config;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- if (lm87_read_value(client, LM87_REG_CONFIG) & 0x80)
+ config = lm87_read_value(client, LM87_REG_CONFIG);
+ if (config < 0 || config & 0x80)
return -ENODEV;
/* Now, we do the remaining detection. */
cid = lm87_read_value(client, LM87_REG_COMPANY_ID);
+ if (cid < 0)
+ return -ENODEV;
rev = lm87_read_value(client, LM87_REG_REVISION);
+ if (rev < 0)
+ return -ENODEV;
if (cid == 0x02 /* National Semiconductor */
&& (rev >= 0x01 && rev <= 0x08))
@@ -989,7 +994,12 @@ static void lm87_restore_config(void *arg)
struct i2c_client *client = arg;
struct lm87_data *data = i2c_get_clientdata(client);
- lm87_write_value(client, LM87_REG_CONFIG, data->config);
+ int err;
+
+ err = lm87_write_value(client, LM87_REG_CONFIG, data->config);
+ if (err < 0)
+ dev_warn(&client->dev, "Failed to restore configuration: %d\n",
+ err);
}
static int lm87_init_client(struct i2c_client *client)
@@ -1013,16 +1023,26 @@ static int lm87_init_client(struct i2c_client *client)
val |= CHAN_VCC_5V;
}
data->channel = val;
- lm87_write_value(client,
- LM87_REG_CHANNEL_MODE, data->channel);
+ rc = lm87_write_value(client, LM87_REG_CHANNEL_MODE,
+ data->channel);
+ if (rc < 0)
+ return rc;
} else if (dev_get_platdata(&client->dev)) {
data->channel = *(u8 *)dev_get_platdata(&client->dev);
- lm87_write_value(client,
- LM87_REG_CHANNEL_MODE, data->channel);
+ rc = lm87_write_value(client, LM87_REG_CHANNEL_MODE,
+ data->channel);
+ if (rc < 0)
+ return rc;
} else {
- data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE);
+ rc = lm87_read_value(client, LM87_REG_CHANNEL_MODE);
+ if (rc < 0)
+ return rc;
+ data->channel = rc;
}
- data->config = lm87_read_value(client, LM87_REG_CONFIG) & 0x6F;
+ rc = lm87_read_value(client, LM87_REG_CONFIG);
+ if (rc < 0)
+ return rc;
+ data->config = rc & 0x6F;
rc = devm_add_action(&client->dev, lm87_restore_config, client);
if (rc)
@@ -1033,28 +1053,52 @@ static int lm87_init_client(struct i2c_client *client)
/* Limits are left uninitialized after power-up */
for (i = 1; i < 6; i++) {
- lm87_write_value(client, LM87_REG_IN_MIN(i), 0x00);
- lm87_write_value(client, LM87_REG_IN_MAX(i), 0xFF);
+ rc = lm87_write_value(client, LM87_REG_IN_MIN(i), 0x00);
+ if (rc < 0)
+ return rc;
+ rc = lm87_write_value(client, LM87_REG_IN_MAX(i), 0xFF);
+ if (rc < 0)
+ return rc;
}
for (i = 0; i < 2; i++) {
- lm87_write_value(client, LM87_REG_TEMP_HIGH[i], 0x7F);
- lm87_write_value(client, LM87_REG_TEMP_LOW[i], 0x00);
- lm87_write_value(client, LM87_REG_AIN_MIN(i), 0x00);
- lm87_write_value(client, LM87_REG_AIN_MAX(i), 0xFF);
+ rc = lm87_write_value(client, LM87_REG_TEMP_HIGH[i],
+ 0x7F);
+ if (rc < 0)
+ return rc;
+ rc = lm87_write_value(client, LM87_REG_TEMP_LOW[i],
+ 0x00);
+ if (rc < 0)
+ return rc;
+ rc = lm87_write_value(client, LM87_REG_AIN_MIN(i), 0x00);
+ if (rc < 0)
+ return rc;
+ rc = lm87_write_value(client, LM87_REG_AIN_MAX(i), 0xFF);
+ if (rc < 0)
+ return rc;
}
if (data->channel & CHAN_TEMP3) {
- lm87_write_value(client, LM87_REG_TEMP_HIGH[2], 0x7F);
- lm87_write_value(client, LM87_REG_TEMP_LOW[2], 0x00);
+ rc = lm87_write_value(client, LM87_REG_TEMP_HIGH[2],
+ 0x7F);
+ if (rc < 0)
+ return rc;
+ rc = lm87_write_value(client, LM87_REG_TEMP_LOW[2],
+ 0x00);
+ if (rc < 0)
+ return rc;
} else {
- lm87_write_value(client, LM87_REG_IN_MIN(0), 0x00);
- lm87_write_value(client, LM87_REG_IN_MAX(0), 0xFF);
+ rc = lm87_write_value(client, LM87_REG_IN_MIN(0), 0x00);
+ if (rc < 0)
+ return rc;
+ rc = lm87_write_value(client, LM87_REG_IN_MAX(0), 0xFF);
+ if (rc < 0)
+ return rc;
}
}
/* Make sure Start is set and INT#_Clear is clear */
if ((data->config & 0x09) != 0x01)
- lm87_write_value(client, LM87_REG_CONFIG,
- (data->config & 0x77) | 0x01);
+ return lm87_write_value(client, LM87_REG_CONFIG,
+ (data->config & 0x77) | 0x01);
return 0;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v2 3/3] hwmon: (lm87) Check I2C errors during detection and setup
2026-10-29 16:40 ` [PATCH v2 3/3] hwmon: (lm87) Check I2C errors during detection and setup Baek Kyu, Kim
@ 2026-10-29 16:50 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-10-29 16:50 UTC (permalink / raw)
To: Baek Kyu, Kim; +Cc: linux-hwmon
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] hwmon: (lm87) premature devm_add_action() in lm87_init_client() on error path
--
commit c6264ca6c303f980a5123962ced0900012b6d8d6
Author: Baek Kyu, Kim <pocj8ur4in@gmail.com>
hwmon: (lm87) Check I2C errors during detection and setup
This commit adds error checking for SMBus read and write operations in
lm87_detect(), lm87_restore_config(), and lm87_init_client().
> diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c
> index a66d486b314cf..32ec44f583eb8 100644
> --- a/drivers/hwmon/lm87.c
> +++ b/drivers/hwmon/lm87.c
[ ... ]
> @@ -989,7 +994,12 @@ static void lm87_restore_config(void *arg)
> struct i2c_client *client = arg;
> struct lm87_data *data = i2c_get_clientdata(client);
>
> - lm87_write_value(client, LM87_REG_CONFIG, data->config);
> + int err;
> +
> + err = lm87_write_value(client, LM87_REG_CONFIG, data->config);
> + if (err < 0)
> + dev_warn(&client->dev, "Failed to restore configuration: %d\n",
> + err);
> }
>
> static int lm87_init_client(struct i2c_client *client)
> @@ -1013,16 +1023,26 @@ static int lm87_init_client(struct i2c_client *client)
[ ... ]
> - data->config = lm87_read_value(client, LM87_REG_CONFIG) & 0x6F;
> + rc = lm87_read_value(client, LM87_REG_CONFIG);
> + if (rc < 0)
> + return rc;
> + data->config = rc & 0x6F;
>
> rc = devm_add_action(&client->dev, lm87_restore_config, client);
> if (rc)
> @@ -1033,28 +1053,52 @@ static int lm87_init_client(struct i2c_client *client)
>
> /* Limits are left uninitialized after power-up */
> for (i = 1; i < 6; i++) {
> - lm87_write_value(client, LM87_REG_IN_MIN(i), 0x00);
> - lm87_write_value(client, LM87_REG_IN_MAX(i), 0xFF);
> + rc = lm87_write_value(client, LM87_REG_IN_MIN(i), 0x00);
> + if (rc < 0)
> + return rc;
> + rc = lm87_write_value(client, LM87_REG_IN_MAX(i), 0xFF);
> + if (rc < 0)
> + return rc;
> }
[ ... ]
> /* Make sure Start is set and INT#_Clear is clear */
> if ((data->config & 0x09) != 0x01)
> - lm87_write_value(client, LM87_REG_CONFIG,
> - (data->config & 0x77) | 0x01);
> + return lm87_write_value(client, LM87_REG_CONFIG,
> + (data->config & 0x77) | 0x01);
> return 0;
> }
[Severity: Low]
In drivers/hwmon/lm87.c, lm87_init_client() registers lm87_restore_config()
via devm_add_action() before writing the sensor limit registers and before
updating LM87_REG_CONFIG at the end of the function:
/* Make sure Start is set and INT#_Clear is clear */
if ((data->config & 0x09) != 0x01)
return lm87_write_value(client, LM87_REG_CONFIG,
(data->config & 0x77) | 0x01);
Now that lm87_init_client() returns early when any of the limit register
writes or the final LM87_REG_CONFIG write fails, devres cleanup will run
lm87_restore_config() during probe unwind even though LM87_REG_CONFIG was
never modified. On an already failing bus, that issues an extra write to
LM87_REG_CONFIG and logs a "Failed to restore configuration" warning.
Should devm_add_action() (or devm_add_action_or_reset()) only be called
after LM87_REG_CONFIG has been successfully modified?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261029164016.338450-1-pocj8ur4in@gmail.com?part=3
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 0/3] hwmon: (lm87) Handle I2C read and write errors
2026-10-29 16:40 ` [PATCH v2 0/3] hwmon: (lm87) Handle I2C read and " Baek Kyu, Kim
` (2 preceding siblings ...)
2026-10-29 16:40 ` [PATCH v2 3/3] hwmon: (lm87) Check I2C errors during detection and setup Baek Kyu, Kim
@ 2026-10-29 19:46 ` Guenter Roeck
3 siblings, 0 replies; 11+ messages in thread
From: Guenter Roeck @ 2026-10-29 19:46 UTC (permalink / raw)
To: Baek Kyu, Kim; +Cc: linux-hwmon, linux-kernel
Hi,
On 10/29/26 09:40, Baek Kyu, Kim wrote:
> The LM87 driver does not check I2C transfer results in its sysfs stores,
> sensor cache refresh, and device setup. As a result, failed writes can
> be reported as successful and failed reads can be used as sensor data.
>
> This series propagates transfer errors to sysfs and probe callers.
> Cached settings change only after successful writes; a failed cache
> refresh is retried, with alarms already read retained for the retry.
>
> Under ARM64 QEMU/KVM, 46 emulated SMBus scenarios passed for LM87 and
> ADM1024. The earlier aout_output regression suite passed all 118 checks.
>
This driver is more than 20 years old. It survived all that time without
i2c error checks, and it is very questionable if it is still active on
any actual hardware.
Please refrain from touching such drivers unless there is a real problem,
observed in the field.
Thanks,
Guenter
^ permalink raw reply [flat|nested] 11+ messages in thread