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

"Rafael J. Wysocki" <[email protected]>
Newsgroups dev.linux.lists.regressions,org.kernel.vger.linux-acpi,org.kernel.vger.linux-kernel
Organization Linux Kernel Development
Message-ID <[email protected]>
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;
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.