diff options
author | Hans de Goede <hdegoede@redhat.com> | 2017-12-26 07:59:11 -0500 |
---|---|---|
committer | Sebastian Reichel <sre@kernel.org> | 2018-01-09 11:41:39 -0500 |
commit | 2b5a4b4bf2224f4f6b6631091bd51cb08d3094be (patch) | |
tree | 106d05c3511243dc552c9129b0e5c4a37c1c2c60 | |
parent | 8eb96f136fe12c3c475326a581098ff3a590acbb (diff) |
power: supply: axp288_fuel_gauge: Rework get_status()
Relying on the (dis)charge current reporting for reporting FULL back to
userspace does not work really well and often leads to the reported status
getting stuck at e.g. 98/99% (the fuelgauge is not perfect) for hours.
What happens is that when the battery is full the axp288 keeps charging it
with a very low current. Until it is really really full and once really
really full, some inaccuracies in the adc lead to it then sometimes
reporting a small discharging rate, even though an external pwr source is
used. So we end up with a status of "charging" for hours after the battery
is actually already full and sometimes this then flip-flops to discharging.
This commit fixes this by first checking if a valid Vbus is present and if
it is present using the fuel-gauge's reported percentage to check for a
full battery.
This commit also changes how get_status() determines if the battery is
charging or discharging when not reporting it as full. We still use the
current direction for this, but instead of reading 4 extra registers for
this (2 16 bit regs), simplify things by using the current-direction bit
in the power-status register, which already gets read anyways.
This also reduces the amount of i2c reads to 1 when on battery and 2
when a valid Vbus is present.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Chen-Yu Tsai <wens@csie.org>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.co.uk>
-rw-r--r-- | drivers/power/supply/axp288_fuel_gauge.c | 43 |
1 files changed, 22 insertions, 21 deletions
diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index eb60f75f00d7..c0b5e40b23e1 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c | |||
@@ -32,6 +32,12 @@ | |||
32 | #include <linux/seq_file.h> | 32 | #include <linux/seq_file.h> |
33 | #include <asm/unaligned.h> | 33 | #include <asm/unaligned.h> |
34 | 34 | ||
35 | #define PS_STAT_VBUS_TRIGGER (1 << 0) | ||
36 | #define PS_STAT_BAT_CHRG_DIR (1 << 2) | ||
37 | #define PS_STAT_VBAT_ABOVE_VHOLD (1 << 3) | ||
38 | #define PS_STAT_VBUS_VALID (1 << 4) | ||
39 | #define PS_STAT_VBUS_PRESENT (1 << 5) | ||
40 | |||
35 | #define CHRG_STAT_BAT_SAFE_MODE (1 << 3) | 41 | #define CHRG_STAT_BAT_SAFE_MODE (1 << 3) |
36 | #define CHRG_STAT_BAT_VALID (1 << 4) | 42 | #define CHRG_STAT_BAT_VALID (1 << 4) |
37 | #define CHRG_STAT_BAT_PRESENT (1 << 5) | 43 | #define CHRG_STAT_BAT_PRESENT (1 << 5) |
@@ -336,8 +342,7 @@ static inline void fuel_gauge_remove_debugfs(struct axp288_fg_info *info) | |||
336 | 342 | ||
337 | static void fuel_gauge_get_status(struct axp288_fg_info *info) | 343 | static void fuel_gauge_get_status(struct axp288_fg_info *info) |
338 | { | 344 | { |
339 | int pwr_stat, ret; | 345 | int pwr_stat, fg_res; |
340 | int charge, discharge; | ||
341 | 346 | ||
342 | pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS); | 347 | pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS); |
343 | if (pwr_stat < 0) { | 348 | if (pwr_stat < 0) { |
@@ -345,29 +350,25 @@ static void fuel_gauge_get_status(struct axp288_fg_info *info) | |||
345 | "PWR STAT read failed:%d\n", pwr_stat); | 350 | "PWR STAT read failed:%d\n", pwr_stat); |
346 | return; | 351 | return; |
347 | } | 352 | } |
348 | ret = iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], &charge); | 353 | |
349 | if (ret < 0) { | 354 | /* Report full if Vbus is valid and the reported capacity is 100% */ |
350 | dev_err(&info->pdev->dev, | 355 | if (pwr_stat & PS_STAT_VBUS_VALID) { |
351 | "ADC charge current read failed:%d\n", ret); | 356 | fg_res = fuel_gauge_reg_readb(info, AXP20X_FG_RES); |
352 | return; | 357 | if (fg_res < 0) { |
353 | } | 358 | dev_err(&info->pdev->dev, |
354 | ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &discharge); | 359 | "FG RES read failed: %d\n", fg_res); |
355 | if (ret < 0) { | 360 | return; |
356 | dev_err(&info->pdev->dev, | 361 | } |
357 | "ADC discharge current read failed:%d\n", ret); | 362 | if (fg_res == (FG_REP_CAP_VALID | 100)) { |
358 | return; | 363 | info->status = POWER_SUPPLY_STATUS_FULL; |
364 | return; | ||
365 | } | ||
359 | } | 366 | } |
360 | 367 | ||
361 | if (charge > 0) | 368 | if (pwr_stat & PS_STAT_BAT_CHRG_DIR) |
362 | info->status = POWER_SUPPLY_STATUS_CHARGING; | 369 | info->status = POWER_SUPPLY_STATUS_CHARGING; |
363 | else if (discharge > 0) | 370 | else |
364 | info->status = POWER_SUPPLY_STATUS_DISCHARGING; | 371 | info->status = POWER_SUPPLY_STATUS_DISCHARGING; |
365 | else { | ||
366 | if (pwr_stat & CHRG_STAT_BAT_PRESENT) | ||
367 | info->status = POWER_SUPPLY_STATUS_FULL; | ||
368 | else | ||
369 | info->status = POWER_SUPPLY_STATUS_NOT_CHARGING; | ||
370 | } | ||
371 | } | 372 | } |
372 | 373 | ||
373 | static int fuel_gauge_get_vbatt(struct axp288_fg_info *info, int *vbatt) | 374 | static int fuel_gauge_get_vbatt(struct axp288_fg_info *info, int *vbatt) |