]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
hwmon: (adt7470) Fix fans stuck in manual mode on I2C errors
authorLuiz Angelo Daros de Luca <luizluca@gmail.com>
Tue, 28 Jul 2026 00:22:17 +0000 (21:22 -0300)
committerGuenter Roeck <linux@roeck-us.net>
Tue, 28 Jul 2026 00:53:28 +0000 (17:53 -0700)
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: ef67959c4253 ("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>
drivers/hwmon/adt7470.c

index 664349756dc2bf3320345bf1f9e26689c2c0af57..481d51617f4bedb1a7e3ac34dd616e714655574a 100644 (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;
+       struct device *dev = regmap_get_device(data->regmap);
+       u8 pwm[ADT7470_FAN_COUNT];
        unsigned int pwm_cfg[2];
-       int err;
+       unsigned long res;
+       int err, err2;
        int i;
-       u8 pwm[ADT7470_FAN_COUNT];
 
        /* 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;