Re: Regression in ACPI battery charging state handling on GLK MRD / Gemini Lake ODM devices

Rafael J. Wysocki posted 1 patch 6 days, 12 hours ago
drivers/acpi/battery.c |   38 ++++++++++++++++++++------------------
1 file changed, 20 insertions(+), 18 deletions(-)
Re: Regression in ACPI battery charging state handling on GLK MRD / Gemini Lake ODM devices
Posted by Rafael J. Wysocki 6 days, 12 hours ago
On Friday, May 29, 2026 9:17:55 PM CEST goldi fiche wrote:
> Kernel versions:
> Works correctly: 6.18.13
> Regression starts: 6.18.16
> Affected hardware:
> Intel Gemini Lake (Celeron N4020)
> GLK MRD platform
> White-label ODM/OEM laptop (Denver branded)
> Broken SMBIOS/DMI firmware
> Relevant commit:
> bb1256e0ddc7e9e406164319769b9f8d8389f056
> "ACPI: battery: fix incorrect charging status when current is zero"
> Problem description:
> The
>  heuristic introduced in the above commit appears to regress charging 
> state reporting on some Gemini Lake GLK MRD systems with broken OEM 
> firmware.
> The firmware reports inconsistent battery information:
> AC connected
> battery charging or pending-charge state active
> but energy-rate/current reported as 0 W
> Example UPower output while charger is connected and battery is at 37%:
> state: pending-charge
> energy-rate: 0 W
> online: yes
> percentage: 37%
> Before this commit/kernel version, the battery charging state behaved correctly from the user perspective.

What exactly do you mean by "correctly"?

I guess you want the status to be "charging" when "percentage" is not 100%?

> The affected system also contains severely broken SMBIOS/DMI data:
> "Default string"
> unresolved AMI template variables
> invalid cache sizes
> "To Be Filled By O.E.M."
> This appears to be common among Gemini Lake white-label ODM devices using GLK MRD firmware.
> A local test patch that bypasses the rate_now == 0 charging-state override restores expected charging-state behavior on this hardware.
> The
>  Huawei-oriented heuristic in the referenced commit appears to be too 
> broad for some GLK MRD systems with faulty EC/ACPI implementations.

It might also check whether or not the battery is full by the
capacity_now/full_charge_capacity ratio, which is done in the
patch below, so please give it a go.

However, it may not work on the Huawei machine in which case
some quirking may be needed.

---
 drivers/acpi/battery.c |   38 ++++++++++++++++++++------------------
 1 file changed, 20 insertions(+), 18 deletions(-)

--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -150,27 +150,28 @@ static int acpi_battery_technology(struc
 
 static int acpi_battery_get_state(struct acpi_battery *battery);
 
-static int acpi_battery_is_charged(struct acpi_battery *battery)
+static bool __acpi_battery_is_charged(struct acpi_battery *battery)
 {
-	/* charging, discharging, critical low or charge limited */
-	if (battery->state != 0)
-		return 0;
-
 	/* battery not reporting charge */
 	if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN ||
 	    battery->capacity_now == 0)
-		return 0;
+		return false;
 
 	/* good batteries update full_charge as the batteries degrade */
-	if (battery->full_charge_capacity == battery->capacity_now)
-		return 1;
+	if (battery->full_charge_capacity <= battery->capacity_now)
+		return true;
 
 	/* fallback to using design values for broken batteries */
-	if (battery->design_capacity <= battery->capacity_now)
-		return 1;
+	return battery->design_capacity <= battery->capacity_now;
+}
 
-	/* we don't do any sort of metric based on percentages */
-	return 0;
+static int acpi_battery_is_charged(struct acpi_battery *battery)
+{
+	/* charging, discharging, critical low or charge limited */
+	if (battery->state != 0)
+		return 0;
+
+	return __acpi_battery_is_charged(battery);
 }
 
 static bool acpi_battery_is_degraded(struct acpi_battery *battery)
@@ -211,13 +212,14 @@ static int acpi_battery_get_property(str
 		if (battery->state & ACPI_BATTERY_STATE_DISCHARGING)
 			val->intval = acpi_battery_handle_discharging(battery);
 		else if (battery->state & ACPI_BATTERY_STATE_CHARGING)
-			/* Validate the status by checking the current. */
-			if (battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN &&
-			    battery->rate_now == 0) {
-				/* On charge but no current (0W/0mA). */
-				val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
-			} else {
+			/* Check the rate and capacity to validate the status. */
+			if (!__acpi_battery_is_charged(battery) ||
+			    (battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN &&
+			     battery->rate_now > 0)) {
 				val->intval = POWER_SUPPLY_STATUS_CHARGING;
+			} else {
+				/* Full and zero rate. */
+				val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
 			}
 		else if (battery->state & ACPI_BATTERY_STATE_CHARGE_LIMITING)
 			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;