]> git.itanic.dy.fi Git - linux-stable/commitdiff
power: supply: bq27xxx: Ensure power_supply_changed() is called on current sign changes
authorHans de Goede <hdegoede@redhat.com>
Sat, 15 Apr 2023 18:23:37 +0000 (20:23 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 30 May 2023 13:17:25 +0000 (14:17 +0100)
commit 939a116142012926e25de0ea6b7e2f8d86a5f1b6 upstream.

On gauges where the current register is signed, there is no charging
flag in the flags register. So only checking flags will not result
in power_supply_changed() getting called when e.g. a charger is plugged
in and the current sign changes from negative (discharging) to
positive (charging).

This causes userspace's notion of the status to lag until userspace
does a poll.

And when a power_supply_leds.c LED trigger is used to indicate charging
status with a LED, this LED will lag until the capacity percentage
changes, which may take many minutes (because the LED trigger only is
updated on power_supply_changed() calls).

Fix this by calling bq27xxx_battery_current_and_status() on gauges with
a signed current register and checking if the status has changed.

Fixes: 297a533b3e62 ("bq27x00: Cache battery registers")
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/power/supply/bq27xxx_battery.c
include/linux/power/bq27xxx_battery.h

index 8f2995e9850a2a3e5fc7c119ba6d468dafa85268..f98b51ce19b5651e34e7334f55d56e3a80d69660 100644 (file)
@@ -1836,6 +1836,7 @@ static int bq27xxx_battery_current_and_status(
 
 static void bq27xxx_battery_update_unlocked(struct bq27xxx_device_info *di)
 {
+       union power_supply_propval status = di->last_status;
        struct bq27xxx_reg_cache cache = {0, };
        bool has_singe_flag = di->opts & BQ27XXX_O_ZERO;
 
@@ -1860,14 +1861,24 @@ static void bq27xxx_battery_update_unlocked(struct bq27xxx_device_info *di)
                if (di->regs[BQ27XXX_REG_CYCT] != INVALID_REG_ADDR)
                        cache.cycle_count = bq27xxx_battery_read_cyct(di);
 
+               /*
+                * On gauges with signed current reporting the current must be
+                * checked to detect charging <-> discharging status changes.
+                */
+               if (!(di->opts & BQ27XXX_O_ZERO))
+                       bq27xxx_battery_current_and_status(di, NULL, &status, &cache);
+
                /* We only have to read charge design full once */
                if (di->charge_design_full <= 0)
                        di->charge_design_full = bq27xxx_battery_read_dcap(di);
        }
 
        if ((di->cache.capacity != cache.capacity) ||
-           (di->cache.flags != cache.flags))
+           (di->cache.flags != cache.flags) ||
+           (di->last_status.intval != status.intval)) {
+               di->last_status.intval = status.intval;
                power_supply_changed(di->bat);
+       }
 
        if (memcmp(&di->cache, &cache, sizeof(cache)) != 0)
                di->cache = cache;
index e3322dad9c85cbccb0dfe868bb006589f59d302c..7c8d65414a70ad5784badca31fedf2c95d44fc0c 100644 (file)
@@ -2,6 +2,8 @@
 #ifndef __LINUX_BQ27X00_BATTERY_H__
 #define __LINUX_BQ27X00_BATTERY_H__
 
+#include <linux/power_supply.h>
+
 enum bq27xxx_chip {
        BQ27000 = 1, /* bq27000, bq27200 */
        BQ27010, /* bq27010, bq27210 */
@@ -70,6 +72,7 @@ struct bq27xxx_device_info {
        int charge_design_full;
        bool removed;
        unsigned long last_update;
+       union power_supply_propval last_status;
        struct delayed_work work;
        struct power_supply *bat;
        struct list_head list;