Index | Thread | Search

From:
me@cyberex.cc <me@cyberex.cc>
Subject:
acpiec(4): fall back to namespace EC device when ECDT namepath is bogus
To:
tech@openbsd.org
Cc:
me@cyberex.cc
Date:
Wed, 28 Oct 2026 07:49:19 +0000

Download raw body.

Thread
Hi,

On a Lenovo IdeaPad L3 15ITL6 (82HL, Tiger Lake) the battery is not
detected on OpenBSD 8.0/amd64 ( GENERIC.MP#84, acpiec.c rev 1.67):

  acpiec at acpi0 not configured
  acpibat0 at acpi0: BAT0 not present

There are no hw.sensors.acpibat0.* sensors at all, apm(8) always reports
"absent", and hw.sensors.acpiac0.indicator0 stays Off even with the AC
adapter connected.

The firmware provides an ECDT whose namepath refers to an object that
does not exist in the namespace:

  ECDT: UID 1, GPE 0x6E, IO 0x62/0x66
  Namepath: "\_SB.PC00.LPCB.H_EC"

The embedded controller described by the DSDT is called EC0:

  Device (EC0) {
      Name (_HID, EisaId ("PNP0C09"))
      Name (_UID, One)
      ...
  }

acpiec attaches from the ECDT (aa->aaa_node == NULL) and
acpiec_getcrs() does:

  sc->sc_devnode = aml_searchname(sc->sc_acpi->sc_root, ecdt->ec_id);

which returns NULL for this namepath, and there is no fallback.  As a
result acpiec_reg() never evaluates _REG on the real EC node.  This
particular DSDT gates every battery/AC method on ECAV, which is only
ever set by EC0._REG:

  Method (_REG, 2) { If (Arg0 == 0x03) { ECAV = Arg1 } ... }

so ECAV stays 0, BAT0._STA() returns 0x0F ("not present") forever and
_BIF()/_BST() return static defaults.  The "acpiec at acpi0 not
configured" line comes from acpi_foundecdt() finding the namespace EC0
later: sc_ec is already set (and its sc_devnode is NULL, so the
equality check does not shortcut the attach).

The diff below makes acpiec_getcrs() fall back to the first PNP0C09
device found in the namespace when the ECDT namepath does not resolve.
It is modelled after acpi_foundecdt() in acpi.c.  Note that simply
ignoring the ECDT and taking the regular _CRS/_GPE path would not work
here: EC0._GPE() reads EC fields (ESPI/SRSP), which cannot be evaluated
before the EC is attached.

Tested on the machine above, with the diff battery, AC status and
charging state all work:

  acpiec0 at acpi0
  acpibat0 at acpi0: BAT0 model "L18D3PF1" serial 49284 type LiP oem "Sunwoda"
  hw.sensors.acpibat0.watthour3=26.97 Wh (remaining capacity), OK
  hw.sensors.acpibat0.raw0=2 (battery charging)
  hw.sensors.acpiac0.indicator0=On (power supply)

The "acpiec ... not configured" message is gone as well.  Full dmesg
(before and after) and an acpidump of this machine are available on
request.

Comments?  In particular, should the fallback also match _UID, or would
it be preferable to retry the attach from the namespace device instead
of binding the ECDT one?

Sergey

Index: sys/dev/acpi/acpiec.c
--- sys/dev/acpi/acpiec.c
+++ sys/dev/acpi/acpiec.c
@@ -49,6 +49,7 @@
 int		acpiec_getcrs(struct acpiec_softc *,
 		   struct acpi_attach_args *);
 int		acpiec_parse_resources(int, union acpi_resource *, void *);
+int		acpiec_foundecdt(struct aml_node *, void *);
 
 void		acpiec_wait(struct acpiec_softc *, uint8_t, uint8_t);
 void		acpiec_sci_event(struct acpiec_softc *);
@@ -435,6 +436,38 @@
 }
 
 int
+acpiec_foundecdt(struct aml_node *node, void *arg)
+{
+	struct acpiec_softc	*sc = arg;
+	struct aml_value	res;
+	const char		*dev;
+
+	if (aml_evalnode(sc->sc_acpi, node, 0, NULL, &res) != 0)
+		return (0);
+
+	switch (res.type) {
+	case AML_OBJTYPE_STRING:
+		dev = res.v_string;
+		break;
+	case AML_OBJTYPE_INTEGER:
+		dev = aml_eisaid(aml_val2int(&res));
+		break;
+	default:
+		aml_freevalue(&res);
+		return (0);
+	}
+
+	if (strcmp(dev, ACPI_DEV_ECD) == 0) {
+		sc->sc_devnode = node->parent;
+		aml_freevalue(&res);
+		return (1);
+	}
+
+	aml_freevalue(&res);
+	return (0);
+}
+
+int
 acpiec_getcrs(struct acpiec_softc *sc, struct acpi_attach_args *aa)
 {
 	struct aml_value	res;
@@ -463,6 +496,19 @@
 		sc->sc_devnode = aml_searchname(sc->sc_acpi->sc_root,
 		   ecdt->ec_id);
 
+		/*
+		* Some BIOSes contain an ECDT whose namepath refers to an
+		* object that does not exist in the namespace (seen on a
+		* Lenovo IdeaPad L3 15ITL6 whose ECDT refers to
+		* \_SB.PC00.LPCB.H_EC while the namespace EC device is
+		* called EC0).  Fall back to the Embedded Controller device
+		* found in the namespace such that _REG is evaluated on the
+		* right node and EC operation regions become available.
+		*/
+		if (sc->sc_devnode == NULL)
+			aml_find_node(sc->sc_acpi->sc_root, "_HID",
+			   acpiec_foundecdt, sc);
+
 		goto ecdtdone;
 	}