Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

watchdog: da9062: da9063: use unlocked xfer function in restart

Machine resets via da9062/da9063 PMICs are challenging since one needs
to use special i2c atomic transfers due to the fact interrupts are
disabled in such late system stages. This is the reason both PMICs don't
use regmap and have instead opted for i2c_smbus_write_byte_data() in
restart handlers.

However extensive testing revealed that even using atomic safe function
is not enough and occasional resets fail with error message "Failed to
shutdown (err = -11)". This is due to the fact that function
i2c_smbus_write_byte_data() in turn calls __i2c_lock_bus_helper()
which might fail with -EAGAIN when bus lock is already taken and cannot
be released anymore.

Thus replace i2c_smbus_write_byte_data() with unlocked flavor of
i2c_smbus_xfer() function to avoid above dead-lock scenario. At this
system stage we don't care about proper locking anymore and only want
proper machine reset to be carried out.

Signed-off-by: Primoz Fiser <primoz.fiser@norik.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Link: https://lore.kernel.org/r/20221216083645.2574077-1-primoz.fiser@norik.com
[groeck: Fixed continuation line alignment]
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>

authored by

Primoz Fiser and committed by
Wim Van Sebroeck
f8ee39b4 00bdbc9a

+24 -6
+12 -3
drivers/watchdog/da9062_wdt.c
··· 155 155 { 156 156 struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); 157 157 struct i2c_client *client = to_i2c_client(wdt->hw->dev); 158 + union i2c_smbus_data msg; 158 159 int ret; 159 160 160 - /* Don't use regmap because it is not atomic safe */ 161 - ret = i2c_smbus_write_byte_data(client, DA9062AA_CONTROL_F, 162 - DA9062AA_SHUTDOWN_MASK); 161 + /* 162 + * Don't use regmap because it is not atomic safe. Additionally, use 163 + * unlocked flavor of i2c_smbus_xfer to avoid scenario where i2c bus 164 + * might be previously locked by some process unable to release the 165 + * lock due to interrupts already being disabled at this late stage. 166 + */ 167 + msg.byte = DA9062AA_SHUTDOWN_MASK; 168 + ret = __i2c_smbus_xfer(client->adapter, client->addr, client->flags, 169 + I2C_SMBUS_WRITE, DA9062AA_CONTROL_F, 170 + I2C_SMBUS_BYTE_DATA, &msg); 171 + 163 172 if (ret < 0) 164 173 dev_alert(wdt->hw->dev, "Failed to shutdown (err = %d)\n", 165 174 ret);
+12 -3
drivers/watchdog/da9063_wdt.c
··· 174 174 { 175 175 struct da9063 *da9063 = watchdog_get_drvdata(wdd); 176 176 struct i2c_client *client = to_i2c_client(da9063->dev); 177 + union i2c_smbus_data msg; 177 178 int ret; 178 179 179 - /* Don't use regmap because it is not atomic safe */ 180 - ret = i2c_smbus_write_byte_data(client, DA9063_REG_CONTROL_F, 181 - DA9063_SHUTDOWN); 180 + /* 181 + * Don't use regmap because it is not atomic safe. Additionally, use 182 + * unlocked flavor of i2c_smbus_xfer to avoid scenario where i2c bus 183 + * might previously be locked by some process unable to release the 184 + * lock due to interrupts already being disabled at this late stage. 185 + */ 186 + msg.byte = DA9063_SHUTDOWN; 187 + ret = __i2c_smbus_xfer(client->adapter, client->addr, client->flags, 188 + I2C_SMBUS_WRITE, DA9063_REG_CONTROL_F, 189 + I2C_SMBUS_BYTE_DATA, &msg); 190 + 182 191 if (ret < 0) 183 192 dev_alert(da9063->dev, "Failed to shutdown (err = %d)\n", 184 193 ret);