[PATCH] usb: typec: tcpm: Support native battery energy properties in battery AMS

Amit Sunil Dhamne <[email protected]>
Newsgroups org.kernel.feeds.b4-sent,org.kernel.vger.linux-kernel,org.kernel.vger.linux-usb
Message-ID <[email protected]>
Some fuel gauges report battery telemetry in energy rather than charge.
Add support for querying native energy properties for Battery Status and
Battery Capabilities AMS, falling back to calculating energy from charge
and average voltage when primary properties are not supported.

+---------+--------------------+----------------------------------+
| Sr. No. | Primary            | Fallback                         |
+---------+--------------------+----------------------------------+
| 1       | ENERGY_NOW         | CHARGE_NOW + VOLTAGE_AVG         |
| 2       | ENERGY_FULL_DESIGN | CHARGE_FULL_DESIGN + VOLTAGE_AVG |
| 3       | ENERGY_FULL        | CHARGE_FULL + VOLTAGE_AVG        |
+---------+--------------------+----------------------------------+
Note: All properties above are to be prefixed with POWER_SUPPLY_PROP_.

Closes: https://lore.kernel.org/all/amVJ1u67qHENBQ5l@venus/
Suggested-by: Sebastian Reichel <[email protected]>
Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Amit Sunil Dhamne <[email protected]>
Reviewed-by: Badhri Jagan Sridharan <[email protected]>
---
 drivers/usb/typec/tcpm/tcpm.c | 57 +++++++++++++++++++++++++++----------------
 1 file changed, 36 insertions(+), 21 deletions(-)

diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index a8cd1959c426..0d7dc0b51c87 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -410,6 +410,9 @@ struct pd_timings {
 /* Convert microwatt to watt */
 #define UW_TO_W(pow)				(div_u64((pow), 1000000))
 
+/* Convert micro-Watt-hours to USB PD energy (increments of 0.1Wh) */
+#define UWH_TO_PD_ENERGY(energy)		(UW_TO_W((u64)(energy) * 10))
+
 /*
  * As per USB PD Spec Rev 3.18 (Sec. 6.5.13.11), the number of fixed batteries
  * that a port can be queried is restricted to 4.
@@ -1580,8 +1583,7 @@ static u16 tcpm_charge_to_energy(int charge, int voltage)
 {
 	u64 energy = div_u64((u64)charge * voltage, 1000000);
 
-	/* Battery telemetry is reported in increments of 0.1Wh */
-	return (u16)UW_TO_W(energy * 10);
+	return UWH_TO_PD_ENERGY(energy);
 }
 
 static int tcpm_pd_send_batt_status(struct tcpm_port *port)
@@ -1612,16 +1614,17 @@ static int tcpm_pd_send_batt_status(struct tcpm_port *port)
 	else
 		batt_present = val.intval > 0;
 
-	ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_CHARGE_NOW,
-					&val);
+	ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_ENERGY_NOW, &val);
 	if (!ret) {
-		charge_now = val.intval;
-		ret = power_supply_get_property(batt,
-						POWER_SUPPLY_PROP_VOLTAGE_AVG,
-						&val);
-		if (!ret)
-			present_charge = tcpm_charge_to_energy(charge_now,
-							       val.intval);
+		present_charge = UWH_TO_PD_ENERGY(val.intval);
+	} else {
+		ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_CHARGE_NOW, &val);
+		if (!ret) {
+			charge_now = val.intval;
+			ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_VOLTAGE_AVG, &val);
+			if (!ret)
+				present_charge = tcpm_charge_to_energy(charge_now, val.intval);
+		}
 	}
 
 	ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_STATUS, &val);
@@ -1678,19 +1681,31 @@ static int tcpm_pd_send_batt_cap(struct tcpm_port *port)
 
 	invalid_ref = false;
 	batt = port->fixed_batt[batt_id];
-	ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_VOLTAGE_AVG,
-					&val);
-	if (!ret) {
-		vol = val.intval;
-		ret = power_supply_get_property(batt,
-						POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-						&val);
+
+	ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, &val);
+	if (!ret)
+		design_cap = UWH_TO_PD_ENERGY(val.intval);
+
+	ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_ENERGY_FULL, &val);
+	if (!ret)
+		charge_cap = UWH_TO_PD_ENERGY(val.intval);
+
+	if (design_cap != BATTERY_PROPERTY_UNKNOWN && charge_cap != BATTERY_PROPERTY_UNKNOWN)
+		goto send_cap;
+
+	ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_VOLTAGE_AVG, &val);
+	if (ret)
+		goto send_cap;
+
+	vol = val.intval;
+	if (design_cap == BATTERY_PROPERTY_UNKNOWN) {
+		ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, &val);
 		if (!ret)
 			design_cap = tcpm_charge_to_energy(val.intval, vol);
+	}
 
-		ret = power_supply_get_property(batt,
-						POWER_SUPPLY_PROP_CHARGE_FULL,
-						&val);
+	if (charge_cap == BATTERY_PROPERTY_UNKNOWN) {
+		ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_CHARGE_FULL, &val);
 		if (!ret)
 			charge_cap = tcpm_charge_to_energy(val.intval, vol);
 	}

---
base-commit: e1e6e541c5c9cf548e9fdc35fc26808c82074440
change-id: 20260820-tcpm-energy-props-upstream-a493353f0054

Best regards,
-- 
Amit Sunil Dhamne <[email protected]>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.