Module Name:    src
Committed By:   jmcneill
Date:           Wed Dec 18 21:19:52 UTC 2024

Modified Files:
        src/sys/dev/acpi: acpi.c acpivar.h

Log Message:
acpi: Honour device dependencies ("_DEP" method).

When creating acpi_devnode devices, build a list of devnodes that are
direct dependencies of this node. The list of dependencies are parent
device nodes (if present), along with all devices returned by the _DEP
method.

When rescanning devices, make sure that all dependencies have been
scanned first. This ensures that drivers attach in the correct order.


To generate a diff of this commit:
cvs rdiff -u -r1.299 -r1.300 src/sys/dev/acpi/acpi.c
cvs rdiff -u -r1.92 -r1.93 src/sys/dev/acpi/acpivar.h

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.c
diff -u src/sys/dev/acpi/acpi.c:1.299 src/sys/dev/acpi/acpi.c:1.300
--- src/sys/dev/acpi/acpi.c:1.299	Wed Mar 20 03:14:45 2024
+++ src/sys/dev/acpi/acpi.c	Wed Dec 18 21:19:52 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: acpi.c,v 1.299 2024/03/20 03:14:45 riastradh Exp $	*/
+/*	$NetBSD: acpi.c,v 1.300 2024/12/18 21:19:52 jmcneill Exp $	*/
 
 /*-
  * Copyright (c) 2003, 2007 The NetBSD Foundation, Inc.
@@ -100,7 +100,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: acpi.c,v 1.299 2024/03/20 03:14:45 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: acpi.c,v 1.300 2024/12/18 21:19:52 jmcneill Exp $");
 
 #include "pci.h"
 #include "opt_acpi.h"
@@ -201,6 +201,7 @@ static bool		acpi_suspend(device_t, cons
 static bool		acpi_resume(device_t, const pmf_qual_t *);
 
 static void		acpi_build_tree(struct acpi_softc *);
+static void		acpi_find_deps(struct acpi_softc *);
 static void		acpi_config_tree(struct acpi_softc *);
 static void		acpi_config_dma(struct acpi_softc *);
 static ACPI_STATUS	acpi_make_devnode(ACPI_HANDLE, uint32_t,
@@ -701,6 +702,11 @@ acpi_build_tree(struct acpi_softc *sc)
 	(void)AcpiWalkNamespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, UINT32_MAX,
 	    acpi_make_devnode, acpi_make_devnode_post, &awc, NULL);
 
+	/*
+	 * Find device dependencies.
+	 */
+	acpi_find_deps(sc);
+
 #if NPCI > 0
 	/*
 	 * Scan the internal namespace.
@@ -710,6 +716,86 @@ acpi_build_tree(struct acpi_softc *sc)
 }
 
 static void
+acpi_add_dep(struct acpi_devnode *ad, struct acpi_devnode *depad)
+{
+	struct acpi_devnodedep *dd;
+
+	dd = kmem_alloc(sizeof(*dd), KM_SLEEP);
+	dd->dd_node = depad;
+	SIMPLEQ_INSERT_TAIL(&ad->ad_deps, dd, dd_list);
+}
+
+static void
+acpi_find_deps(struct acpi_softc *sc)
+{
+	struct acpi_devnode *ad;
+
+	SIMPLEQ_FOREACH(ad, &sc->sc_head, ad_list) {
+		struct acpi_devnode *depad;
+		ACPI_OBJECT *obj;
+		ACPI_HANDLE _dep;
+		ACPI_BUFFER buf;
+		ACPI_STATUS rv;
+		u_int ref;
+
+		if (acpi_is_scope(ad) ||
+		    ad->ad_parent == NULL ||
+		    ad->ad_devinfo->Type != ACPI_TYPE_DEVICE) {
+			continue;
+		}
+
+		/* Add an implicit dependency on parent devices. */
+		if (!acpi_is_scope(ad->ad_parent) &&
+		    ad->ad_parent->ad_devinfo->Type == ACPI_TYPE_DEVICE) {
+			acpi_add_dep(ad, ad->ad_parent);
+		}
+
+		rv = AcpiGetHandle(ad->ad_handle, "_DEP", &_dep);
+		if (ACPI_FAILURE(rv)) {
+			goto logit;
+		}
+
+		buf.Pointer = NULL;
+		buf.Length = ACPI_ALLOCATE_BUFFER;
+		rv = AcpiEvaluateObjectTyped(_dep, NULL, NULL, &buf,
+		    ACPI_TYPE_PACKAGE);
+		if (ACPI_FAILURE(rv)) {
+			goto logit;
+		}
+		obj = buf.Pointer;
+
+		for (ref = 0; ref < obj->Package.Count; ref++) {
+			ACPI_OBJECT *robj = &obj->Package.Elements[ref];
+			ACPI_HANDLE rhdl;
+
+			rv = acpi_eval_reference_handle(robj, &rhdl);
+			if (ACPI_FAILURE(rv)) {
+				continue;
+			}
+
+			depad = acpi_match_node(rhdl);
+			if (depad != NULL) {
+				acpi_add_dep(ad, depad);
+			}
+		}
+
+		ACPI_FREE(buf.Pointer);
+
+logit:
+		if (!SIMPLEQ_EMPTY(&ad->ad_deps)) {
+			struct acpi_devnodedep *dd;
+
+			aprint_debug_dev(sc->sc_dev, "%s dependencies:",
+			    ad->ad_name);
+			SIMPLEQ_FOREACH(dd, &ad->ad_deps, dd_list) {
+				aprint_debug(" %s", dd->dd_node->ad_name);
+			}
+			aprint_debug("\n"); 
+		}
+	}
+}
+
+static void
 acpi_config_tree(struct acpi_softc *sc)
 {
 	/*
@@ -809,6 +895,7 @@ acpi_make_devnode(ACPI_HANDLE handle, ui
 
 		SIMPLEQ_INIT(&ad->ad_child_head);
 		SIMPLEQ_INSERT_TAIL(&sc->sc_head, ad, ad_list);
+		SIMPLEQ_INIT(&ad->ad_deps);
 
 		if (ad->ad_parent != NULL) {
 
@@ -935,9 +1022,62 @@ acpi_rescan(device_t self, const char *i
 }
 
 static void
-acpi_rescan_early(struct acpi_softc *sc)
+acpi_rescan_node(struct acpi_softc *sc, struct acpi_devnode *ad)
 {
+	const char * const hpet_ids[] = { "PNP0103", NULL };
 	struct acpi_attach_args aa;
+	struct acpi_devnodedep *dd;
+	ACPI_DEVICE_INFO *di = ad->ad_devinfo;
+
+	if (ad->ad_scanned || ad->ad_device != NULL) {
+		return;
+	}
+
+	/*
+	 * Mark as scanned before checking dependencies to
+	 * break out of dependency cycles.
+	 */
+	ad->ad_scanned = true;
+
+	if (!acpi_device_present(ad->ad_handle)) {
+		return;
+	}
+
+	if (acpi_match_hid(di, acpi_ignored_ids) != 0) {
+		return;
+	}
+
+	if (acpi_match_hid(di, hpet_ids) != 0 && sc->sc_hpet != NULL) {
+		return;
+	}
+
+	/* Rescan dependencies first. */
+	SIMPLEQ_FOREACH(dd, &ad->ad_deps, dd_list) {
+		if (!dd->dd_node->ad_scanned) {
+			acpi_rescan_node(sc, dd->dd_node);
+		}
+	}
+
+	aa.aa_node = ad;
+	aa.aa_iot = sc->sc_iot;
+	aa.aa_memt = sc->sc_memt;
+	if (ad->ad_pciinfo != NULL) {
+		aa.aa_pc = ad->ad_pciinfo->ap_pc;
+		aa.aa_pciflags = sc->sc_pciflags;
+	}
+	aa.aa_ic = sc->sc_ic;
+	aa.aa_dmat = ad->ad_dmat;
+	aa.aa_dmat64 = ad->ad_dmat64;
+
+	ad->ad_device = config_found(sc->sc_dev, &aa, acpi_print,
+	    CFARGS(.iattr = "acpinodebus",
+		   .devhandle = devhandle_from_acpi(devhandle_invalid(),
+						    ad->ad_handle)));
+}
+
+static void
+acpi_rescan_early(struct acpi_softc *sc)
+{
 	struct acpi_devnode *ad;
 
 	/*
@@ -959,32 +1099,21 @@ acpi_rescan_early(struct acpi_softc *sc)
 
 		KASSERT(ad->ad_handle != NULL);
 
-		aa.aa_node = ad;
-		aa.aa_iot = sc->sc_iot;
-		aa.aa_memt = sc->sc_memt;
-		if (ad->ad_pciinfo != NULL) {
-			aa.aa_pc = ad->ad_pciinfo->ap_pc;
-			aa.aa_pciflags = sc->sc_pciflags;
-		}
-		aa.aa_ic = sc->sc_ic;
-		aa.aa_dmat = ad->ad_dmat;
-		aa.aa_dmat64 = ad->ad_dmat64;
-
-		ad->ad_device = config_found(sc->sc_dev, &aa, acpi_print,
-		    CFARGS(.iattr = "acpinodebus",
-			   .devhandle = devhandle_from_acpi(devhandle_invalid(),
-							    ad->ad_handle)));
+		acpi_rescan_node(sc, ad);
 	}
 }
 
 static void
 acpi_rescan_nodes(struct acpi_softc *sc)
 {
-	const char * const hpet_ids[] = { "PNP0103", NULL };
-	struct acpi_attach_args aa;
 	struct acpi_devnode *ad;
 	ACPI_DEVICE_INFO *di;
 
+	/* Reset scan state. */
+	SIMPLEQ_FOREACH(ad, &sc->sc_head, ad_list) {
+		ad->ad_scanned = false;
+	}
+
 	SIMPLEQ_FOREACH(ad, &sc->sc_head, ad_list) {
 
 		if (ad->ad_device != NULL)
@@ -1019,29 +1148,9 @@ acpi_rescan_nodes(struct acpi_softc *sc)
 		if (acpi_match_hid(di, acpi_early_ids) != 0)
 			continue;
 
-		if (acpi_match_hid(di, acpi_ignored_ids) != 0)
-			continue;
-
-		if (acpi_match_hid(di, hpet_ids) != 0 && sc->sc_hpet != NULL)
-			continue;
-
 		KASSERT(ad->ad_handle != NULL);
 
-		aa.aa_node = ad;
-		aa.aa_iot = sc->sc_iot;
-		aa.aa_memt = sc->sc_memt;
-		if (ad->ad_pciinfo != NULL) {
-			aa.aa_pc = ad->ad_pciinfo->ap_pc;
-			aa.aa_pciflags = sc->sc_pciflags;
-		}
-		aa.aa_ic = sc->sc_ic;
-		aa.aa_dmat = ad->ad_dmat;
-		aa.aa_dmat64 = ad->ad_dmat64;
-
-		ad->ad_device = config_found(sc->sc_dev, &aa, acpi_print,
-		    CFARGS(.iattr = "acpinodebus",
-			   .devhandle = devhandle_from_acpi(devhandle_invalid(),
-							    ad->ad_handle)));
+		acpi_rescan_node(sc, ad);
 	}
 }
 

Index: src/sys/dev/acpi/acpivar.h
diff -u src/sys/dev/acpi/acpivar.h:1.92 src/sys/dev/acpi/acpivar.h:1.93
--- src/sys/dev/acpi/acpivar.h:1.92	Mon Dec  9 22:10:25 2024
+++ src/sys/dev/acpi/acpivar.h	Wed Dec 18 21:19:52 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: acpivar.h,v 1.92 2024/12/09 22:10:25 jmcneill Exp $	*/
+/*	$NetBSD: acpivar.h,v 1.93 2024/12/18 21:19:52 jmcneill Exp $	*/
 
 /*
  * Copyright 2001 Wasabi Systems, Inc.
@@ -102,6 +102,12 @@ struct acpi_pci_info {
 #define ACPI_PCI_INFO_DEVICE	__BIT(0)	/* PCI device */
 #define ACPI_PCI_INFO_BRIDGE	__BIT(1)	/* PCI bridge */
 
+/* Represents a device node dependency. */
+struct acpi_devnodedep {
+	SIMPLEQ_ENTRY(acpi_devnodedep)	 dd_list;
+	struct acpi_devnode		*dd_node;
+};
+
 /*
  * An ACPI device node.
  *
@@ -142,6 +148,9 @@ struct acpi_devnode {
 	SIMPLEQ_ENTRY(acpi_devnode)	ad_list;
 	SIMPLEQ_ENTRY(acpi_devnode)	ad_child_list;
 	SIMPLEQ_HEAD(, acpi_devnode)	ad_child_head;
+	SIMPLEQ_HEAD(, acpi_devnodedep)	ad_deps;
+
+	bool			ad_scanned;	/* private: acpi_rescan state */
 };
 
 /*

Reply via email to