power: supply: qcom_battmgr: report charge levels on SM8350-class firmware
Subsystems (2)
cover power: supply: qcom_battmgr: report charge levels on SM8350-class firmware
On SM8350-class firmware (everything that is not SC8280XP/X1E80100) the battery reports no usable charge level to userspace: charge_full and charge_full_design fail with -ENODATA and there is no charge_now at all. As a consequence UPower cannot derive an absolute energy level and never shows a runtime estimate - the UI is limited to "charging"/"discharging".
Patch 1 is the actual bug: battmgr->unit is only populated from a BATTMGR_BAT_INFO response, which this firmware class never sends, so the mAh guard on the CHARGE_* properties always rejects them. The values are fetched from the firmware and stored on every access, they are just thrown away again.
Patch 2 adds CHARGE_NOW. There is no dedicated firmware property for it, but BATT_CHG_COUNTER carries the remaining charge rather than a monotonic counter on this firmware. This is the part I am least sure about, since it does not match the charge_counter semantics described in Documentation/ABI/testing/sysfs-class-power. Two measurements across the SoC range agree with the reported capacity to within a percent, so at least on this platform the interpretation holds. Happy to drop this patch or to collect a full discharge curve if you prefer.
Both patches were tested on a Fairphone 5 (QCM6490) running postmarketOS. Before the series:
energy: 0 Wh
energy-full: 0 Wh
energy-rate: 12.4713 W
percentage: 59%After, discharging and charging:
energy: 14.4347 Wh energy: 15.6003 Wh
energy-full: 16.8807 Wh energy-full: 18.1505 Wh
energy-rate: 3.39313 W energy-rate: 7.3492 W
time to empty: 4.3 hours time to full: 20.8 minutesTwo observations that are out of scope here but worth recording:
- power_now reports 72097076 while current_now * voltage_now gives ~11.9 W, and qcom-battmgr-usb/current_now reports 11078000 against an input_current_limit of 3000000. Both look like unscaled firmware values. UPower does not use either, so this series is unaffected.
- state_of_health reports 100 while charge_full/charge_full_design works out to 96.6%, so the firmware appears to derive the two independently.
The time-to-full estimate is optimistic compared to the firmware's own time_to_full_avg because UPower extrapolates linearly and does not model the CV taper. That is expected and not a regression.
Jan-Michael Brummer (2):
power: supply: qcom_battmgr: fix CHARGE_FULL* on SM8350-class firmware
power: supply: qcom_battmgr: expose CHARGE_NOW on SM8350-class
firmware
drivers/power/supply/qcom_battmgr.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)
PATCH 1/2 power: supply: qcom_battmgr: fix CHARGE_FULL* on SM8350-class firmware
battmgr->unit is only ever assigned in the BATTMGR_BAT_INFO handler of the SC8280XP callback. The SM8350-class firmware does not implement that request - every property is fetched individually via BATTMGR_BAT_PROPERTY_GET - so unit keeps its zero-initialised value, QCOM_BATTMGR_UNIT_mWh.
POWER_SUPPLY_PROP_CHARGE_FULL and POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN are guarded by 'if (unit != QCOM_BATTMGR_UNIT_mAh) return -ENODATA;', hence both properties always fail on these platforms, even though the values are requested from the firmware and stored in info.last_full_capacity and info.design_capacity on every access:
$ cat /sys/class/power_supply/qcom-battmgr-bat/charge_full cat: read error: No data available
Without a full charge value userspace cannot derive an absolute energy level, so UPower reports a zero energy level and never computes a time-to-empty/time-to-full estimate:
energy: 0 Wh
energy-full: 0 Wh
energy-rate: 12.4713 W
percentage: 59%The firmware of this class reports charge domain values, so set the unit statically when registering the SM8350/SM8550 power supplies. With this patch the properties read back consistently on a Fairphone 5:
charge_full 4116000 charge_full_design 4260000 capacity 85
--- drivers/power/supply/qcom_battmgr.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/power/supply/qcom_battmgr.c +8 -0
@@ -1690,6 +1690,14 @@ static int qcom_battmgr_probe(struct auxiliary_device *adev,return dev_err_probe (dev,PTR_ERR (battmgr->wls_psy),"failed to register wireless charing power supply\n" ); }else { +/* +* The SM8350-class firmware has no BATTMGR_BAT_INFO request, so +* @unit is never populated from the firmware. It reports charge +* domain values, so set it statically here - otherwise the +* CHARGE_* properties are rejected with -ENODATA. +*/ + battmgr->unit = QCOM_BATTMGR_UNIT_mAh; +if (battmgr->variant ==QCOM_BATTMGR_SM8550 ) psy_desc = &sm8550_bat_psy_desc;else
Re: [PATCH 1/2] power: supply: qcom_battmgr: fix CHARGE_FULL* on SM8350-class firmware
Quoted 28 lines · Jan-Michael Brummer
battmgr->unitis only ever assigned in theBATTMGR_BAT_INFOhandler of the SC8280XP callback. The SM8350-class firmware does not implement that request - every property is fetched individually viaBATTMGR_BAT_PROPERTY_GET- so unit keeps its zero-initialised value,QCOM_BATTMGR_UNIT_mWh.
POWER_SUPPLY_PROP_CHARGE_FULLandPOWER_SUPPLY_PROP_CHARGE_FULL_DESIGNare guarded by 'if (unit !=QCOM_BATTMGR_UNIT_mAh) return-ENODATA;', hence both properties always fail on these platforms, even though the values are requested from the firmware and stored in info.last_full_capacity and info.design_capacity on every access:$ cat /sys/class/power_supply/qcom-battmgr-bat/charge_full cat: read error: No data availableWithout a full charge value userspace cannot derive an absolute energy level, so UPower reports a zero energy level and never computes a time-to-empty/time-to-full estimate:
energy: 0 Wh energy-full: 0 Wh energy-rate: 12.4713 W percentage: 59%The firmware of this class reports charge domain values, so set the unit statically when registering the SM8350/SM8550 power supplies. With this patch the properties read back consistently on a Fairphone 5:
Quoted 7 lines
charge_full 4116000 charge_full_design 4260000 capacity 85Fixes 29e8142b5623 ("power: supply: Introduce Qualcomm PMIC GLINK power supply")Signed-off-by Jan-Michael Brummer <[email protected]>---
This also implicitly unlocks reporting
POWER_SUPPLY_PROP_CHARGE_EMPTYPOWER_SUPPLY_PROP_CHARGE_NOW
which are not populated and will always report 0.
We should probably further limit that.
Konrad
PATCH 2/2 power: supply: qcom_battmgr: expose CHARGE_NOW on SM8350-class firmware
In reply to Jan-Michael BrummerThe SM8350-class firmware does not implement a dedicated 'charge now' property. It does however report BATT_CHG_COUNTER, and on this class of firmware that value is the remaining charge in uAh rather than a monotonic counter. Measured on a Fairphone 5 with charge_full at 4116000 uAh:
BATT_CHG_COUNTER reported capacity counter / charge_full 2434614 59% 59.1% 3525354 85% 85.6%
Map POWER_SUPPLY_PROP_CHARGE_NOW onto the same firmware property and store the value in status.capacity, which the CHARGE_NOW/ENERGY_NOW case of qcom_battmgr_bat_get_property() already reads.
Together with the preceding patch this gives userspace both the full charge and the current charge. UPower now derives an energy level and runtime estimates in both directions:
state: discharging state: charging
energy: 14.4347 Wh energy: 15.6003 Wh
energy-full: 16.8807 Wh energy-full: 18.1505 Wh
energy-rate: 3.39313 W energy-rate: 7.3492 W
time to empty: 4.3 hours time to full: 20.8 minutes--- drivers/power/supply/qcom_battmgr.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/power/supply/qcom_battmgr.c +7 -1
@@ -441,6 +441,7 @@ static const u8 sm8350_bat_prop_map[] = { [POWER_SUPPLY_PROP_CYCLE_COUNT] = BATT_CYCLE_COUNT, [POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN] = BATT_CHG_FULL_DESIGN, [POWER_SUPPLY_PROP_CHARGE_FULL] = BATT_CHG_FULL, + [POWER_SUPPLY_PROP_CHARGE_NOW] = BATT_CHG_COUNTER, [POWER_SUPPLY_PROP_MODEL_NAME] = BATT_MODEL_NAME, [POWER_SUPPLY_PROP_TIME_TO_FULL_AVG] = BATT_TTF_AVG, [POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG] = BATT_TTE_AVG,
@@ -878,6 +879,7 @@ static const enum power_supply_property sm8350_bat_props[] = {POWER_SUPPLY_PROP_CYCLE_COUNT ,POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN ,POWER_SUPPLY_PROP_CHARGE_FULL , +POWER_SUPPLY_PROP_CHARGE_NOW ,POWER_SUPPLY_PROP_MODEL_NAME ,POWER_SUPPLY_PROP_TIME_TO_FULL_AVG , POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
@@ -910,6 +912,7 @@ static const enum power_supply_property sm8550_bat_props[] = {POWER_SUPPLY_PROP_CYCLE_COUNT ,POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN ,POWER_SUPPLY_PROP_CHARGE_FULL , +POWER_SUPPLY_PROP_CHARGE_NOW ,POWER_SUPPLY_PROP_MODEL_NAME ,POWER_SUPPLY_PROP_TIME_TO_FULL_AVG , POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
@@ -1442,7 +1445,10 @@ static void qcom_battmgr_sm8350_callback(struct qcom_battmgr *battmgr, battmgr->info.technology =le32_to_cpu (resp->intval.value);break ;case BATT_CHG_COUNTER : - battmgr->info.charge_count =le32_to_cpu (resp->intval.value); + val =le32_to_cpu (resp->intval.value); + battmgr->info.charge_count = val; +/* the firmware reports the remaining charge here */ + battmgr->status.capacity = val;break ;case BATT_CYCLE_COUNT : battmgr->info.cycle_count =le32_to_cpu (resp->intval.value);
Re: [PATCH 2/2] power: supply: qcom_battmgr: expose CHARGE_NOW on SM8350-class firmware
Quoted 18 lines · Jan-Michael Brummer
The SM8350-class firmware does not implement a dedicated 'charge now' property. It does however report
BATT_CHG_COUNTER, and on this class of firmware that value is the remaining charge in uAh rather than a monotonic counter. Measured on a Fairphone 5 withcharge_fullat 4116000 uAh:BATT_CHG_COUNTER reported capacity counter / charge_full 2434614 59% 59.1% 3525354 85% 85.6%Map
POWER_SUPPLY_PROP_CHARGE_NOWonto the same firmware property and store the value in status.capacity, which the CHARGE_NOW/ENERGY_NOW case ofqcom_battmgr_bat_get_property()already reads.Together with the preceding patch this gives userspace both the full charge and the current charge. UPower now derives an energy level and runtime estimates in both directions:
Quoted 8 lines
state: discharging state: charging energy: 14.4347 Wh energy: 15.6003 Wh energy-full: 16.8807 Wh energy-full: 18.1505 Wh energy-rate: 3.39313 W energy-rate: 7.3492 W time to empty: 4.3 hours time to full: 20.8 minutesSigned-off-by Jan-Michael Brummer <[email protected]>---
Ok this makes more sense now. We should still gate the other property though.
[...]
drivers/power/supply/qcom_battmgr.c static void qcom_battmgr_sm8350_callback(struct qcom_battmgr *battmgr, · line 1451
Quoted 9 lines
drivers/power/supply/qcom_battmgr.c +4
-1@@ -1442,7 +1445,10 @@ static void qcom_battmgr_sm8350_callback(struct qcom_battmgr *battmgr, battmgr->info.technology =le32_to_cpu (resp->intval.value);break ;case BATT_CHG_COUNTER : - battmgr->info.charge_count =le32_to_cpu (resp->intval.value); + val =le32_to_cpu (resp->intval.value); + battmgr->info.charge_count = val; +/* the firmware reports the remaining charge here */ + battmgr->status.capacity = val;
Let me try and find whether we can rely on this - on newer targets many properties are more or less just front-ends for vendor plumbing (because there's a ton of different chargers etc.)
Konrad
Re: [PATCH 2/2] power: supply: qcom_battmgr: expose CHARGE_NOW on SM8350-class firmware
Quoted 32 lines · Konrad Dybcio
Quoted 18 lines · Jan-Michael Brummer
The SM8350-class firmware does not implement a dedicated 'charge now' property. It does however report
BATT_CHG_COUNTER, and on this class of firmware that value is the remaining charge in uAh rather than a monotonic counter. Measured on a Fairphone 5 withcharge_fullat 4116000 uAh:BATT_CHG_COUNTER reported capacity counter / charge_full 2434614 59% 59.1% 3525354 85% 85.6%Map
POWER_SUPPLY_PROP_CHARGE_NOWonto the same firmware property and store the value in status.capacity, which the CHARGE_NOW/ENERGY_NOW case ofqcom_battmgr_bat_get_property()already reads.Together with the preceding patch this gives userspace both the full charge and the current charge. UPower now derives an energy level and runtime estimates in both directions:
Quoted 8 lines
state: discharging state: charging energy: 14.4347 Wh energy: 15.6003 Wh energy-full: 16.8807 Wh energy-full: 18.1505 Wh energy-rate: 3.39313 W energy-rate: 7.3492 W time to empty: 4.3 hours time to full: 20.8 minutesSigned-off-by Jan-Michael Brummer <[email protected]>---
Ok this makes more sense now. We should still gate the other property though.
[...]
Quoted 13 lines
drivers/power/supply/qcom_battmgr.c static void qcom_battmgr_sm8350_callback(struct qcom_battmgr *battmgr, · line 1451
Quoted 9 lines
drivers/power/supply/qcom_battmgr.c +4
-1@@ -1442,7 +1445,10 @@ static void qcom_battmgr_sm8350_callback(struct qcom_battmgr *battmgr, battmgr->info.technology =le32_to_cpu (resp->intval.value);break ;case BATT_CHG_COUNTER : - battmgr->info.charge_count =le32_to_cpu (resp->intval.value); + val =le32_to_cpu (resp->intval.value); + battmgr->info.charge_count = val; +/* the firmware reports the remaining charge here */ + battmgr->status.capacity = val;Let me try and find whether we can rely on this - on newer targets many properties are more or less just front-ends for vendor plumbing (because there's a ton of different chargers etc.)
There's a comment saying /* remaining capacity in uAh */, so it seems like this should be OK
Konrad
Re: [PATCH 2/2] power: supply: qcom_battmgr: expose CHARGE_NOW on SM8350-class firmware
Am Mittwoch, dem 02.09.2026 um 18:21 +0200 schrieb Konrad Dybcio:
Quoted 37 lines · Konrad Dybcio
Quoted 36 lines · Jan-Michael Brummer
Quoted 35 lines
The SM8350-class firmware does not implement a dedicated 'charge now'
property. It does however reportBATT_CHG_COUNTER, and on this class of
firmware that value is the remaining charge in uAh rather than a monotonic counter. Measured on a Fairphone 5 withcharge_fullat 4116000 uAh:BATT_CHG_COUNTER reported capacity counter /
charge_full2434614 59% 59.1% 3525354 85% 85.6%Map
POWER_SUPPLY_PROP_CHARGE_NOWonto the same firmware property and
store the value in status.capacity, which the CHARGE_NOW/ENERGY_NOW case
ofqcom_battmgr_bat_get_property()already reads.Together with the preceding patch this gives userspace both the full
charge and the current charge. UPower now derives an energy level and
runtime estimates in both directions:state: discharging state:
charging
energy: 14.4347 Wh energy:
15.6003 Wh
energy-full: 16.8807 Wh energy-full:
18.1505 Wh
energy-rate: 3.39313 W energy-rate:
7.3492 W
time to empty: 4.3 hours time to full:
20.8 minutes
Quoted 5 lines
Quoted 5 lines
Quoted 2 lines
Signed-off-by Jan-Michael Brummer <[email protected]>---
Ok this makes more sense now. We should still gate the other property though.
Hi Konrad,
so this one is fine and can be merged? Or am i'm missing something?
Quoted 25 lines
Quoted 12 lines
[...]
Quoted 9 lines
(quoted diff) +0
-0@@ -1442,7 +1445,10 @@ static void qcom_battmgr_sm8350_callback(struct qcom_battmgr *battmgr,
battmgr->info.technology=le32_to_cpu(resp->intval.value);
break;
caseBATT_CHG_COUNTER:(quoted diff) +0
-1- battmgr->info.charge_count =
le32_to_cpu(resp->intval.value);drivers/power/supply/qcom_battmgr.c +1
-0+ val =le32_to_cpu (resp->intval.value);Quoted 8 lines
drivers/power/supply/qcom_battmgr.c static void qcom_battmgr_sm8350_callback(struct qcom_battmgr *battmgr, · line 1451
Quoted 4 lines
(quoted diff) +2
-0+ battmgr->info.charge_count = val; + /* the firmware reports the remainingcharge here */
drivers/power/supply/qcom_battmgr.c +1
-0+ battmgr->status.capacity = val;Let me try and find whether we can rely on this - on newer targets many properties are more or less just front-ends for vendor plumbing (because there's a ton of different chargers etc.)
There's a comment saying /* remaining capacity in uAh */, so it seems like this should be OK
Konrad