linux-pm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/1] power: supply: bq27xxx: expose battery data when CI=1
@ 2022-04-20 12:30 Sicelo A. Mhlongo
  2022-04-20 12:30 ` [PATCH v2 1/1] " Sicelo A. Mhlongo
  0 siblings, 1 reply; 3+ messages in thread
From: Sicelo A. Mhlongo @ 2022-04-20 12:30 UTC (permalink / raw)
  To: pali, sre; +Cc: linux-pm, linux-kernel, Sicelo A. Mhlongo

We currently discard capacity information provided by the chip when the
Capacity Inaccurate flag is set. However, in many cases, having this
stale data is better than no information at all.

Even if the chip has been fully reset, the datasheet shows that capacity
values (NAC specifically) can be seeded by using the WRTNAC facility of
the Control and Mode registers. After seeding, CI remains set, but the
capacity values are as accurate as the NAC value provided to WRTNAC is.

On the Nokia N900, such seed value can be obtained from rx51_battery,
which reads battery data directly from the battery's BSI pin. This can
be done in userspace via i2c access to the registers. With this patch,
once seeded, capacity values are available through the driver.

The patch was successfully tested on bq27200 on the Nokia N900.

Sicelo A. Mhlongo (1):
  power: supply: bq27xxx: expose battery data when CI=1

 drivers/power/supply/bq27xxx_battery.c | 59 ++++++++++++--------------
 1 file changed, 27 insertions(+), 32 deletions(-)

-- 
2.35.2


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH v2 1/1] power: supply: bq27xxx: expose battery data when CI=1
  2022-04-20 12:30 [PATCH v2 0/1] power: supply: bq27xxx: expose battery data when CI=1 Sicelo A. Mhlongo
@ 2022-04-20 12:30 ` Sicelo A. Mhlongo
  2022-05-03 15:38   ` Sebastian Reichel
  0 siblings, 1 reply; 3+ messages in thread
From: Sicelo A. Mhlongo @ 2022-04-20 12:30 UTC (permalink / raw)
  To: pali, sre; +Cc: linux-pm, linux-kernel, Sicelo A. Mhlongo

When the Capacity Inaccurate flag is set, the chip still provides data
about the battery, albeit inaccurate. Instead of discarding capacity
values for CI=1, expose the stale data and use the
POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED property to indicate that the
values should be used with care.

Reviewed-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Sicelo A. Mhlongo <absicsz@gmail.com>
---
 drivers/power/supply/bq27xxx_battery.c | 59 ++++++++++++--------------
 1 file changed, 27 insertions(+), 32 deletions(-)

diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
index 72e727cd31e8..9adc7f43bbfd 100644
--- a/drivers/power/supply/bq27xxx_battery.c
+++ b/drivers/power/supply/bq27xxx_battery.c
@@ -1572,14 +1572,6 @@ static int bq27xxx_battery_read_charge(struct bq27xxx_device_info *di, u8 reg)
  */
 static inline int bq27xxx_battery_read_nac(struct bq27xxx_device_info *di)
 {
-	int flags;
-
-	if (di->opts & BQ27XXX_O_ZERO) {
-		flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true);
-		if (flags >= 0 && (flags & BQ27000_FLAG_CI))
-			return -ENODATA;
-	}
-
 	return bq27xxx_battery_read_charge(di, BQ27XXX_REG_NAC);
 }
 
@@ -1742,6 +1734,18 @@ static bool bq27xxx_battery_dead(struct bq27xxx_device_info *di, u16 flags)
 		return flags & (BQ27XXX_FLAG_SOC1 | BQ27XXX_FLAG_SOCF);
 }
 
+/*
+ * Returns true if reported battery capacity is inaccurate
+ */
+static bool bq27xxx_battery_capacity_inaccurate(struct bq27xxx_device_info *di,
+						 u16 flags)
+{
+	if (di->opts & BQ27XXX_O_HAS_CI)
+		return (flags & BQ27000_FLAG_CI);
+	else
+		return false;
+}
+
 static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di)
 {
 	/* Unlikely but important to return first */
@@ -1751,6 +1755,8 @@ static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di)
 		return POWER_SUPPLY_HEALTH_COLD;
 	if (unlikely(bq27xxx_battery_dead(di, di->cache.flags)))
 		return POWER_SUPPLY_HEALTH_DEAD;
+	if (unlikely(bq27xxx_battery_capacity_inaccurate(di, di->cache.flags)))
+		return POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED;
 
 	return POWER_SUPPLY_HEALTH_GOOD;
 }
@@ -1766,30 +1772,19 @@ void bq27xxx_battery_update(struct bq27xxx_device_info *di)
 		cache.flags = -1; /* read error */
 	if (cache.flags >= 0) {
 		cache.temperature = bq27xxx_battery_read_temperature(di);
-		if (has_ci_flag && (cache.flags & BQ27000_FLAG_CI)) {
-			dev_info_once(di->dev, "battery is not calibrated! ignoring capacity values\n");
-			cache.capacity = -ENODATA;
-			cache.energy = -ENODATA;
-			cache.time_to_empty = -ENODATA;
-			cache.time_to_empty_avg = -ENODATA;
-			cache.time_to_full = -ENODATA;
-			cache.charge_full = -ENODATA;
-			cache.health = -ENODATA;
-		} else {
-			if (di->regs[BQ27XXX_REG_TTE] != INVALID_REG_ADDR)
-				cache.time_to_empty = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTE);
-			if (di->regs[BQ27XXX_REG_TTECP] != INVALID_REG_ADDR)
-				cache.time_to_empty_avg = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTECP);
-			if (di->regs[BQ27XXX_REG_TTF] != INVALID_REG_ADDR)
-				cache.time_to_full = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTF);
-
-			cache.charge_full = bq27xxx_battery_read_fcc(di);
-			cache.capacity = bq27xxx_battery_read_soc(di);
-			if (di->regs[BQ27XXX_REG_AE] != INVALID_REG_ADDR)
-				cache.energy = bq27xxx_battery_read_energy(di);
-			di->cache.flags = cache.flags;
-			cache.health = bq27xxx_battery_read_health(di);
-		}
+		if (di->regs[BQ27XXX_REG_TTE] != INVALID_REG_ADDR)
+			cache.time_to_empty = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTE);
+		if (di->regs[BQ27XXX_REG_TTECP] != INVALID_REG_ADDR)
+			cache.time_to_empty_avg = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTECP);
+		if (di->regs[BQ27XXX_REG_TTF] != INVALID_REG_ADDR)
+			cache.time_to_full = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTF);
+
+		cache.charge_full = bq27xxx_battery_read_fcc(di);
+		cache.capacity = bq27xxx_battery_read_soc(di);
+		if (di->regs[BQ27XXX_REG_AE] != INVALID_REG_ADDR)
+			cache.energy = bq27xxx_battery_read_energy(di);
+		di->cache.flags = cache.flags;
+		cache.health = bq27xxx_battery_read_health(di);
 		if (di->regs[BQ27XXX_REG_CYCT] != INVALID_REG_ADDR)
 			cache.cycle_count = bq27xxx_battery_read_cyct(di);
 
-- 
2.35.2


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH v2 1/1] power: supply: bq27xxx: expose battery data when CI=1
  2022-04-20 12:30 ` [PATCH v2 1/1] " Sicelo A. Mhlongo
@ 2022-05-03 15:38   ` Sebastian Reichel
  0 siblings, 0 replies; 3+ messages in thread
From: Sebastian Reichel @ 2022-05-03 15:38 UTC (permalink / raw)
  To: Sicelo A. Mhlongo; +Cc: pali, linux-pm, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 4659 bytes --]

Hi,

On Wed, Apr 20, 2022 at 02:30:59PM +0200, Sicelo A. Mhlongo wrote:
> When the Capacity Inaccurate flag is set, the chip still provides data
> about the battery, albeit inaccurate. Instead of discarding capacity
> values for CI=1, expose the stale data and use the
> POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED property to indicate that the
> values should be used with care.
> 
> Reviewed-by: Pali Rohár <pali@kernel.org>
> Signed-off-by: Sicelo A. Mhlongo <absicsz@gmail.com>
> ---

Thanks, queued.

-- Sebastian

>  drivers/power/supply/bq27xxx_battery.c | 59 ++++++++++++--------------
>  1 file changed, 27 insertions(+), 32 deletions(-)
> 
> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
> index 72e727cd31e8..9adc7f43bbfd 100644
> --- a/drivers/power/supply/bq27xxx_battery.c
> +++ b/drivers/power/supply/bq27xxx_battery.c
> @@ -1572,14 +1572,6 @@ static int bq27xxx_battery_read_charge(struct bq27xxx_device_info *di, u8 reg)
>   */
>  static inline int bq27xxx_battery_read_nac(struct bq27xxx_device_info *di)
>  {
> -	int flags;
> -
> -	if (di->opts & BQ27XXX_O_ZERO) {
> -		flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true);
> -		if (flags >= 0 && (flags & BQ27000_FLAG_CI))
> -			return -ENODATA;
> -	}
> -
>  	return bq27xxx_battery_read_charge(di, BQ27XXX_REG_NAC);
>  }
>  
> @@ -1742,6 +1734,18 @@ static bool bq27xxx_battery_dead(struct bq27xxx_device_info *di, u16 flags)
>  		return flags & (BQ27XXX_FLAG_SOC1 | BQ27XXX_FLAG_SOCF);
>  }
>  
> +/*
> + * Returns true if reported battery capacity is inaccurate
> + */
> +static bool bq27xxx_battery_capacity_inaccurate(struct bq27xxx_device_info *di,
> +						 u16 flags)
> +{
> +	if (di->opts & BQ27XXX_O_HAS_CI)
> +		return (flags & BQ27000_FLAG_CI);
> +	else
> +		return false;
> +}
> +
>  static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di)
>  {
>  	/* Unlikely but important to return first */
> @@ -1751,6 +1755,8 @@ static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di)
>  		return POWER_SUPPLY_HEALTH_COLD;
>  	if (unlikely(bq27xxx_battery_dead(di, di->cache.flags)))
>  		return POWER_SUPPLY_HEALTH_DEAD;
> +	if (unlikely(bq27xxx_battery_capacity_inaccurate(di, di->cache.flags)))
> +		return POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED;
>  
>  	return POWER_SUPPLY_HEALTH_GOOD;
>  }
> @@ -1766,30 +1772,19 @@ void bq27xxx_battery_update(struct bq27xxx_device_info *di)
>  		cache.flags = -1; /* read error */
>  	if (cache.flags >= 0) {
>  		cache.temperature = bq27xxx_battery_read_temperature(di);
> -		if (has_ci_flag && (cache.flags & BQ27000_FLAG_CI)) {
> -			dev_info_once(di->dev, "battery is not calibrated! ignoring capacity values\n");
> -			cache.capacity = -ENODATA;
> -			cache.energy = -ENODATA;
> -			cache.time_to_empty = -ENODATA;
> -			cache.time_to_empty_avg = -ENODATA;
> -			cache.time_to_full = -ENODATA;
> -			cache.charge_full = -ENODATA;
> -			cache.health = -ENODATA;
> -		} else {
> -			if (di->regs[BQ27XXX_REG_TTE] != INVALID_REG_ADDR)
> -				cache.time_to_empty = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTE);
> -			if (di->regs[BQ27XXX_REG_TTECP] != INVALID_REG_ADDR)
> -				cache.time_to_empty_avg = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTECP);
> -			if (di->regs[BQ27XXX_REG_TTF] != INVALID_REG_ADDR)
> -				cache.time_to_full = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTF);
> -
> -			cache.charge_full = bq27xxx_battery_read_fcc(di);
> -			cache.capacity = bq27xxx_battery_read_soc(di);
> -			if (di->regs[BQ27XXX_REG_AE] != INVALID_REG_ADDR)
> -				cache.energy = bq27xxx_battery_read_energy(di);
> -			di->cache.flags = cache.flags;
> -			cache.health = bq27xxx_battery_read_health(di);
> -		}
> +		if (di->regs[BQ27XXX_REG_TTE] != INVALID_REG_ADDR)
> +			cache.time_to_empty = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTE);
> +		if (di->regs[BQ27XXX_REG_TTECP] != INVALID_REG_ADDR)
> +			cache.time_to_empty_avg = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTECP);
> +		if (di->regs[BQ27XXX_REG_TTF] != INVALID_REG_ADDR)
> +			cache.time_to_full = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTF);
> +
> +		cache.charge_full = bq27xxx_battery_read_fcc(di);
> +		cache.capacity = bq27xxx_battery_read_soc(di);
> +		if (di->regs[BQ27XXX_REG_AE] != INVALID_REG_ADDR)
> +			cache.energy = bq27xxx_battery_read_energy(di);
> +		di->cache.flags = cache.flags;
> +		cache.health = bq27xxx_battery_read_health(di);
>  		if (di->regs[BQ27XXX_REG_CYCT] != INVALID_REG_ADDR)
>  			cache.cycle_count = bq27xxx_battery_read_cyct(di);
>  
> -- 
> 2.35.2
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2022-05-03 15:39 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-20 12:30 [PATCH v2 0/1] power: supply: bq27xxx: expose battery data when CI=1 Sicelo A. Mhlongo
2022-04-20 12:30 ` [PATCH v2 1/1] " Sicelo A. Mhlongo
2022-05-03 15:38   ` Sebastian Reichel

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).