Module Name:    src
Committed By:   jmcneill
Date:           Sun Dec  8 20:44:40 UTC 2024

Modified Files:
        src/sys/dev/acpi: acpi_i2c.c

Log Message:
acpi: find I2C devices that are not children of the controller node

An I2C device does not necessarily have to be a child of an I2C controller
device node. It may be defined elsewhere in the device tree, with a
I2cSerialBusV2 resource that points to the I2C bus that it sits on.

For the simple case of I2C HID devices, we know that they have exactly
one I2C bus resource. When a controller calls acpi_enter_i2c_devs,
search for unclaimed device nodes with a resource that points to this
controller, and add any I2C HID devices found.


To generate a diff of this commit:
cvs rdiff -u -r1.12 -r1.13 src/sys/dev/acpi/acpi_i2c.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/acpi/acpi_i2c.c
diff -u src/sys/dev/acpi/acpi_i2c.c:1.12 src/sys/dev/acpi/acpi_i2c.c:1.13
--- src/sys/dev/acpi/acpi_i2c.c:1.12	Sat Jul 23 03:08:17 2022
+++ src/sys/dev/acpi/acpi_i2c.c	Sun Dec  8 20:44:40 2024
@@ -1,4 +1,4 @@
-/* $NetBSD: acpi_i2c.c,v 1.12 2022/07/23 03:08:17 thorpej Exp $ */
+/* $NetBSD: acpi_i2c.c,v 1.13 2024/12/08 20:44:40 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2017, 2021 The NetBSD Foundation, Inc.
@@ -30,7 +30,9 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: acpi_i2c.c,v 1.12 2022/07/23 03:08:17 thorpej Exp $");
+__KERNEL_RCSID(0, "$NetBSD: acpi_i2c.c,v 1.13 2024/12/08 20:44:40 jmcneill Exp $");
+
+#include <sys/device.h>
 
 #include <dev/acpi/acpireg.h>
 #include <dev/acpi/acpivar.h>
@@ -42,10 +44,45 @@ __KERNEL_RCSID(0, "$NetBSD: acpi_i2c.c,v
 #define _COMPONENT	ACPI_BUS_COMPONENT
 ACPI_MODULE_NAME	("acpi_i2c")
 
+static const struct device_compatible_entry hid_compat_data[] = {
+	{ .compat = "PNP0C50" },
+	DEVICE_COMPAT_EOL
+};
+
 struct acpi_i2c_context {
 	uint16_t i2c_addr;
+	struct acpi_devnode *res_src;
 };
 
+static struct acpi_devnode *
+acpi_i2c_resource_find_source(ACPI_RESOURCE_SOURCE *rs)
+{
+	ACPI_STATUS rv;
+	ACPI_HANDLE hdl;
+	struct acpi_devnode *ad;
+
+	if (rs->StringPtr == NULL) {
+		return NULL;
+	}
+
+	rv = AcpiGetHandle(NULL, rs->StringPtr, &hdl);
+	if (ACPI_FAILURE(rv)) {
+		printf("%s: couldn't lookup '%s': %s\n", __func__,
+		    rs->StringPtr, AcpiFormatException(rv));
+		return NULL;
+	}
+
+	SIMPLEQ_FOREACH(ad, &acpi_softc->sc_head, ad_list) {
+		if (ad->ad_handle == hdl) {
+			return ad;
+		}
+	}
+
+	printf("%s: no acpi devnode matching resource source '%s'\n",
+	    __func__, rs->StringPtr);
+	return NULL;
+}
+
 static ACPI_STATUS
 acpi_i2c_resource_parse_callback(ACPI_RESOURCE *res, void *context)
 {
@@ -58,6 +95,8 @@ acpi_i2c_resource_parse_callback(ACPI_RE
 		switch (res->Data.I2cSerialBus.Type) {
 		case ACPI_RESOURCE_SERIAL_TYPE_I2C:
 			i2cc->i2c_addr = res->Data.I2cSerialBus.SlaveAddress;
+			i2cc->res_src = acpi_i2c_resource_find_source(
+			    &res->Data.I2cSerialBus.ResourceSource);
 			break;
 		}
 		break;
@@ -113,6 +152,40 @@ acpi_enter_i2c_device(struct acpi_devnod
 	prop_object_release(dev);
 }
 
+static void
+acpi_enter_i2chid_devs(device_t dev, struct acpi_devnode *devnode,
+    prop_array_t array)
+{
+	struct acpi_devnode *ad;
+
+	KASSERT(dev != NULL);
+
+	SIMPLEQ_FOREACH(ad, &acpi_softc->sc_head, ad_list) {
+		struct acpi_attach_args aa = {
+			.aa_node = ad
+		};
+		struct acpi_i2c_context i2cc;
+		ACPI_STATUS rv;
+
+		if (!acpi_device_present(ad->ad_handle))
+			continue;
+		if (ad->ad_device != NULL)
+			continue;
+		if (acpi_compatible_match(&aa, hid_compat_data) == 0)
+			continue;
+
+		memset(&i2cc, 0, sizeof(i2cc));
+		rv = AcpiWalkResources(ad->ad_handle, "_CRS",
+		    acpi_i2c_resource_parse_callback, &i2cc);
+		if (ACPI_SUCCESS(rv) &&
+		    i2cc.i2c_addr != 0 &&
+		    i2cc.res_src == devnode) {
+			aprint_debug_dev(dev, "claiming %s\n", ad->ad_name);
+			acpi_enter_i2c_device(ad, array);
+		}
+	}
+}
+
 prop_array_t
 acpi_enter_i2c_devs(device_t dev, struct acpi_devnode *devnode)
 {
@@ -132,6 +205,7 @@ acpi_enter_i2c_devs(device_t dev, struct
 
 	if (dev != NULL) {
 		acpi_claim_childdevs(dev, devnode);
+		acpi_enter_i2chid_devs(dev, devnode, array);
 	}
 
 	return array;

Reply via email to