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

power: supply: bq27xxx: Retrieve again when busy

Multiple applications may access the battery gauge at the same time, so
the gauge may be busy and EBUSY will be returned. The driver will set a
flag to record the EBUSY state, and this flag will be kept until the next
periodic update. When this flag is set, bq27xxx_battery_get_property()
will just return ENODEV until the flag is updated.

Even if the gauge was busy during the last accessing attempt, returning
ENODEV is not ideal, and can cause confusion in the applications layer.

Instead, retry accessing the I2C to update the flag is as expected, for
the gauge typically recovers from busy state within a few milliseconds.
If still failed to access the gauge, the real error code would be returned
instead of ENODEV (as suggested by Pali Rohár).

Reviewed-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Jerry Lv <Jerry.Lv@axis.com>
Link: https://lore.kernel.org/r/20250415-foo-fix-v2-1-5b45a395e4cc@axis.com
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>

authored by

Jerry Lv and committed by
Sebastian Reichel
f16d9fb6 1e3e2cf2

+13 -2
+1 -1
drivers/power/supply/bq27xxx_battery.c
··· 2131 2131 mutex_unlock(&di->lock); 2132 2132 2133 2133 if (psp != POWER_SUPPLY_PROP_PRESENT && di->cache.flags < 0) 2134 - return -ENODEV; 2134 + return di->cache.flags; 2135 2135 2136 2136 switch (psp) { 2137 2137 case POWER_SUPPLY_PROP_STATUS:
+12 -1
drivers/power/supply/bq27xxx_battery_i2c.c
··· 6 6 * Andrew F. Davis <afd@ti.com> 7 7 */ 8 8 9 + #include <linux/delay.h> 9 10 #include <linux/i2c.h> 10 11 #include <linux/interrupt.h> 11 12 #include <linux/module.h> ··· 32 31 struct i2c_msg msg[2]; 33 32 u8 data[2]; 34 33 int ret; 34 + int retry = 0; 35 35 36 36 if (!client->adapter) 37 37 return -ENODEV; ··· 49 47 else 50 48 msg[1].len = 2; 51 49 52 - ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); 50 + do { 51 + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); 52 + if (ret == -EBUSY && ++retry < 3) { 53 + /* sleep 10 milliseconds when busy */ 54 + usleep_range(10000, 11000); 55 + continue; 56 + } 57 + break; 58 + } while (1); 59 + 53 60 if (ret < 0) 54 61 return ret; 55 62