hwmon: (adt7470) Fix fans stuck in manual mode on I2C errors

During adt7470_read_temperatures(), the driver temporarily switches
the PWM channels to manual mode, performs the temperature collection,
and then restores the original configuration registers.

However, if an I2C transaction fails at any point after entering manual
mode, the function aborts and returns immediately. This leaves the
configuration registers un-restored, permanently trapping the fans in
manual mode.

Introduce a recovery path to ensure that the original PWM configuration
registers are always restored, even when intermediate I2C operations
fail.

Reported-by: sashiko-bot@kernel.org
Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org
Fixes: ef67959c42 ("hwmon: (adt7470) Convert to use regmap")
Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-1-598e38a46ba6@gmail.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
This commit is contained in:
Luiz Angelo Daros de Luca 2026-07-27 21:22:17 -03:00 committed by Guenter Roeck
parent d0b704e569
commit 625a2c02a1

View File

@ -205,11 +205,12 @@ static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned in
/* Probe for temperature sensors. Assumes lock is held */
static int adt7470_read_temperatures(struct adt7470_data *data)
{
unsigned long res;
unsigned int pwm_cfg[2];
int err;
int i;
struct device *dev = regmap_get_device(data->regmap);
u8 pwm[ADT7470_FAN_COUNT];
unsigned int pwm_cfg[2];
unsigned long res;
int err, err2;
int i;
/* save pwm[1-4] config register */
err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]);
@ -233,19 +234,19 @@ static int adt7470_read_temperatures(struct adt7470_data *data)
err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2),
ADT7470_PWM_AUTO_MASK, 0);
if (err < 0)
return err;
goto out_restore;
/* write pwm control to whatever it was */
err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0],
ADT7470_PWM_COUNT);
if (err < 0)
return err;
goto out_restore;
/* start reading temperature sensors */
err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK);
if (err < 0)
return err;
goto out_restore;
/* Delay is 200ms * number of temp sensors. */
res = msleep_interruptible((data->num_temp_sensors >= 0 ?
@ -256,13 +257,30 @@ static int adt7470_read_temperatures(struct adt7470_data *data)
err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
ADT7470_T05_STB_MASK, 0);
if (err < 0)
return err;
goto out_restore;
out_restore:
/* restore pwm[1-4] config registers */
err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]);
if (err < 0)
return err;
err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]);
err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]);
if (err2 < 0) {
dev_warn_ratelimited(dev,
"failed to restore PWM{1,2} config (%d)\n",
err2);
if (!err)
err = err2;
}
err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]);
if (err2 < 0) {
dev_warn_ratelimited(dev,
"failed to restore PWM{3,4} config (%d)\n",
err2);
if (!err)
err = err2;
}
if (err < 0)
return err;