1b72d5b75SMichael Corcoran /*
2b72d5b75SMichael Corcoran  * CDDL HEADER START
3b72d5b75SMichael Corcoran  *
4b72d5b75SMichael Corcoran  * The contents of this file are subject to the terms of the
5b72d5b75SMichael Corcoran  * Common Development and Distribution License (the "License").
6b72d5b75SMichael Corcoran  * You may not use this file except in compliance with the License.
7b72d5b75SMichael Corcoran  *
8b72d5b75SMichael Corcoran  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9b72d5b75SMichael Corcoran  * or http://www.opensolaris.org/os/licensing.
10b72d5b75SMichael Corcoran  * See the License for the specific language governing permissions
11b72d5b75SMichael Corcoran  * and limitations under the License.
12b72d5b75SMichael Corcoran  *
13b72d5b75SMichael Corcoran  * When distributing Covered Code, include this CDDL HEADER in each
14b72d5b75SMichael Corcoran  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15b72d5b75SMichael Corcoran  * If applicable, add the following below this CDDL HEADER, with the
16b72d5b75SMichael Corcoran  * fields enclosed by brackets "[]" replaced with your own identifying
17b72d5b75SMichael Corcoran  * information: Portions Copyright [yyyy] [name of copyright owner]
18b72d5b75SMichael Corcoran  *
19b72d5b75SMichael Corcoran  * CDDL HEADER END
20b72d5b75SMichael Corcoran  */
21b72d5b75SMichael Corcoran /*
22a3114836SGerry Liu  * Copyright (c) 2009-2010, Intel Corporation.
23b72d5b75SMichael Corcoran  * All rights reserved.
24672fc84aSRobert Mustacchi  * Copyright (c) 2018, Joyent, Inc.
25*3fe80ca4SDan Cross  * Copyright 2023 Oxide Computer Company
26b72d5b75SMichael Corcoran  */
27b72d5b75SMichael Corcoran 
28b72d5b75SMichael Corcoran /*
29b72d5b75SMichael Corcoran  * Platform specific device enumerator for ACPI specific devices.
30b72d5b75SMichael Corcoran  * "x86 system devices" refers to the suite of hardware components which are
31b72d5b75SMichael Corcoran  * common to the x86 platform and play important roles in the system
32b72d5b75SMichael Corcoran  * architecture but can't be enumerated/discovered through industry-standard
33b72d5b75SMichael Corcoran  * bus specifications. Examples of these x86 system devices include:
34b72d5b75SMichael Corcoran  *   * Logical processor/CPU
35b72d5b75SMichael Corcoran  *   * Memory device
36b72d5b75SMichael Corcoran  *   * Non-PCI discoverable IOMMU or DMA Remapping Engine
37b72d5b75SMichael Corcoran  *   * Non-PCI discoverable IOxAPIC
38b72d5b75SMichael Corcoran  *   * Non-PCI discoverable HPET (High Precision Event Timer)
39b72d5b75SMichael Corcoran  *   * ACPI defined devices, including power button, sleep button, battery etc.
40b72d5b75SMichael Corcoran  *
41b72d5b75SMichael Corcoran  * X86 system devices may be discovered through BIOS/Firmware interfaces, such
42b72d5b75SMichael Corcoran  * as SMBIOS tables, MPS tables and ACPI tables since their discovery isn't
43b72d5b75SMichael Corcoran  * covered by any industry-standard bus specifications.
44b72d5b75SMichael Corcoran  *
45b72d5b75SMichael Corcoran  * In order to aid Solaris in flexibly managing x86 system devices,
46b72d5b75SMichael Corcoran  * x86 system devices are placed into a specific firmware device
47b72d5b75SMichael Corcoran  * subtree whose device path is '/devices/fw'.
48b72d5b75SMichael Corcoran  *
49b72d5b75SMichael Corcoran  * This driver populates the firmware device subtree with ACPI-discoverable
50b72d5b75SMichael Corcoran  * system devices if possible. To achieve that, the ACPI object
51b72d5b75SMichael Corcoran  * namespace is abstracted as ACPI virtual buses which host system devices.
52b72d5b75SMichael Corcoran  * Another nexus driver for the ACPI virtual bus will manage all devices
53b72d5b75SMichael Corcoran  * connected to it.
54b72d5b75SMichael Corcoran  *
55b72d5b75SMichael Corcoran  * For more detailed information, please refer to PSARC/2009/104.
56b72d5b75SMichael Corcoran  */
57b72d5b75SMichael Corcoran 
58b72d5b75SMichael Corcoran #include <sys/types.h>
59b72d5b75SMichael Corcoran #include <sys/bitmap.h>
60b72d5b75SMichael Corcoran #include <sys/cmn_err.h>
61b72d5b75SMichael Corcoran #include <sys/ddi_subrdefs.h>
62b72d5b75SMichael Corcoran #include <sys/errno.h>
63b72d5b75SMichael Corcoran #include <sys/modctl.h>
64b72d5b75SMichael Corcoran #include <sys/mutex.h>
65a3114836SGerry Liu #include <sys/note.h>
66b72d5b75SMichael Corcoran #include <sys/obpdefs.h>
67b72d5b75SMichael Corcoran #include <sys/sunddi.h>
68b72d5b75SMichael Corcoran #include <sys/sunndi.h>
69b72d5b75SMichael Corcoran #include <sys/acpi/acpi.h>
70b72d5b75SMichael Corcoran #include <sys/acpica.h>
71b72d5b75SMichael Corcoran #include <sys/acpidev.h>
72a3114836SGerry Liu #include <sys/acpidev_dr.h>
73b72d5b75SMichael Corcoran #include <sys/acpidev_impl.h>
74b72d5b75SMichael Corcoran 
75b72d5b75SMichael Corcoran /* Patchable through /etc/system */
76b72d5b75SMichael Corcoran int acpidev_options = 0;
77b72d5b75SMichael Corcoran int acpidev_debug = 0;
78b72d5b75SMichael Corcoran 
79a3114836SGerry Liu krwlock_t acpidev_class_lock;
80b72d5b75SMichael Corcoran acpidev_class_list_t *acpidev_class_list_root = NULL;
81a3114836SGerry Liu ulong_t acpidev_object_type_mask[BT_BITOUL(ACPI_TYPE_NS_NODE_MAX + 1)];
82b72d5b75SMichael Corcoran 
83b72d5b75SMichael Corcoran /* ACPI device autoconfig global status */
84b72d5b75SMichael Corcoran typedef enum acpidev_status {
85b72d5b75SMichael Corcoran 	ACPIDEV_STATUS_FAILED = -2,	/* ACPI device autoconfig failed */
86b72d5b75SMichael Corcoran 	ACPIDEV_STATUS_DISABLED = -1,	/* ACPI device autoconfig disabled */
87b72d5b75SMichael Corcoran 	ACPIDEV_STATUS_UNKNOWN = 0,	/* initial status */
88b72d5b75SMichael Corcoran 	ACPIDEV_STATUS_INITIALIZED,	/* ACPI device autoconfig initialized */
89b72d5b75SMichael Corcoran 	ACPIDEV_STATUS_FIRST_PASS,	/* first probing finished */
90b72d5b75SMichael Corcoran 	ACPIDEV_STATUS_READY		/* second probing finished */
91b72d5b75SMichael Corcoran } acpidev_status_t;
92b72d5b75SMichael Corcoran 
93b72d5b75SMichael Corcoran static acpidev_status_t acpidev_status = ACPIDEV_STATUS_UNKNOWN;
94b72d5b75SMichael Corcoran static kmutex_t	acpidev_drv_lock;
95b72d5b75SMichael Corcoran static dev_info_t *acpidev_root_dip = NULL;
96b72d5b75SMichael Corcoran 
97b72d5b75SMichael Corcoran /* Boot time ACPI device enumerator. */
98b72d5b75SMichael Corcoran static void acpidev_boot_probe(int type);
99b72d5b75SMichael Corcoran 
100b72d5b75SMichael Corcoran /* DDI module auto configuration interface */
101b72d5b75SMichael Corcoran extern struct mod_ops mod_miscops;
102b72d5b75SMichael Corcoran 
103b72d5b75SMichael Corcoran static struct modlmisc modlmisc = {
104b72d5b75SMichael Corcoran 	&mod_miscops,
105b72d5b75SMichael Corcoran 	"ACPI device enumerator"
106b72d5b75SMichael Corcoran };
107b72d5b75SMichael Corcoran 
108b72d5b75SMichael Corcoran static struct modlinkage modlinkage = {
109b72d5b75SMichael Corcoran 	MODREV_1,
110b72d5b75SMichael Corcoran 	(void *)&modlmisc,
111b72d5b75SMichael Corcoran 	NULL
112b72d5b75SMichael Corcoran };
113b72d5b75SMichael Corcoran 
114b72d5b75SMichael Corcoran int
_init(void)115b72d5b75SMichael Corcoran _init(void)
116b72d5b75SMichael Corcoran {
117b72d5b75SMichael Corcoran 	int err;
118b72d5b75SMichael Corcoran 
119b72d5b75SMichael Corcoran 	if ((err = mod_install(&modlinkage)) == 0) {
120b72d5b75SMichael Corcoran 		bzero(acpidev_object_type_mask,
121b72d5b75SMichael Corcoran 		    sizeof (acpidev_object_type_mask));
122b72d5b75SMichael Corcoran 		mutex_init(&acpidev_drv_lock, NULL, MUTEX_DRIVER, NULL);
123b72d5b75SMichael Corcoran 		rw_init(&acpidev_class_lock, NULL, RW_DEFAULT, NULL);
124a3114836SGerry Liu 		acpidev_dr_init();
125b72d5b75SMichael Corcoran 		impl_bus_add_probe(acpidev_boot_probe);
126b72d5b75SMichael Corcoran 	} else {
127b72d5b75SMichael Corcoran 		cmn_err(CE_WARN, "!acpidev: failed to install driver.");
128b72d5b75SMichael Corcoran 	}
129b72d5b75SMichael Corcoran 
130b72d5b75SMichael Corcoran 	return (err);
131b72d5b75SMichael Corcoran }
132b72d5b75SMichael Corcoran 
133b72d5b75SMichael Corcoran int
_fini(void)134b72d5b75SMichael Corcoran _fini(void)
135b72d5b75SMichael Corcoran {
136b72d5b75SMichael Corcoran 	/* No support for module unload. */
137b72d5b75SMichael Corcoran 	return (EBUSY);
138b72d5b75SMichael Corcoran }
139b72d5b75SMichael Corcoran 
140b72d5b75SMichael Corcoran int
_info(struct modinfo * modinfop)141b72d5b75SMichael Corcoran _info(struct modinfo *modinfop)
142b72d5b75SMichael Corcoran {
143b72d5b75SMichael Corcoran 	return (mod_info(&modlinkage, modinfop));
144b72d5b75SMichael Corcoran }
145b72d5b75SMichael Corcoran 
146b72d5b75SMichael Corcoran /* Check blacklists and load platform specific driver modules. */
147b72d5b75SMichael Corcoran static ACPI_STATUS
acpidev_load_plat_modules(void)148b72d5b75SMichael Corcoran acpidev_load_plat_modules(void)
149b72d5b75SMichael Corcoran {
150b72d5b75SMichael Corcoran 	return (AE_OK);
151b72d5b75SMichael Corcoran }
152b72d5b75SMichael Corcoran 
153b72d5b75SMichael Corcoran /* Unload platform specific driver modules. */
154b72d5b75SMichael Corcoran static void
acpidev_unload_plat_modules(void)155b72d5b75SMichael Corcoran acpidev_unload_plat_modules(void)
156b72d5b75SMichael Corcoran {
157b72d5b75SMichael Corcoran }
158b72d5b75SMichael Corcoran 
159b72d5b75SMichael Corcoran /* Unregister all device class drivers from the device driver lists. */
160b72d5b75SMichael Corcoran static void
acpidev_class_list_fini(void)161b72d5b75SMichael Corcoran acpidev_class_list_fini(void)
162b72d5b75SMichael Corcoran {
163b72d5b75SMichael Corcoran 	acpidev_unload_plat_modules();
164b72d5b75SMichael Corcoran 
165672fc84aSRobert Mustacchi 	(void) acpidev_unregister_class(&acpidev_class_list_usbport,
166672fc84aSRobert Mustacchi 	    &acpidev_class_usbport);
167672fc84aSRobert Mustacchi 
168a3114836SGerry Liu 	if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) {
169a3114836SGerry Liu 		(void) acpidev_unregister_class(&acpidev_class_list_scope,
170a3114836SGerry Liu 		    &acpidev_class_pci);
171a3114836SGerry Liu 		(void) acpidev_unregister_class(&acpidev_class_list_device,
172a3114836SGerry Liu 		    &acpidev_class_pci);
173a3114836SGerry Liu 	}
174a3114836SGerry Liu 
175b72d5b75SMichael Corcoran 	if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) {
176b72d5b75SMichael Corcoran 		(void) acpidev_unregister_class(&acpidev_class_list_device,
177b72d5b75SMichael Corcoran 		    &acpidev_class_memory);
178b72d5b75SMichael Corcoran 	}
179b72d5b75SMichael Corcoran 
180b72d5b75SMichael Corcoran 	if (acpidev_options & ACPIDEV_OUSER_NO_CPU) {
181b72d5b75SMichael Corcoran 		(void) acpidev_unregister_class(&acpidev_class_list_device,
182b72d5b75SMichael Corcoran 		    &acpidev_class_cpu);
183b72d5b75SMichael Corcoran 		(void) acpidev_unregister_class(&acpidev_class_list_scope,
184b72d5b75SMichael Corcoran 		    &acpidev_class_cpu);
185b72d5b75SMichael Corcoran 		(void) acpidev_unregister_class(&acpidev_class_list_root,
186b72d5b75SMichael Corcoran 		    &acpidev_class_cpu);
187b72d5b75SMichael Corcoran 	}
188b72d5b75SMichael Corcoran 
189b72d5b75SMichael Corcoran 	if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) {
190b72d5b75SMichael Corcoran 		(void) acpidev_unregister_class(&acpidev_class_list_device,
191b72d5b75SMichael Corcoran 		    &acpidev_class_container);
192b72d5b75SMichael Corcoran 	}
193b72d5b75SMichael Corcoran 
194b72d5b75SMichael Corcoran 	(void) acpidev_unregister_class(&acpidev_class_list_device,
195b72d5b75SMichael Corcoran 	    &acpidev_class_device);
196b72d5b75SMichael Corcoran 	(void) acpidev_unregister_class(&acpidev_class_list_root,
197b72d5b75SMichael Corcoran 	    &acpidev_class_device);
198b72d5b75SMichael Corcoran 
199b72d5b75SMichael Corcoran 	(void) acpidev_unregister_class(&acpidev_class_list_root,
200b72d5b75SMichael Corcoran 	    &acpidev_class_scope);
201b72d5b75SMichael Corcoran }
202b72d5b75SMichael Corcoran 
203b72d5b75SMichael Corcoran /* Register all device class drivers onto the driver lists. */
204b72d5b75SMichael Corcoran static ACPI_STATUS
acpidev_class_list_init(uint64_t * fp)205b72d5b75SMichael Corcoran acpidev_class_list_init(uint64_t *fp)
206b72d5b75SMichael Corcoran {
207b72d5b75SMichael Corcoran 	ACPI_STATUS rc = AE_OK;
208b72d5b75SMichael Corcoran 
209b72d5b75SMichael Corcoran 	/* Set bit in mask for supported object types. */
210b72d5b75SMichael Corcoran 	BT_SET(acpidev_object_type_mask, ACPI_TYPE_LOCAL_SCOPE);
211b72d5b75SMichael Corcoran 	BT_SET(acpidev_object_type_mask, ACPI_TYPE_DEVICE);
212b72d5b75SMichael Corcoran 
213b72d5b75SMichael Corcoran 	/*
214b72d5b75SMichael Corcoran 	 * Register the ACPI scope class driver onto the class driver lists.
215b72d5b75SMichael Corcoran 	 * Currently only ACPI scope objects under ACPI root node, such as _PR,
216b72d5b75SMichael Corcoran 	 * _SB, _TZ etc, need to be handled, so only register the scope class
217b72d5b75SMichael Corcoran 	 * driver onto the root list.
218b72d5b75SMichael Corcoran 	 */
219b72d5b75SMichael Corcoran 	if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_root,
220b72d5b75SMichael Corcoran 	    &acpidev_class_scope, B_FALSE))) {
221b72d5b75SMichael Corcoran 		goto error_out;
222b72d5b75SMichael Corcoran 	}
223b72d5b75SMichael Corcoran 
224b72d5b75SMichael Corcoran 	/*
225b72d5b75SMichael Corcoran 	 * Register the ACPI device class driver onto the class driver lists.
226b72d5b75SMichael Corcoran 	 * The ACPI device class driver should be registered at the tail to
227b72d5b75SMichael Corcoran 	 * handle all device objects which haven't been handled by other
228b72d5b75SMichael Corcoran 	 * HID/CID specific device class drivers.
229b72d5b75SMichael Corcoran 	 */
230b72d5b75SMichael Corcoran 	if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_root,
231b72d5b75SMichael Corcoran 	    &acpidev_class_device, B_TRUE))) {
232b72d5b75SMichael Corcoran 		goto error_root_device;
233b72d5b75SMichael Corcoran 	}
234b72d5b75SMichael Corcoran 	if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_device,
235b72d5b75SMichael Corcoran 	    &acpidev_class_device, B_TRUE))) {
236b72d5b75SMichael Corcoran 		goto error_device_device;
237b72d5b75SMichael Corcoran 	}
238b72d5b75SMichael Corcoran 
239b72d5b75SMichael Corcoran 	/* Check and register support for ACPI container device. */
240b72d5b75SMichael Corcoran 	if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) {
241b72d5b75SMichael Corcoran 		if (ACPI_FAILURE(acpidev_register_class(
242b72d5b75SMichael Corcoran 		    &acpidev_class_list_device, &acpidev_class_container,
243b72d5b75SMichael Corcoran 		    B_FALSE))) {
244b72d5b75SMichael Corcoran 			goto error_device_container;
245b72d5b75SMichael Corcoran 		}
246b72d5b75SMichael Corcoran 		*fp |= ACPI_DEVCFG_CONTAINER;
247b72d5b75SMichael Corcoran 	}
248b72d5b75SMichael Corcoran 
249b72d5b75SMichael Corcoran 	/* Check and register support for ACPI CPU device. */
250b72d5b75SMichael Corcoran 	if ((acpidev_options & ACPIDEV_OUSER_NO_CPU) == 0) {
251b72d5b75SMichael Corcoran 		/* Handle ACPI CPU Device */
252b72d5b75SMichael Corcoran 		if (ACPI_FAILURE(acpidev_register_class(
253b72d5b75SMichael Corcoran 		    &acpidev_class_list_device, &acpidev_class_cpu, B_FALSE))) {
254b72d5b75SMichael Corcoran 			goto error_device_cpu;
255b72d5b75SMichael Corcoran 		}
256b72d5b75SMichael Corcoran 		/* Handle ACPI Processor under _PR */
257b72d5b75SMichael Corcoran 		if (ACPI_FAILURE(acpidev_register_class(
258b72d5b75SMichael Corcoran 		    &acpidev_class_list_scope, &acpidev_class_cpu, B_FALSE))) {
259b72d5b75SMichael Corcoran 			goto error_scope_cpu;
260b72d5b75SMichael Corcoran 		}
261b72d5b75SMichael Corcoran 		/* House-keeping for CPU scan */
262b72d5b75SMichael Corcoran 		if (ACPI_FAILURE(acpidev_register_class(
263b72d5b75SMichael Corcoran 		    &acpidev_class_list_root, &acpidev_class_cpu, B_FALSE))) {
264b72d5b75SMichael Corcoran 			goto error_root_cpu;
265b72d5b75SMichael Corcoran 		}
266b72d5b75SMichael Corcoran 		BT_SET(acpidev_object_type_mask, ACPI_TYPE_PROCESSOR);
267b72d5b75SMichael Corcoran 		*fp |= ACPI_DEVCFG_CPU;
268b72d5b75SMichael Corcoran 	}
269b72d5b75SMichael Corcoran 
270a3114836SGerry Liu 	/* Check support of ACPI memory devices. */
271b72d5b75SMichael Corcoran 	if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) {
272b72d5b75SMichael Corcoran 		/*
273b72d5b75SMichael Corcoran 		 * Register the ACPI memory class driver onto the
274b72d5b75SMichael Corcoran 		 * acpidev_class_list_device list because ACPI module
275b72d5b75SMichael Corcoran 		 * class driver uses that list.
276b72d5b75SMichael Corcoran 		 */
277b72d5b75SMichael Corcoran 		if (ACPI_FAILURE(acpidev_register_class(
278b72d5b75SMichael Corcoran 		    &acpidev_class_list_device, &acpidev_class_memory,
279b72d5b75SMichael Corcoran 		    B_FALSE))) {
280b72d5b75SMichael Corcoran 			goto error_device_memory;
281b72d5b75SMichael Corcoran 		}
282b72d5b75SMichael Corcoran 		*fp |= ACPI_DEVCFG_MEMORY;
283b72d5b75SMichael Corcoran 	}
284b72d5b75SMichael Corcoran 
285a3114836SGerry Liu 	/* Check support of PCI/PCIex Host Bridge devices. */
286a3114836SGerry Liu 	if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) {
287a3114836SGerry Liu 		/*
288a3114836SGerry Liu 		 * Register pci/pciex class drivers onto
289a3114836SGerry Liu 		 * the acpidev_class_list_device class list because ACPI
290a3114836SGerry Liu 		 * module class driver uses that list.
291a3114836SGerry Liu 		 */
292a3114836SGerry Liu 		if (ACPI_FAILURE(acpidev_register_class(
293a3114836SGerry Liu 		    &acpidev_class_list_device, &acpidev_class_pci,
294a3114836SGerry Liu 		    B_FALSE))) {
295a3114836SGerry Liu 			goto error_device_pci;
296a3114836SGerry Liu 		}
297a3114836SGerry Liu 
298a3114836SGerry Liu 		/*
299a3114836SGerry Liu 		 * Register pci/pciex class drivers onto the
300a3114836SGerry Liu 		 * acpidev_class_list_scope class list.
301a3114836SGerry Liu 		 */
302a3114836SGerry Liu 		if (ACPI_FAILURE(acpidev_register_class(
303a3114836SGerry Liu 		    &acpidev_class_list_scope, &acpidev_class_pci,
304a3114836SGerry Liu 		    B_FALSE))) {
305a3114836SGerry Liu 			goto error_scope_pci;
306a3114836SGerry Liu 		}
307a3114836SGerry Liu 
308a3114836SGerry Liu 		*fp |= ACPI_DEVCFG_PCI;
309a3114836SGerry Liu 	}
310a3114836SGerry Liu 
311672fc84aSRobert Mustacchi 	/* Check support of USB port enumeration */
312672fc84aSRobert Mustacchi 	if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_usbport,
313672fc84aSRobert Mustacchi 	    &acpidev_class_usbport, B_TRUE))) {
314672fc84aSRobert Mustacchi 		goto error_usbport;
315672fc84aSRobert Mustacchi 	}
316672fc84aSRobert Mustacchi 
317672fc84aSRobert Mustacchi 
318b72d5b75SMichael Corcoran 	/* Check blacklist and load platform specific modules. */
319b72d5b75SMichael Corcoran 	rc = acpidev_load_plat_modules();
320b72d5b75SMichael Corcoran 	if (ACPI_FAILURE(rc)) {
321a3114836SGerry Liu 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to check blacklist "
322b72d5b75SMichael Corcoran 		    "or load pratform modules.");
323b72d5b75SMichael Corcoran 		goto error_plat;
324b72d5b75SMichael Corcoran 	}
325b72d5b75SMichael Corcoran 
326b72d5b75SMichael Corcoran 	return (AE_OK);
327b72d5b75SMichael Corcoran 
328b72d5b75SMichael Corcoran error_plat:
329a3114836SGerry Liu 	if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) {
330a3114836SGerry Liu 		(void) acpidev_unregister_class(&acpidev_class_list_scope,
331a3114836SGerry Liu 		    &acpidev_class_pci);
332a3114836SGerry Liu 	}
333672fc84aSRobert Mustacchi 
334672fc84aSRobert Mustacchi error_usbport:
335672fc84aSRobert Mustacchi 	(void) acpidev_unregister_class(&acpidev_class_list_usbport,
336672fc84aSRobert Mustacchi 	    &acpidev_class_usbport);
337672fc84aSRobert Mustacchi 
338a3114836SGerry Liu error_scope_pci:
339a3114836SGerry Liu 	if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) {
340a3114836SGerry Liu 		(void) acpidev_unregister_class(&acpidev_class_list_device,
341a3114836SGerry Liu 		    &acpidev_class_pci);
342a3114836SGerry Liu 	}
343a3114836SGerry Liu error_device_pci:
344b72d5b75SMichael Corcoran 	if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) {
345b72d5b75SMichael Corcoran 		(void) acpidev_unregister_class(&acpidev_class_list_device,
346b72d5b75SMichael Corcoran 		    &acpidev_class_memory);
347b72d5b75SMichael Corcoran 	}
348b72d5b75SMichael Corcoran error_device_memory:
349b72d5b75SMichael Corcoran 	if (acpidev_options & ACPIDEV_OUSER_NO_CPU) {
350b72d5b75SMichael Corcoran 		(void) acpidev_unregister_class(&acpidev_class_list_root,
351b72d5b75SMichael Corcoran 		    &acpidev_class_cpu);
352b72d5b75SMichael Corcoran 	}
353b72d5b75SMichael Corcoran error_root_cpu:
354b72d5b75SMichael Corcoran 	if (acpidev_options & ACPIDEV_OUSER_NO_CPU) {
355b72d5b75SMichael Corcoran 		(void) acpidev_unregister_class(&acpidev_class_list_scope,
356b72d5b75SMichael Corcoran 		    &acpidev_class_cpu);
357b72d5b75SMichael Corcoran 	}
358b72d5b75SMichael Corcoran error_scope_cpu:
359b72d5b75SMichael Corcoran 	if (acpidev_options & ACPIDEV_OUSER_NO_CPU) {
360b72d5b75SMichael Corcoran 		(void) acpidev_unregister_class(&acpidev_class_list_device,
361b72d5b75SMichael Corcoran 		    &acpidev_class_cpu);
362b72d5b75SMichael Corcoran 	}
363b72d5b75SMichael Corcoran error_device_cpu:
364b72d5b75SMichael Corcoran 	if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) {
365b72d5b75SMichael Corcoran 		(void) acpidev_unregister_class(&acpidev_class_list_device,
366b72d5b75SMichael Corcoran 		    &acpidev_class_container);
367b72d5b75SMichael Corcoran 	}
368b72d5b75SMichael Corcoran error_device_container:
369b72d5b75SMichael Corcoran 	(void) acpidev_unregister_class(&acpidev_class_list_device,
370b72d5b75SMichael Corcoran 	    &acpidev_class_device);
371b72d5b75SMichael Corcoran error_device_device:
372b72d5b75SMichael Corcoran 	(void) acpidev_unregister_class(&acpidev_class_list_root,
373b72d5b75SMichael Corcoran 	    &acpidev_class_device);
374b72d5b75SMichael Corcoran error_root_device:
375b72d5b75SMichael Corcoran 	(void) acpidev_unregister_class(&acpidev_class_list_root,
376b72d5b75SMichael Corcoran 	    &acpidev_class_scope);
377b72d5b75SMichael Corcoran error_out:
378b72d5b75SMichael Corcoran 	ACPIDEV_DEBUG(CE_WARN,
379a3114836SGerry Liu 	    "!acpidev: failed to register built-in class drivers.");
380b72d5b75SMichael Corcoran 	*fp = 0;
381b72d5b75SMichael Corcoran 
382b72d5b75SMichael Corcoran 	return (AE_ERROR);
383b72d5b75SMichael Corcoran }
384b72d5b75SMichael Corcoran 
385b72d5b75SMichael Corcoran /*
386b72d5b75SMichael Corcoran  * Called in single threaded context during boot, no protection for
387b72d5b75SMichael Corcoran  * reentrance.
388b72d5b75SMichael Corcoran  */
389b72d5b75SMichael Corcoran static ACPI_STATUS
acpidev_create_root_node(void)390b72d5b75SMichael Corcoran acpidev_create_root_node(void)
391b72d5b75SMichael Corcoran {
392*3fe80ca4SDan Cross 	int rv = AE_OK;
393b72d5b75SMichael Corcoran 	dev_info_t *dip = NULL;
394b72d5b75SMichael Corcoran 	acpidev_data_handle_t objhdl;
395b72d5b75SMichael Corcoran 	char *compatibles[] = {
396b72d5b75SMichael Corcoran 		ACPIDEV_HID_ROOTNEX,
397b72d5b75SMichael Corcoran 		ACPIDEV_TYPE_ROOTNEX,
398b72d5b75SMichael Corcoran 		ACPIDEV_HID_VIRTNEX,
399b72d5b75SMichael Corcoran 		ACPIDEV_TYPE_VIRTNEX,
400b72d5b75SMichael Corcoran 	};
401b72d5b75SMichael Corcoran 
402*3fe80ca4SDan Cross 	ndi_devi_enter(ddi_root_node());
403b72d5b75SMichael Corcoran 	ASSERT(acpidev_root_dip == NULL);
404b72d5b75SMichael Corcoran 
405b72d5b75SMichael Corcoran 	/* Query whether device node already exists. */
406b72d5b75SMichael Corcoran 	dip = ddi_find_devinfo(ACPIDEV_NODE_NAME_ROOT, -1, 0);
407b72d5b75SMichael Corcoran 	if (dip != NULL && ddi_get_parent(dip) == ddi_root_node()) {
408*3fe80ca4SDan Cross 		ndi_devi_exit(ddi_root_node());
409b72d5b75SMichael Corcoran 		cmn_err(CE_WARN, "!acpidev: node /devices/%s already exists, "
410b72d5b75SMichael Corcoran 		    "disable driver.", ACPIDEV_NODE_NAME_ROOT);
411b72d5b75SMichael Corcoran 		return (AE_ALREADY_EXISTS);
412b72d5b75SMichael Corcoran 	}
413b72d5b75SMichael Corcoran 
414b72d5b75SMichael Corcoran 	/* Create the device node if it doesn't exist. */
415b72d5b75SMichael Corcoran 	rv = ndi_devi_alloc(ddi_root_node(), ACPIDEV_NODE_NAME_ROOT,
416b72d5b75SMichael Corcoran 	    (pnode_t)DEVI_SID_NODEID, &dip);
417b72d5b75SMichael Corcoran 	if (rv != NDI_SUCCESS) {
418*3fe80ca4SDan Cross 		ndi_devi_exit(ddi_root_node());
419a3114836SGerry Liu 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to create device node "
420b72d5b75SMichael Corcoran 		    "for ACPI root with errcode %d.", rv);
421b72d5b75SMichael Corcoran 		return (AE_ERROR);
422b72d5b75SMichael Corcoran 	}
423b72d5b75SMichael Corcoran 
424b72d5b75SMichael Corcoran 	/* Build cross reference between dip and ACPI object. */
425b72d5b75SMichael Corcoran 	if (ACPI_FAILURE(acpica_tag_devinfo(dip, ACPI_ROOT_OBJECT))) {
426b72d5b75SMichael Corcoran 		(void) ddi_remove_child(dip, 0);
427*3fe80ca4SDan Cross 		ndi_devi_exit(ddi_root_node());
428a3114836SGerry Liu 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to tag object %s.",
429b72d5b75SMichael Corcoran 		    ACPIDEV_OBJECT_NAME_SB);
430b72d5b75SMichael Corcoran 		return (AE_ERROR);
431b72d5b75SMichael Corcoran 	}
432b72d5b75SMichael Corcoran 
433b72d5b75SMichael Corcoran 	/* Set device properties. */
434b72d5b75SMichael Corcoran 	rv = ndi_prop_update_string_array(DDI_DEV_T_NONE, dip,
435b72d5b75SMichael Corcoran 	    OBP_COMPATIBLE, ACPIDEV_ARRAY_PARAM(compatibles));
436b72d5b75SMichael Corcoran 	if (rv == NDI_SUCCESS) {
437b72d5b75SMichael Corcoran 		rv = ndi_prop_update_string(DDI_DEV_T_NONE, dip,
438b72d5b75SMichael Corcoran 		    OBP_DEVICETYPE, ACPIDEV_TYPE_ROOTNEX);
439b72d5b75SMichael Corcoran 	}
440b72d5b75SMichael Corcoran 	if (rv != DDI_SUCCESS) {
441b72d5b75SMichael Corcoran 		ACPIDEV_DEBUG(CE_WARN,
442a3114836SGerry Liu 		    "!acpidev: failed to set device property for /devices/%s.",
443b72d5b75SMichael Corcoran 		    ACPIDEV_NODE_NAME_ROOT);
444b72d5b75SMichael Corcoran 		goto error_out;
445b72d5b75SMichael Corcoran 	}
446b72d5b75SMichael Corcoran 
447b72d5b75SMichael Corcoran 	/* Manually create an object handle for the root node */
448b72d5b75SMichael Corcoran 	objhdl = acpidev_data_create_handle(ACPI_ROOT_OBJECT);
449b72d5b75SMichael Corcoran 	if (objhdl == NULL) {
450a3114836SGerry Liu 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to create object "
451b72d5b75SMichael Corcoran 		    "handle for the root node.");
452b72d5b75SMichael Corcoran 		goto error_out;
453b72d5b75SMichael Corcoran 	}
454b72d5b75SMichael Corcoran 	objhdl->aod_level = 0;
455b72d5b75SMichael Corcoran 	objhdl->aod_hdl = ACPI_ROOT_OBJECT;
456b72d5b75SMichael Corcoran 	objhdl->aod_dip = dip;
457b72d5b75SMichael Corcoran 	objhdl->aod_class = &acpidev_class_scope;
458b72d5b75SMichael Corcoran 	objhdl->aod_status = acpidev_query_device_status(ACPI_ROOT_OBJECT);
459b72d5b75SMichael Corcoran 	objhdl->aod_iflag = ACPIDEV_ODF_STATUS_VALID |
460b72d5b75SMichael Corcoran 	    ACPIDEV_ODF_DEVINFO_CREATED | ACPIDEV_ODF_DEVINFO_TAGGED;
461b72d5b75SMichael Corcoran 
462b72d5b75SMichael Corcoran 	/* Bind device driver. */
463b72d5b75SMichael Corcoran 	(void) ndi_devi_bind_driver(dip, 0);
464b72d5b75SMichael Corcoran 
465b72d5b75SMichael Corcoran 	acpidev_root_dip = dip;
466*3fe80ca4SDan Cross 	ndi_devi_exit(ddi_root_node());
467b72d5b75SMichael Corcoran 
468b72d5b75SMichael Corcoran 	return (AE_OK);
469b72d5b75SMichael Corcoran 
470b72d5b75SMichael Corcoran error_out:
471b72d5b75SMichael Corcoran 	(void) acpica_untag_devinfo(dip, ACPI_ROOT_OBJECT);
472b72d5b75SMichael Corcoran 	(void) ddi_remove_child(dip, 0);
473*3fe80ca4SDan Cross 	ndi_devi_exit(ddi_root_node());
474b72d5b75SMichael Corcoran 	return (AE_ERROR);
475b72d5b75SMichael Corcoran }
476b72d5b75SMichael Corcoran 
477b72d5b75SMichael Corcoran static void
acpidev_initialize(void)478b72d5b75SMichael Corcoran acpidev_initialize(void)
479b72d5b75SMichael Corcoran {
480b72d5b75SMichael Corcoran 	int rc;
481b72d5b75SMichael Corcoran 	char *str = NULL;
482b72d5b75SMichael Corcoran 	uint64_t features = 0;
483b72d5b75SMichael Corcoran 
484b72d5b75SMichael Corcoran 	/* Check whether it has already been initialized. */
485b72d5b75SMichael Corcoran 	if (acpidev_status == ACPIDEV_STATUS_DISABLED) {
486b72d5b75SMichael Corcoran 		cmn_err(CE_CONT, "?acpidev: ACPI device autoconfig "
487b72d5b75SMichael Corcoran 		    "disabled by user.\n");
488b72d5b75SMichael Corcoran 		return;
489b72d5b75SMichael Corcoran 	} else if (acpidev_status != ACPIDEV_STATUS_UNKNOWN) {
490b72d5b75SMichael Corcoran 		ACPIDEV_DEBUG(CE_NOTE,
491a3114836SGerry Liu 		    "!acpidev: initialization called more than once.");
492b72d5b75SMichael Corcoran 		return;
493b72d5b75SMichael Corcoran 	}
494b72d5b75SMichael Corcoran 
495b72d5b75SMichael Corcoran 	/* Check whether ACPI device autoconfig has been disabled by user. */
496b72d5b75SMichael Corcoran 	rc = ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
497b72d5b75SMichael Corcoran 	    DDI_PROP_DONTPASS, "acpidev-autoconfig", &str);
498b72d5b75SMichael Corcoran 	if (rc == DDI_SUCCESS) {
499b72d5b75SMichael Corcoran 		if (strcasecmp(str, "off") == 0 || strcasecmp(str, "no") == 0) {
500b72d5b75SMichael Corcoran 			cmn_err(CE_CONT, "?acpidev: ACPI device autoconfig "
501b72d5b75SMichael Corcoran 			    "disabled by user.\n");
502b72d5b75SMichael Corcoran 			ddi_prop_free(str);
503b72d5b75SMichael Corcoran 			acpidev_status = ACPIDEV_STATUS_DISABLED;
504b72d5b75SMichael Corcoran 			return;
505b72d5b75SMichael Corcoran 		}
506b72d5b75SMichael Corcoran 		ddi_prop_free(str);
507b72d5b75SMichael Corcoran 	}
508b72d5b75SMichael Corcoran 
509b72d5b75SMichael Corcoran 	/* Initialize acpica subsystem. */
510b72d5b75SMichael Corcoran 	if (ACPI_FAILURE(acpica_init())) {
511b72d5b75SMichael Corcoran 		cmn_err(CE_WARN,
512b72d5b75SMichael Corcoran 		    "!acpidev: failed to initialize acpica subsystem.");
513b72d5b75SMichael Corcoran 		acpidev_status = ACPIDEV_STATUS_FAILED;
514b72d5b75SMichael Corcoran 		return;
515b72d5b75SMichael Corcoran 	}
516b72d5b75SMichael Corcoran 
517b72d5b75SMichael Corcoran 	/* Check ACPICA subsystem status. */
518b72d5b75SMichael Corcoran 	if (!acpica_get_core_feature(ACPI_FEATURE_FULL_INIT)) {
519b72d5b75SMichael Corcoran 		cmn_err(CE_WARN, "!acpidev: ACPICA hasn't been fully "
520b72d5b75SMichael Corcoran 		    "initialized, ACPI device autoconfig will be disabled.");
521b72d5b75SMichael Corcoran 		acpidev_status = ACPIDEV_STATUS_DISABLED;
522b72d5b75SMichael Corcoran 		return;
523b72d5b75SMichael Corcoran 	}
524b72d5b75SMichael Corcoran 
525b72d5b75SMichael Corcoran 	/* Converts acpidev-options from type string to int, if any */
526b72d5b75SMichael Corcoran 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
527b72d5b75SMichael Corcoran 	    DDI_PROP_DONTPASS, "acpidev-options", &str) == DDI_PROP_SUCCESS) {
528b72d5b75SMichael Corcoran 		long data;
529b72d5b75SMichael Corcoran 		rc = ddi_strtol(str, NULL, 0, &data);
530b72d5b75SMichael Corcoran 		if (rc == 0) {
531b72d5b75SMichael Corcoran 			(void) e_ddi_prop_remove(DDI_DEV_T_NONE,
532b72d5b75SMichael Corcoran 			    ddi_root_node(), "acpidev-options");
533b72d5b75SMichael Corcoran 			(void) e_ddi_prop_update_int(DDI_DEV_T_NONE,
534b72d5b75SMichael Corcoran 			    ddi_root_node(), "acpidev-options", data);
535b72d5b75SMichael Corcoran 		}
536b72d5b75SMichael Corcoran 		ddi_prop_free(str);
537b72d5b75SMichael Corcoran 	}
538b72d5b75SMichael Corcoran 	/* Get acpidev_options user options. */
539b72d5b75SMichael Corcoran 	acpidev_options = ddi_prop_get_int(DDI_DEV_T_ANY, ddi_root_node(),
540b72d5b75SMichael Corcoran 	    DDI_PROP_DONTPASS, "acpidev-options", acpidev_options);
541b72d5b75SMichael Corcoran 
542a3114836SGerry Liu 	/* Check whether ACPI based DR has been disabled by user. */
543a3114836SGerry Liu 	rc = ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
544a3114836SGerry Liu 	    DDI_PROP_DONTPASS, "acpidev-dr", &str);
545a3114836SGerry Liu 	if (rc == DDI_SUCCESS) {
546a3114836SGerry Liu 		if (strcasecmp(str, "off") == 0 || strcasecmp(str, "no") == 0) {
547a3114836SGerry Liu 			cmn_err(CE_CONT, "?acpidev: ACPI based DR has been "
548a3114836SGerry Liu 			    "disabled by user.\n");
549a3114836SGerry Liu 			acpidev_dr_enable = 0;
550a3114836SGerry Liu 		}
551a3114836SGerry Liu 		ddi_prop_free(str);
552a3114836SGerry Liu 	}
553a3114836SGerry Liu 
554b72d5b75SMichael Corcoran 	/* Register all device class drivers. */
555b72d5b75SMichael Corcoran 	if (ACPI_FAILURE(acpidev_class_list_init(&features))) {
556b72d5b75SMichael Corcoran 		cmn_err(CE_WARN,
557b72d5b75SMichael Corcoran 		    "!acpidev: failed to initalize class driver lists.");
558b72d5b75SMichael Corcoran 		acpidev_status = ACPIDEV_STATUS_FAILED;
559b72d5b75SMichael Corcoran 		return;
560b72d5b75SMichael Corcoran 	}
561b72d5b75SMichael Corcoran 
562b72d5b75SMichael Corcoran 	/* Create root node for ACPI/firmware device subtree. */
563b72d5b75SMichael Corcoran 	if (ACPI_FAILURE(acpidev_create_root_node())) {
564b72d5b75SMichael Corcoran 		cmn_err(CE_WARN, "!acpidev: failed to create root node "
565b72d5b75SMichael Corcoran 		    "for acpi device tree.");
566b72d5b75SMichael Corcoran 		acpidev_class_list_fini();
567b72d5b75SMichael Corcoran 		acpidev_status = ACPIDEV_STATUS_FAILED;
568b72d5b75SMichael Corcoran 		return;
569b72d5b75SMichael Corcoran 	}
570b72d5b75SMichael Corcoran 
571b72d5b75SMichael Corcoran 	/* Notify acpica to enable ACPI device auto configuration. */
572b72d5b75SMichael Corcoran 	acpica_set_core_feature(ACPI_FEATURE_DEVCFG);
573b72d5b75SMichael Corcoran 	acpica_set_devcfg_feature(features);
574b72d5b75SMichael Corcoran 
575b72d5b75SMichael Corcoran 	ACPIDEV_DEBUG(CE_NOTE, "!acpidev: ACPI device autoconfig initialized.");
576b72d5b75SMichael Corcoran 	acpidev_status = ACPIDEV_STATUS_INITIALIZED;
577b72d5b75SMichael Corcoran }
578b72d5b75SMichael Corcoran 
579b72d5b75SMichael Corcoran /*
580b72d5b75SMichael Corcoran  * Probe devices in ACPI namespace which can't be enumerated by other methods
581b72d5b75SMichael Corcoran  * at boot time.
582b72d5b75SMichael Corcoran  */
583b72d5b75SMichael Corcoran static ACPI_STATUS
acpidev_boot_probe_device(acpidev_op_type_t op_type)584b72d5b75SMichael Corcoran acpidev_boot_probe_device(acpidev_op_type_t op_type)
585b72d5b75SMichael Corcoran {
586b72d5b75SMichael Corcoran 	ACPI_STATUS rc = AE_OK;
587b72d5b75SMichael Corcoran 	acpidev_walk_info_t *infop;
588b72d5b75SMichael Corcoran 
589b72d5b75SMichael Corcoran 	ASSERT(acpidev_root_dip != NULL);
590b72d5b75SMichael Corcoran 	ASSERT(op_type == ACPIDEV_OP_BOOT_PROBE ||
591b72d5b75SMichael Corcoran 	    op_type == ACPIDEV_OP_BOOT_REPROBE);
592b72d5b75SMichael Corcoran 
593b72d5b75SMichael Corcoran 	infop = acpidev_alloc_walk_info(op_type, 0, ACPI_ROOT_OBJECT,
594b72d5b75SMichael Corcoran 	    &acpidev_class_list_root, NULL);
595b72d5b75SMichael Corcoran 	if (infop == NULL) {
596a3114836SGerry Liu 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to allocate walk info "
597b72d5b75SMichael Corcoran 		    "object in acpi_boot_probe_device().");
598b72d5b75SMichael Corcoran 		return (AE_ERROR);
599b72d5b75SMichael Corcoran 	}
600b72d5b75SMichael Corcoran 	/* Enumerate ACPI devices. */
601b72d5b75SMichael Corcoran 	rc = acpidev_probe_child(infop);
602b72d5b75SMichael Corcoran 	if (ACPI_FAILURE(rc)) {
603b72d5b75SMichael Corcoran 		cmn_err(CE_WARN, "!acpidev: failed to probe child object "
604b72d5b75SMichael Corcoran 		    "under ACPI root node.");
605b72d5b75SMichael Corcoran 	}
606b72d5b75SMichael Corcoran 	acpidev_free_walk_info(infop);
607b72d5b75SMichael Corcoran 
608b72d5b75SMichael Corcoran 	return (rc);
609b72d5b75SMichael Corcoran }
610b72d5b75SMichael Corcoran 
611b72d5b75SMichael Corcoran /*
612b72d5b75SMichael Corcoran  * Platform specific device prober for ACPI virtual bus.
613b72d5b75SMichael Corcoran  * It will be called in single-threaded environment to enumerate devices in
614b72d5b75SMichael Corcoran  * ACPI namespace at boot time.
615b72d5b75SMichael Corcoran  */
616b72d5b75SMichael Corcoran static void
acpidev_boot_probe(int type)617b72d5b75SMichael Corcoran acpidev_boot_probe(int type)
618b72d5b75SMichael Corcoran {
619b72d5b75SMichael Corcoran 	ACPI_STATUS rc;
620b72d5b75SMichael Corcoran 
621b72d5b75SMichael Corcoran 	/* Initialize subsystem on first pass. */
622b72d5b75SMichael Corcoran 	mutex_enter(&acpidev_drv_lock);
623b72d5b75SMichael Corcoran 	if (type == 0) {
624b72d5b75SMichael Corcoran 		acpidev_initialize();
625b72d5b75SMichael Corcoran 		if (acpidev_status != ACPIDEV_STATUS_INITIALIZED &&
626b72d5b75SMichael Corcoran 		    acpidev_status != ACPIDEV_STATUS_DISABLED) {
627b72d5b75SMichael Corcoran 			cmn_err(CE_WARN, "!acpidev: driver disabled due to "
628b72d5b75SMichael Corcoran 			    "initalization failure.");
629b72d5b75SMichael Corcoran 		}
630b72d5b75SMichael Corcoran 	}
631b72d5b75SMichael Corcoran 
632b72d5b75SMichael Corcoran 	/* Probe ACPI devices */
633b72d5b75SMichael Corcoran 	if (type == 0 && acpidev_status == ACPIDEV_STATUS_INITIALIZED) {
634b72d5b75SMichael Corcoran 		rc = acpidev_boot_probe_device(ACPIDEV_OP_BOOT_PROBE);
635b72d5b75SMichael Corcoran 		if (ACPI_SUCCESS(rc)) {
636a3114836SGerry Liu 			/*
637a3114836SGerry Liu 			 * Support of DR operations will be disabled
638a3114836SGerry Liu 			 * if failed to initialize DR subsystem.
639a3114836SGerry Liu 			 */
640a3114836SGerry Liu 			rc = acpidev_dr_initialize(acpidev_root_dip);
641a3114836SGerry Liu 			if (ACPI_FAILURE(rc) && rc != AE_SUPPORT) {
642a3114836SGerry Liu 				cmn_err(CE_CONT, "?acpidev: failed to "
643a3114836SGerry Liu 				    "initialize DR subsystem.");
644a3114836SGerry Liu 			}
645b72d5b75SMichael Corcoran 			acpidev_status = ACPIDEV_STATUS_FIRST_PASS;
646b72d5b75SMichael Corcoran 		} else {
647a3114836SGerry Liu 			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to probe ACPI "
648b72d5b75SMichael Corcoran 			    "devices during boot.");
649b72d5b75SMichael Corcoran 			acpidev_status = ACPIDEV_STATUS_FAILED;
650b72d5b75SMichael Corcoran 		}
651b72d5b75SMichael Corcoran 	} else if (type != 0 && acpidev_status == ACPIDEV_STATUS_FIRST_PASS) {
652b72d5b75SMichael Corcoran 		rc = acpidev_boot_probe_device(ACPIDEV_OP_BOOT_REPROBE);
653b72d5b75SMichael Corcoran 		if (ACPI_SUCCESS(rc)) {
654b72d5b75SMichael Corcoran 			acpidev_status = ACPIDEV_STATUS_READY;
655b72d5b75SMichael Corcoran 		} else {
656a3114836SGerry Liu 			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to reprobe "
657b72d5b75SMichael Corcoran 			    "ACPI devices during boot.");
658b72d5b75SMichael Corcoran 			acpidev_status = ACPIDEV_STATUS_FAILED;
659b72d5b75SMichael Corcoran 		}
660b72d5b75SMichael Corcoran 	} else if (acpidev_status != ACPIDEV_STATUS_FAILED &&
661b72d5b75SMichael Corcoran 	    acpidev_status != ACPIDEV_STATUS_DISABLED &&
662b72d5b75SMichael Corcoran 	    acpidev_status != ACPIDEV_STATUS_READY) {
663b72d5b75SMichael Corcoran 		cmn_err(CE_WARN,
664b72d5b75SMichael Corcoran 		    "!acpidev: invalid ACPI device autoconfig global status.");
665b72d5b75SMichael Corcoran 	}
666b72d5b75SMichael Corcoran 	mutex_exit(&acpidev_drv_lock);
667b72d5b75SMichael Corcoran }
668b72d5b75SMichael Corcoran 
669b72d5b75SMichael Corcoran ACPI_STATUS
acpidev_probe_child(acpidev_walk_info_t * infop)670b72d5b75SMichael Corcoran acpidev_probe_child(acpidev_walk_info_t *infop)
671b72d5b75SMichael Corcoran {
672b72d5b75SMichael Corcoran 	dev_info_t *pdip;
673b72d5b75SMichael Corcoran 	ACPI_STATUS res, rc = AE_OK;
674b72d5b75SMichael Corcoran 	ACPI_HANDLE child;
675b72d5b75SMichael Corcoran 	ACPI_OBJECT_TYPE type;
676b72d5b75SMichael Corcoran 	acpidev_class_list_t *it;
677b72d5b75SMichael Corcoran 	acpidev_walk_info_t *cinfop;
678b72d5b75SMichael Corcoran 	acpidev_data_handle_t datap;
679b72d5b75SMichael Corcoran 
680b72d5b75SMichael Corcoran 	/* Validate parameter first. */
681b72d5b75SMichael Corcoran 	ASSERT(infop != NULL);
682b72d5b75SMichael Corcoran 	if (infop == NULL) {
683b72d5b75SMichael Corcoran 		ACPIDEV_DEBUG(CE_WARN,
684a3114836SGerry Liu 		    "!acpidev: infop is NULL in acpidev_probe_child().");
685b72d5b75SMichael Corcoran 		return (AE_BAD_PARAMETER);
686b72d5b75SMichael Corcoran 	}
687b72d5b75SMichael Corcoran 	ASSERT(infop->awi_level < ACPIDEV_MAX_ENUM_LEVELS - 1);
688b72d5b75SMichael Corcoran 	if (infop->awi_level >= ACPIDEV_MAX_ENUM_LEVELS - 1) {
689a3114836SGerry Liu 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: recursive level is too deep "
690b72d5b75SMichael Corcoran 		    "in acpidev_probe_child().");
691b72d5b75SMichael Corcoran 		return (AE_BAD_PARAMETER);
692b72d5b75SMichael Corcoran 	}
693b72d5b75SMichael Corcoran 	ASSERT(infop->awi_class_list != NULL);
694b72d5b75SMichael Corcoran 	ASSERT(infop->awi_hdl != NULL);
695b72d5b75SMichael Corcoran 	ASSERT(infop->awi_info != NULL);
696b72d5b75SMichael Corcoran 	ASSERT(infop->awi_name != NULL);
697b72d5b75SMichael Corcoran 	ASSERT(infop->awi_data != NULL);
698b72d5b75SMichael Corcoran 	if (infop->awi_class_list == NULL || infop->awi_hdl == NULL ||
699b72d5b75SMichael Corcoran 	    infop->awi_info == NULL || infop->awi_name == NULL ||
700b72d5b75SMichael Corcoran 	    infop->awi_data == NULL) {
701a3114836SGerry Liu 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: infop has NULL fields in "
702a3114836SGerry Liu 		    "acpidev_probe_child().");
703b72d5b75SMichael Corcoran 		return (AE_BAD_PARAMETER);
704b72d5b75SMichael Corcoran 	}
705b72d5b75SMichael Corcoran 	pdip = acpidev_walk_info_get_pdip(infop);
706b72d5b75SMichael Corcoran 	if (pdip == NULL) {
707b72d5b75SMichael Corcoran 		ACPIDEV_DEBUG(CE_WARN,
708a3114836SGerry Liu 		    "!acpidev: pdip is NULL in acpidev_probe_child().");
709b72d5b75SMichael Corcoran 		return (AE_BAD_PARAMETER);
710b72d5b75SMichael Corcoran 	}
711b72d5b75SMichael Corcoran 
712*3fe80ca4SDan Cross 	ndi_devi_enter(pdip);
713b72d5b75SMichael Corcoran 	rw_enter(&acpidev_class_lock, RW_READER);
714b72d5b75SMichael Corcoran 
715b72d5b75SMichael Corcoran 	/* Call pre-probe callback functions. */
716b72d5b75SMichael Corcoran 	for (it = *(infop->awi_class_list); it != NULL; it = it->acl_next) {
717b72d5b75SMichael Corcoran 		if (it->acl_class->adc_pre_probe == NULL) {
718b72d5b75SMichael Corcoran 			continue;
719b72d5b75SMichael Corcoran 		}
720b72d5b75SMichael Corcoran 		infop->awi_class_curr = it->acl_class;
721b72d5b75SMichael Corcoran 		if (ACPI_FAILURE(it->acl_class->adc_pre_probe(infop))) {
722a3114836SGerry Liu 			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to pre-probe "
723b72d5b75SMichael Corcoran 			    "device of type %s under %s.",
724b72d5b75SMichael Corcoran 			    it->acl_class->adc_class_name, infop->awi_name);
725b72d5b75SMichael Corcoran 		}
726b72d5b75SMichael Corcoran 	}
727b72d5b75SMichael Corcoran 
728b72d5b75SMichael Corcoran 	/* Walk child objects. */
729b72d5b75SMichael Corcoran 	child = NULL;
730b72d5b75SMichael Corcoran 	while (ACPI_SUCCESS(AcpiGetNextObject(ACPI_TYPE_ANY,
731b72d5b75SMichael Corcoran 	    infop->awi_hdl, child, &child))) {
732b72d5b75SMichael Corcoran 		/* Skip object if we're not interested in it. */
733b72d5b75SMichael Corcoran 		if (ACPI_FAILURE(AcpiGetType(child, &type)) ||
734b72d5b75SMichael Corcoran 		    type > ACPI_TYPE_NS_NODE_MAX ||
735b72d5b75SMichael Corcoran 		    BT_TEST(acpidev_object_type_mask, type) == 0) {
736b72d5b75SMichael Corcoran 			continue;
737b72d5b75SMichael Corcoran 		}
738b72d5b75SMichael Corcoran 
739a3114836SGerry Liu 		/* It's another hotplug-capable board, skip it. */
740a3114836SGerry Liu 		if (infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE &&
741a3114836SGerry Liu 		    acpidev_dr_device_is_board(child)) {
742a3114836SGerry Liu 			continue;
743a3114836SGerry Liu 		}
744a3114836SGerry Liu 
745b72d5b75SMichael Corcoran 		/* Allocate the walk info structure. */
746b72d5b75SMichael Corcoran 		cinfop = acpidev_alloc_walk_info(infop->awi_op_type,
747b72d5b75SMichael Corcoran 		    infop->awi_level + 1, child, NULL, infop);
748b72d5b75SMichael Corcoran 		if (cinfop == NULL) {
749a3114836SGerry Liu 			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to allocate "
750b72d5b75SMichael Corcoran 			    "walk info child object of %s.",
751b72d5b75SMichael Corcoran 			    infop->awi_name);
752b72d5b75SMichael Corcoran 			/* Mark error and continue to handle next child. */
753b72d5b75SMichael Corcoran 			rc = AE_ERROR;
754b72d5b75SMichael Corcoran 			continue;
755b72d5b75SMichael Corcoran 		}
756b72d5b75SMichael Corcoran 
757b72d5b75SMichael Corcoran 		/*
758b72d5b75SMichael Corcoran 		 * Remember the class list used to handle this object.
759b72d5b75SMichael Corcoran 		 * It should be the same list for different passes of scans.
760b72d5b75SMichael Corcoran 		 */
761b72d5b75SMichael Corcoran 		ASSERT(cinfop->awi_data != NULL);
762b72d5b75SMichael Corcoran 		datap = cinfop->awi_data;
763b72d5b75SMichael Corcoran 		if (cinfop->awi_op_type == ACPIDEV_OP_BOOT_PROBE) {
764b72d5b75SMichael Corcoran 			datap->aod_class_list = infop->awi_class_list;
765b72d5b75SMichael Corcoran 		}
766b72d5b75SMichael Corcoran 
767b72d5b75SMichael Corcoran 		/* Call registered process callbacks. */
768b72d5b75SMichael Corcoran 		for (it = *(infop->awi_class_list); it != NULL;
769b72d5b75SMichael Corcoran 		    it = it->acl_next) {
770b72d5b75SMichael Corcoran 			if (it->acl_class->adc_probe == NULL) {
771b72d5b75SMichael Corcoran 				continue;
772b72d5b75SMichael Corcoran 			}
773b72d5b75SMichael Corcoran 			cinfop->awi_class_curr = it->acl_class;
774b72d5b75SMichael Corcoran 			res = it->acl_class->adc_probe(cinfop);
775b72d5b75SMichael Corcoran 			if (ACPI_FAILURE(res)) {
776b72d5b75SMichael Corcoran 				rc = res;
777a3114836SGerry Liu 				ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to "
778b72d5b75SMichael Corcoran 				    "process object of type %s under %s.",
779b72d5b75SMichael Corcoran 				    it->acl_class->adc_class_name,
780b72d5b75SMichael Corcoran 				    infop->awi_name);
781b72d5b75SMichael Corcoran 			}
782b72d5b75SMichael Corcoran 		}
783b72d5b75SMichael Corcoran 
784b72d5b75SMichael Corcoran 		/* Free resources. */
785b72d5b75SMichael Corcoran 		acpidev_free_walk_info(cinfop);
786b72d5b75SMichael Corcoran 	}
787b72d5b75SMichael Corcoran 
788b72d5b75SMichael Corcoran 	/* Call post-probe callback functions. */
789b72d5b75SMichael Corcoran 	for (it = *(infop->awi_class_list); it != NULL; it = it->acl_next) {
790b72d5b75SMichael Corcoran 		if (it->acl_class->adc_post_probe == NULL) {
791b72d5b75SMichael Corcoran 			continue;
792b72d5b75SMichael Corcoran 		}
793b72d5b75SMichael Corcoran 		infop->awi_class_curr = it->acl_class;
794b72d5b75SMichael Corcoran 		if (ACPI_FAILURE(it->acl_class->adc_post_probe(infop))) {
795a3114836SGerry Liu 			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to post-probe "
796b72d5b75SMichael Corcoran 			    "device of type %s under %s.",
797b72d5b75SMichael Corcoran 			    it->acl_class->adc_class_name, infop->awi_name);
798b72d5b75SMichael Corcoran 		}
799b72d5b75SMichael Corcoran 	}
800b72d5b75SMichael Corcoran 
801b72d5b75SMichael Corcoran 	rw_exit(&acpidev_class_lock);
802*3fe80ca4SDan Cross 	ndi_devi_exit(pdip);
803b72d5b75SMichael Corcoran 
804b72d5b75SMichael Corcoran 	return (rc);
805b72d5b75SMichael Corcoran }
806b72d5b75SMichael Corcoran 
807b72d5b75SMichael Corcoran ACPI_STATUS
acpidev_process_object(acpidev_walk_info_t * infop,int flags)808b72d5b75SMichael Corcoran acpidev_process_object(acpidev_walk_info_t *infop, int flags)
809b72d5b75SMichael Corcoran {
810b72d5b75SMichael Corcoran 	ACPI_STATUS rc = AE_OK;
811b72d5b75SMichael Corcoran 	char *devname;
812b72d5b75SMichael Corcoran 	dev_info_t *dip, *pdip;
813b72d5b75SMichael Corcoran 	ACPI_HANDLE hdl;
814b72d5b75SMichael Corcoran 	ACPI_DEVICE_INFO *adip;
815b72d5b75SMichael Corcoran 	acpidev_class_t *clsp;
816b72d5b75SMichael Corcoran 	acpidev_data_handle_t datap;
817b72d5b75SMichael Corcoran 	acpidev_filter_result_t res;
818b72d5b75SMichael Corcoran 
819b72d5b75SMichael Corcoran 	/* Validate parameters first. */
820b72d5b75SMichael Corcoran 	ASSERT(infop != NULL);
821b72d5b75SMichael Corcoran 	if (infop == NULL) {
822b72d5b75SMichael Corcoran 		ACPIDEV_DEBUG(CE_WARN,
823a3114836SGerry Liu 		    "!acpidev: infop is NULL in acpidev_process_object().");
824b72d5b75SMichael Corcoran 		return (AE_BAD_PARAMETER);
825b72d5b75SMichael Corcoran 	}
826b72d5b75SMichael Corcoran 	ASSERT(infop->awi_hdl != NULL);
827b72d5b75SMichael Corcoran 	ASSERT(infop->awi_info != NULL);
828b72d5b75SMichael Corcoran 	ASSERT(infop->awi_data != NULL);
829b72d5b75SMichael Corcoran 	ASSERT(infop->awi_class_curr != NULL);
830b72d5b75SMichael Corcoran 	ASSERT(infop->awi_class_curr->adc_filter != NULL);
831b72d5b75SMichael Corcoran 	hdl = infop->awi_hdl;
832b72d5b75SMichael Corcoran 	adip = infop->awi_info;
833b72d5b75SMichael Corcoran 	datap = infop->awi_data;
834b72d5b75SMichael Corcoran 	clsp = infop->awi_class_curr;
835b72d5b75SMichael Corcoran 	if (hdl == NULL || datap == NULL || adip == NULL || clsp == NULL ||
836b72d5b75SMichael Corcoran 	    clsp->adc_filter == NULL) {
837a3114836SGerry Liu 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: infop has NULL pointer in "
838b72d5b75SMichael Corcoran 		    "acpidev_process_object().");
839b72d5b75SMichael Corcoran 		return (AE_BAD_PARAMETER);
840b72d5b75SMichael Corcoran 	}
841b72d5b75SMichael Corcoran 	pdip = acpidev_walk_info_get_pdip(infop);
842b72d5b75SMichael Corcoran 	if (pdip == NULL) {
843a3114836SGerry Liu 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to get pdip for %s "
844b72d5b75SMichael Corcoran 		    "in acpidev_process_object().", infop->awi_name);
845b72d5b75SMichael Corcoran 		return (AE_BAD_PARAMETER);
846b72d5b75SMichael Corcoran 	}
847b72d5b75SMichael Corcoran 
848b72d5b75SMichael Corcoran 	/*
849b72d5b75SMichael Corcoran 	 * Check whether the object has already been handled.
850b72d5b75SMichael Corcoran 	 * Tag and child dip pointer are used to indicate the object has been
851b72d5b75SMichael Corcoran 	 * handled by the ACPI auto configure driver. It has the
852b72d5b75SMichael Corcoran 	 * following usages:
853b72d5b75SMichael Corcoran 	 * 1) Prevent creating dip for objects which already have a dip
854b72d5b75SMichael Corcoran 	 *    when reloading the ACPI auto configure driver.
855b72d5b75SMichael Corcoran 	 * 2) Prevent creating multiple dips for ACPI objects with ACPI
856b72d5b75SMichael Corcoran 	 *    aliases. Currently ACPICA framework has no way to tell whether
857b72d5b75SMichael Corcoran 	 *    an object is an alias or not for some types of object. So tag
858b72d5b75SMichael Corcoran 	 *    is used to indicate that the object has been handled.
859b72d5b75SMichael Corcoran 	 * 3) Prevent multiple class drivers from creating multiple devices for
860b72d5b75SMichael Corcoran 	 *    the same ACPI object.
861b72d5b75SMichael Corcoran 	 */
862b72d5b75SMichael Corcoran 	if ((flags & ACPIDEV_PROCESS_FLAG_CREATE) &&
863b72d5b75SMichael Corcoran 	    (flags & ACPIDEV_PROCESS_FLAG_CHECK) &&
864b72d5b75SMichael Corcoran 	    !(infop->awi_flags & ACPIDEV_WI_DISABLE_CREATE) &&
865b72d5b75SMichael Corcoran 	    (infop->awi_flags & ACPIDEV_WI_DEVICE_CREATED)) {
866b72d5b75SMichael Corcoran 		ASSERT(infop->awi_dip != NULL);
867b72d5b75SMichael Corcoran 		ACPIDEV_DEBUG(CE_NOTE,
868a3114836SGerry Liu 		    "!acpidev: device has already been created for object %s.",
869b72d5b75SMichael Corcoran 		    infop->awi_name);
870b72d5b75SMichael Corcoran 		return (AE_ALREADY_EXISTS);
871b72d5b75SMichael Corcoran 	}
872b72d5b75SMichael Corcoran 
873b72d5b75SMichael Corcoran 	/*
874b72d5b75SMichael Corcoran 	 * Determine action according to following rules based on device
875b72d5b75SMichael Corcoran 	 * status returned by _STA method. Please refer to ACPI3.0b section
876b72d5b75SMichael Corcoran 	 * 6.3.1 and 6.5.1.
877b72d5b75SMichael Corcoran 	 * present functioning enabled	Action
878b72d5b75SMichael Corcoran 	 *	0	0	x	Do nothing
879a3114836SGerry Liu 	 *	1	x	0	Do nothing
880b72d5b75SMichael Corcoran 	 *	1	x	1	Create node and scan child
881a3114836SGerry Liu 	 *	x	1	0	Do nothing
882b72d5b75SMichael Corcoran 	 *	x	1	1	Create node and scan child
883b72d5b75SMichael Corcoran 	 */
884b72d5b75SMichael Corcoran 	if ((datap->aod_iflag & ACPIDEV_ODF_STATUS_VALID) == 0 ||
885b72d5b75SMichael Corcoran 	    (flags & ACPIDEV_PROCESS_FLAG_SYNCSTATUS)) {
88635786f68SRobert Mustacchi 		datap->aod_status = acpidev_query_device_status(hdl);
887b72d5b75SMichael Corcoran 		datap->aod_iflag |= ACPIDEV_ODF_STATUS_VALID;
888b72d5b75SMichael Corcoran 	}
889a3114836SGerry Liu 	if (!acpidev_check_device_enabled(datap->aod_status)) {
890a3114836SGerry Liu 		ACPIDEV_DEBUG(CE_NOTE, "!acpidev: object %s doesn't exist.",
891b72d5b75SMichael Corcoran 		    infop->awi_name);
892a3114836SGerry Liu 		/*
893a3114836SGerry Liu 		 * Need to scan for hotplug-capable boards even if object
894a3114836SGerry Liu 		 * doesn't exist or has been disabled during the first pass.
895a3114836SGerry Liu 		 * So just disable creating device node and keep on scanning.
896a3114836SGerry Liu 		 */
897a3114836SGerry Liu 		if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE) {
898a3114836SGerry Liu 			flags &= ~ACPIDEV_PROCESS_FLAG_CREATE;
899a3114836SGerry Liu 		} else {
900a3114836SGerry Liu 			return (AE_NOT_EXIST);
901a3114836SGerry Liu 		}
902b72d5b75SMichael Corcoran 	}
903b72d5b75SMichael Corcoran 
904b72d5b75SMichael Corcoran 	ASSERT(infop->awi_data != NULL);
905b72d5b75SMichael Corcoran 	ASSERT(infop->awi_parent != NULL);
906b72d5b75SMichael Corcoran 	ASSERT(infop->awi_parent->awi_data != NULL);
907a3114836SGerry Liu 	if (flags & ACPIDEV_PROCESS_FLAG_CREATE) {
908a3114836SGerry Liu 		mutex_enter(&(DEVI(pdip)->devi_lock));
909a3114836SGerry Liu 		/*
910a3114836SGerry Liu 		 * Put the device into offline state if its parent is in
911a3114836SGerry Liu 		 * offline state.
912a3114836SGerry Liu 		 */
913a3114836SGerry Liu 		if (DEVI_IS_DEVICE_OFFLINE(pdip)) {
914a3114836SGerry Liu 			flags |= ACPIDEV_PROCESS_FLAG_OFFLINE;
915a3114836SGerry Liu 		}
916a3114836SGerry Liu 		mutex_exit(&(DEVI(pdip)->devi_lock));
917b72d5b75SMichael Corcoran 	}
918b72d5b75SMichael Corcoran 
919b72d5b75SMichael Corcoran 	/* Evaluate filtering rules and generate device name. */
920b72d5b75SMichael Corcoran 	devname = kmem_zalloc(ACPIDEV_MAX_NAMELEN + 1, KM_SLEEP);
921b72d5b75SMichael Corcoran 	(void) memcpy(devname, (char *)&adip->Name, sizeof (adip->Name));
922b72d5b75SMichael Corcoran 	if (flags & ACPIDEV_PROCESS_FLAG_CREATE) {
923b72d5b75SMichael Corcoran 		res = clsp->adc_filter(infop, devname, ACPIDEV_MAX_NAMELEN);
924b72d5b75SMichael Corcoran 	} else {
925b72d5b75SMichael Corcoran 		res = clsp->adc_filter(infop, NULL, 0);
926b72d5b75SMichael Corcoran 	}
927b72d5b75SMichael Corcoran 
928b72d5b75SMichael Corcoran 	/* Create device if requested. */
929b72d5b75SMichael Corcoran 	if ((flags & ACPIDEV_PROCESS_FLAG_CREATE) &&
930b72d5b75SMichael Corcoran 	    !(infop->awi_flags & ACPIDEV_WI_DISABLE_CREATE) &&
931b72d5b75SMichael Corcoran 	    !(infop->awi_flags & ACPIDEV_WI_DEVICE_CREATED) &&
932b72d5b75SMichael Corcoran 	    (res == ACPIDEV_FILTER_DEFAULT || res == ACPIDEV_FILTER_CREATE)) {
933b72d5b75SMichael Corcoran 		int ret;
934b72d5b75SMichael Corcoran 
935b72d5b75SMichael Corcoran 		/*
936b72d5b75SMichael Corcoran 		 * Allocate dip and set default properties.
937b72d5b75SMichael Corcoran 		 * Properties can be overriden in class specific init routines.
938b72d5b75SMichael Corcoran 		 */
939b72d5b75SMichael Corcoran 		ASSERT(infop->awi_dip == NULL);
940b72d5b75SMichael Corcoran 		ndi_devi_alloc_sleep(pdip, devname, (pnode_t)DEVI_SID_NODEID,
941b72d5b75SMichael Corcoran 		    &dip);
942b72d5b75SMichael Corcoran 		infop->awi_dip = dip;
943b72d5b75SMichael Corcoran 		ret = ndi_prop_update_string(DDI_DEV_T_NONE, dip,
944b72d5b75SMichael Corcoran 		    OBP_DEVICETYPE, clsp->adc_dev_type);
945b72d5b75SMichael Corcoran 		if (ret != NDI_SUCCESS) {
946b72d5b75SMichael Corcoran 			ACPIDEV_DEBUG(CE_WARN,
947a3114836SGerry Liu 			    "!acpidev: failed to set device property for %s.",
948b72d5b75SMichael Corcoran 			    infop->awi_name);
949b72d5b75SMichael Corcoran 			(void) ddi_remove_child(dip, 0);
950b72d5b75SMichael Corcoran 			infop->awi_dip = NULL;
951b72d5b75SMichael Corcoran 			kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1);
952b72d5b75SMichael Corcoran 			return (AE_ERROR);
953b72d5b75SMichael Corcoran 		}
954b72d5b75SMichael Corcoran 
955b72d5b75SMichael Corcoran 		/* Build cross reference between dip and ACPI object. */
956b72d5b75SMichael Corcoran 		if ((flags & ACPIDEV_PROCESS_FLAG_NOTAG) == 0 &&
957b72d5b75SMichael Corcoran 		    ACPI_FAILURE(acpica_tag_devinfo(dip, hdl))) {
958b72d5b75SMichael Corcoran 			cmn_err(CE_WARN,
959b72d5b75SMichael Corcoran 			    "!acpidev: failed to tag object %s.",
960b72d5b75SMichael Corcoran 			    infop->awi_name);
961b72d5b75SMichael Corcoran 			(void) ddi_remove_child(dip, 0);
962b72d5b75SMichael Corcoran 			infop->awi_dip = NULL;
963b72d5b75SMichael Corcoran 			kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1);
964b72d5b75SMichael Corcoran 			return (AE_ERROR);
965b72d5b75SMichael Corcoran 		}
966b72d5b75SMichael Corcoran 
967b72d5b75SMichael Corcoran 		/* Call class specific initialization callback. */
968b72d5b75SMichael Corcoran 		if (clsp->adc_init != NULL &&
969b72d5b75SMichael Corcoran 		    ACPI_FAILURE(clsp->adc_init(infop))) {
970b72d5b75SMichael Corcoran 			ACPIDEV_DEBUG(CE_WARN,
971a3114836SGerry Liu 			    "!acpidev: failed to initialize device %s.",
972b72d5b75SMichael Corcoran 			    infop->awi_name);
973b72d5b75SMichael Corcoran 			if ((flags & ACPIDEV_PROCESS_FLAG_NOTAG) == 0) {
974b72d5b75SMichael Corcoran 				(void) acpica_untag_devinfo(dip, hdl);
975b72d5b75SMichael Corcoran 			}
976b72d5b75SMichael Corcoran 			(void) ddi_remove_child(dip, 0);
977b72d5b75SMichael Corcoran 			infop->awi_dip = NULL;
978b72d5b75SMichael Corcoran 			kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1);
979b72d5b75SMichael Corcoran 			return (AE_ERROR);
980b72d5b75SMichael Corcoran 		}
981b72d5b75SMichael Corcoran 
982b72d5b75SMichael Corcoran 		/* Set device into offline state if requested. */
983b72d5b75SMichael Corcoran 		if (flags & ACPIDEV_PROCESS_FLAG_OFFLINE) {
984b72d5b75SMichael Corcoran 			mutex_enter(&(DEVI(dip)->devi_lock));
985b72d5b75SMichael Corcoran 			DEVI_SET_DEVICE_OFFLINE(dip);
986b72d5b75SMichael Corcoran 			mutex_exit(&(DEVI(dip)->devi_lock));
987b72d5b75SMichael Corcoran 		}
988b72d5b75SMichael Corcoran 
989b72d5b75SMichael Corcoran 		/* Mark status */
990b72d5b75SMichael Corcoran 		infop->awi_flags |= ACPIDEV_WI_DEVICE_CREATED;
991b72d5b75SMichael Corcoran 		datap->aod_iflag |= ACPIDEV_ODF_DEVINFO_CREATED;
992b72d5b75SMichael Corcoran 		datap->aod_dip = dip;
993b72d5b75SMichael Corcoran 		datap->aod_class = clsp;
994b72d5b75SMichael Corcoran 		/* Hold reference count on class driver. */
995b72d5b75SMichael Corcoran 		atomic_inc_32(&clsp->adc_refcnt);
996b72d5b75SMichael Corcoran 		if ((flags & ACPIDEV_PROCESS_FLAG_NOTAG) == 0) {
997b72d5b75SMichael Corcoran 			datap->aod_iflag |= ACPIDEV_ODF_DEVINFO_TAGGED;
998b72d5b75SMichael Corcoran 		}
999b72d5b75SMichael Corcoran 
1000b72d5b75SMichael Corcoran 		/* Bind device driver. */
1001b72d5b75SMichael Corcoran 		if ((flags & ACPIDEV_PROCESS_FLAG_NOBIND) != 0) {
1002b72d5b75SMichael Corcoran 			mutex_enter(&(DEVI(dip)->devi_lock));
1003b72d5b75SMichael Corcoran 			DEVI(dip)->devi_flags |= DEVI_NO_BIND;
1004b72d5b75SMichael Corcoran 			mutex_exit(&(DEVI(dip)->devi_lock));
1005b72d5b75SMichael Corcoran 		} else {
1006b72d5b75SMichael Corcoran 			(void) ndi_devi_bind_driver(dip, 0);
1007b72d5b75SMichael Corcoran 		}
1008a3114836SGerry Liu 
1009a3114836SGerry Liu 		/* Hold reference on branch when hot-adding devices. */
1010a3114836SGerry Liu 		if (flags & ACPIDEV_PROCESS_FLAG_HOLDBRANCH) {
1011a3114836SGerry Liu 			e_ddi_branch_hold(dip);
1012a3114836SGerry Liu 		}
1013b72d5b75SMichael Corcoran 	}
1014b72d5b75SMichael Corcoran 
1015b72d5b75SMichael Corcoran 	/* Free resources */
1016b72d5b75SMichael Corcoran 	kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1);
1017b72d5b75SMichael Corcoran 	rc = AE_OK;
1018b72d5b75SMichael Corcoran 
1019b72d5b75SMichael Corcoran 	/* Recursively scan child objects if requested. */
1020b72d5b75SMichael Corcoran 	switch (res) {
1021b72d5b75SMichael Corcoran 	case ACPIDEV_FILTER_DEFAULT:
1022b72d5b75SMichael Corcoran 		/* FALLTHROUGH */
1023b72d5b75SMichael Corcoran 	case ACPIDEV_FILTER_SCAN:
1024b72d5b75SMichael Corcoran 		/* Check if we need to scan child. */
1025b72d5b75SMichael Corcoran 		if ((flags & ACPIDEV_PROCESS_FLAG_SCAN) &&
1026b72d5b75SMichael Corcoran 		    !(infop->awi_flags & ACPIDEV_WI_DISABLE_SCAN) &&
1027b72d5b75SMichael Corcoran 		    !(infop->awi_flags & ACPIDEV_WI_CHILD_SCANNED)) {
1028b72d5b75SMichael Corcoran 			/* probe child object. */
1029b72d5b75SMichael Corcoran 			rc = acpidev_probe_child(infop);
1030b72d5b75SMichael Corcoran 			if (ACPI_FAILURE(rc)) {
1031b72d5b75SMichael Corcoran 				ACPIDEV_DEBUG(CE_WARN,
1032a3114836SGerry Liu 				    "!acpidev: failed to probe subtree of %s.",
1033b72d5b75SMichael Corcoran 				    infop->awi_name);
1034b72d5b75SMichael Corcoran 				rc = AE_ERROR;
1035b72d5b75SMichael Corcoran 			}
1036b72d5b75SMichael Corcoran 			/* Mark object as scanned. */
1037b72d5b75SMichael Corcoran 			infop->awi_flags |= ACPIDEV_WI_CHILD_SCANNED;
1038b72d5b75SMichael Corcoran 		}
1039b72d5b75SMichael Corcoran 		break;
1040b72d5b75SMichael Corcoran 
1041b72d5b75SMichael Corcoran 	case ACPIDEV_FILTER_CREATE:
1042b72d5b75SMichael Corcoran 		/* FALLTHROUGH */
1043b72d5b75SMichael Corcoran 	case ACPIDEV_FILTER_CONTINUE:
1044b72d5b75SMichael Corcoran 		/* FALLTHROUGH */
1045b72d5b75SMichael Corcoran 	case ACPIDEV_FILTER_SKIP:
1046b72d5b75SMichael Corcoran 		break;
1047b72d5b75SMichael Corcoran 
1048b72d5b75SMichael Corcoran 	case ACPIDEV_FILTER_FAILED:
1049b72d5b75SMichael Corcoran 		ACPIDEV_DEBUG(CE_WARN,
1050a3114836SGerry Liu 		    "!acpidev: failed to probe device for %s.",
1051b72d5b75SMichael Corcoran 		    infop->awi_name);
1052b72d5b75SMichael Corcoran 		rc = AE_ERROR;
1053b72d5b75SMichael Corcoran 		break;
1054b72d5b75SMichael Corcoran 
1055b72d5b75SMichael Corcoran 	default:
1056b72d5b75SMichael Corcoran 		cmn_err(CE_WARN,
1057b72d5b75SMichael Corcoran 		    "!acpidev: unknown filter result code %d.", res);
1058b72d5b75SMichael Corcoran 		rc = AE_ERROR;
1059b72d5b75SMichael Corcoran 		break;
1060b72d5b75SMichael Corcoran 	}
1061b72d5b75SMichael Corcoran 
1062b72d5b75SMichael Corcoran 	return (rc);
1063b72d5b75SMichael Corcoran }
1064b72d5b75SMichael Corcoran 
1065b72d5b75SMichael Corcoran acpidev_filter_result_t
acpidev_filter_default(acpidev_walk_info_t * infop,ACPI_HANDLE hdl,acpidev_filter_rule_t * afrp,char * devname,int len)1066b72d5b75SMichael Corcoran acpidev_filter_default(acpidev_walk_info_t *infop, ACPI_HANDLE hdl,
1067b72d5b75SMichael Corcoran     acpidev_filter_rule_t *afrp, char *devname, int len)
1068b72d5b75SMichael Corcoran {
1069a3114836SGerry Liu 	_NOTE(ARGUNUSED(hdl));
1070a3114836SGerry Liu 
1071b72d5b75SMichael Corcoran 	ASSERT(afrp != NULL);
1072b72d5b75SMichael Corcoran 	ASSERT(devname == NULL || len >= ACPIDEV_MAX_NAMELEN);
1073b72d5b75SMichael Corcoran 	if (infop->awi_level < afrp->adf_minlvl ||
1074b72d5b75SMichael Corcoran 	    infop->awi_level > afrp->adf_maxlvl) {
1075b72d5b75SMichael Corcoran 		return (ACPIDEV_FILTER_CONTINUE);
1076b72d5b75SMichael Corcoran 	} else if (afrp->adf_pattern != NULL &&
1077b72d5b75SMichael Corcoran 	    strncmp(afrp->adf_pattern,
1078b72d5b75SMichael Corcoran 	    (char *)&infop->awi_info->Name,
1079b72d5b75SMichael Corcoran 	    sizeof (infop->awi_info->Name))) {
1080b72d5b75SMichael Corcoran 		return (ACPIDEV_FILTER_CONTINUE);
1081b72d5b75SMichael Corcoran 	}
1082b72d5b75SMichael Corcoran 	if (afrp->adf_replace != NULL && devname != NULL) {
1083a3114836SGerry Liu 		(void) strlcpy(devname, afrp->adf_replace, len);
1084b72d5b75SMichael Corcoran 	}
1085b72d5b75SMichael Corcoran 
1086b72d5b75SMichael Corcoran 	return (afrp->adf_retcode);
1087b72d5b75SMichael Corcoran }
1088b72d5b75SMichael Corcoran 
1089b72d5b75SMichael Corcoran acpidev_filter_result_t
acpidev_filter_device(acpidev_walk_info_t * infop,ACPI_HANDLE hdl,acpidev_filter_rule_t * afrp,int entries,char * devname,int len)1090b72d5b75SMichael Corcoran acpidev_filter_device(acpidev_walk_info_t *infop, ACPI_HANDLE hdl,
1091b72d5b75SMichael Corcoran     acpidev_filter_rule_t *afrp, int entries, char *devname, int len)
1092b72d5b75SMichael Corcoran {
1093b72d5b75SMichael Corcoran 	acpidev_filter_result_t res;
1094b72d5b75SMichael Corcoran 
1095584b574aSToomas Soome 	res = ACPIDEV_FILTER_FAILED;
1096b72d5b75SMichael Corcoran 	/* Evaluate filtering rules. */
1097b72d5b75SMichael Corcoran 	for (; entries > 0; entries--, afrp++) {
1098b72d5b75SMichael Corcoran 		if (afrp->adf_filter_func != NULL) {
1099b72d5b75SMichael Corcoran 			res = afrp->adf_filter_func(infop, hdl, afrp,
1100b72d5b75SMichael Corcoran 			    devname, len);
1101b72d5b75SMichael Corcoran 		} else {
1102b72d5b75SMichael Corcoran 			res = acpidev_filter_default(infop, hdl, afrp,
1103b72d5b75SMichael Corcoran 			    devname, len);
1104b72d5b75SMichael Corcoran 		}
1105b72d5b75SMichael Corcoran 		if (res == ACPIDEV_FILTER_DEFAULT ||
1106b72d5b75SMichael Corcoran 		    res == ACPIDEV_FILTER_SCAN) {
1107b72d5b75SMichael Corcoran 			infop->awi_class_list = afrp->adf_class_list;
1108b72d5b75SMichael Corcoran 			break;
1109b72d5b75SMichael Corcoran 		}
1110b72d5b75SMichael Corcoran 	}
1111b72d5b75SMichael Corcoran 
1112b72d5b75SMichael Corcoran 	return (res);
1113b72d5b75SMichael Corcoran }
1114b72d5b75SMichael Corcoran 
1115b72d5b75SMichael Corcoran dev_info_t *
acpidev_root_node(void)1116b72d5b75SMichael Corcoran acpidev_root_node(void)
1117b72d5b75SMichael Corcoran {
1118b72d5b75SMichael Corcoran 	return (acpidev_root_dip);
1119b72d5b75SMichael Corcoran }
1120b72d5b75SMichael Corcoran 
1121b72d5b75SMichael Corcoran ACPI_STATUS
acpidev_register_class(acpidev_class_list_t ** listpp,acpidev_class_t * clsp,boolean_t tail)1122b72d5b75SMichael Corcoran acpidev_register_class(acpidev_class_list_t **listpp, acpidev_class_t *clsp,
1123b72d5b75SMichael Corcoran     boolean_t tail)
1124b72d5b75SMichael Corcoran {
1125b72d5b75SMichael Corcoran 	ACPI_STATUS rc;
1126b72d5b75SMichael Corcoran 	acpidev_class_list_t *item;
1127b72d5b75SMichael Corcoran 	acpidev_class_list_t *temp;
1128b72d5b75SMichael Corcoran 
1129b72d5b75SMichael Corcoran 	ASSERT(clsp != NULL);
1130b72d5b75SMichael Corcoran 	ASSERT(listpp != NULL);
1131b72d5b75SMichael Corcoran 	if (listpp == NULL || clsp == NULL) {
1132b72d5b75SMichael Corcoran 		ACPIDEV_DEBUG(CE_WARN,
1133a3114836SGerry Liu 		    "!acpidev: invalid parameter in acpidev_register_class().");
1134b72d5b75SMichael Corcoran 		return (AE_BAD_PARAMETER);
1135b72d5b75SMichael Corcoran 	} else if (clsp->adc_version != ACPIDEV_CLASS_REV) {
1136b72d5b75SMichael Corcoran 		cmn_err(CE_WARN,
1137b72d5b75SMichael Corcoran 		    "!acpidev: class driver %s version mismatch.",
1138b72d5b75SMichael Corcoran 		    clsp->adc_class_name);
1139b72d5b75SMichael Corcoran 		return (AE_BAD_DATA);
1140b72d5b75SMichael Corcoran 	}
1141b72d5b75SMichael Corcoran 
1142b72d5b75SMichael Corcoran 	rc = AE_OK;
1143b72d5b75SMichael Corcoran 	item = kmem_zalloc(sizeof (*item), KM_SLEEP);
1144b72d5b75SMichael Corcoran 	item->acl_class = clsp;
1145b72d5b75SMichael Corcoran 	rw_enter(&acpidev_class_lock, RW_WRITER);
1146b72d5b75SMichael Corcoran 	/* Check for duplicated item. */
1147b72d5b75SMichael Corcoran 	for (temp = *listpp; temp != NULL; temp = temp->acl_next) {
1148b72d5b75SMichael Corcoran 		if (temp->acl_class == clsp) {
1149b72d5b75SMichael Corcoran 			cmn_err(CE_WARN,
1150b72d5b75SMichael Corcoran 			    "!acpidev: register duplicate class driver %s.",
1151b72d5b75SMichael Corcoran 			    clsp->adc_class_name);
1152b72d5b75SMichael Corcoran 			rc = AE_ALREADY_EXISTS;
1153b72d5b75SMichael Corcoran 			break;
1154b72d5b75SMichael Corcoran 		}
1155b72d5b75SMichael Corcoran 	}
1156b72d5b75SMichael Corcoran 	if (ACPI_SUCCESS(rc)) {
1157b72d5b75SMichael Corcoran 		if (tail) {
1158b72d5b75SMichael Corcoran 			while (*listpp) {
1159b72d5b75SMichael Corcoran 				listpp = &(*listpp)->acl_next;
1160b72d5b75SMichael Corcoran 			}
1161b72d5b75SMichael Corcoran 		}
1162b72d5b75SMichael Corcoran 		item->acl_next = *listpp;
1163b72d5b75SMichael Corcoran 		*listpp = item;
1164b72d5b75SMichael Corcoran 	}
1165b72d5b75SMichael Corcoran 	rw_exit(&acpidev_class_lock);
1166b72d5b75SMichael Corcoran 	if (ACPI_FAILURE(rc)) {
1167b72d5b75SMichael Corcoran 		kmem_free(item, sizeof (*item));
1168b72d5b75SMichael Corcoran 	}
1169b72d5b75SMichael Corcoran 
1170b72d5b75SMichael Corcoran 	return (rc);
1171b72d5b75SMichael Corcoran }
1172b72d5b75SMichael Corcoran 
1173b72d5b75SMichael Corcoran ACPI_STATUS
acpidev_unregister_class(acpidev_class_list_t ** listpp,acpidev_class_t * clsp)1174b72d5b75SMichael Corcoran acpidev_unregister_class(acpidev_class_list_t **listpp,
1175b72d5b75SMichael Corcoran     acpidev_class_t *clsp)
1176b72d5b75SMichael Corcoran {
1177b72d5b75SMichael Corcoran 	ACPI_STATUS rc = AE_NOT_FOUND;
1178b72d5b75SMichael Corcoran 	acpidev_class_list_t *temp;
1179b72d5b75SMichael Corcoran 
1180b72d5b75SMichael Corcoran 	ASSERT(clsp != NULL);
1181b72d5b75SMichael Corcoran 	ASSERT(listpp != NULL);
1182b72d5b75SMichael Corcoran 	if (listpp == NULL || clsp == NULL) {
1183a3114836SGerry Liu 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter "
1184b72d5b75SMichael Corcoran 		    "in acpidev_unregister_class().");
1185b72d5b75SMichael Corcoran 		return (AE_BAD_PARAMETER);
1186b72d5b75SMichael Corcoran 	}
1187b72d5b75SMichael Corcoran 
1188b72d5b75SMichael Corcoran 	rw_enter(&acpidev_class_lock, RW_WRITER);
1189b72d5b75SMichael Corcoran 	for (temp = NULL; *listpp; listpp = &(*listpp)->acl_next) {
1190b72d5b75SMichael Corcoran 		if ((*listpp)->acl_class == clsp) {
1191b72d5b75SMichael Corcoran 			temp = *listpp;
1192b72d5b75SMichael Corcoran 			*listpp = (*listpp)->acl_next;
1193b72d5b75SMichael Corcoran 			break;
1194b72d5b75SMichael Corcoran 		}
1195b72d5b75SMichael Corcoran 	}
1196b72d5b75SMichael Corcoran 	if (temp == NULL) {
1197a3114836SGerry Liu 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: class %p(%s) doesn't exist "
1198b72d5b75SMichael Corcoran 		    "in acpidev_unregister_class().",
1199b72d5b75SMichael Corcoran 		    (void *)clsp, clsp->adc_class_name);
1200b72d5b75SMichael Corcoran 		rc = AE_NOT_FOUND;
1201b72d5b75SMichael Corcoran 	} else if (temp->acl_class->adc_refcnt != 0) {
1202a3114836SGerry Liu 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: class %p(%s) is still in use "
1203b72d5b75SMichael Corcoran 		    "in acpidev_unregister_class()..",
1204b72d5b75SMichael Corcoran 		    (void *)clsp, clsp->adc_class_name);
1205b72d5b75SMichael Corcoran 		rc = AE_ERROR;
1206b72d5b75SMichael Corcoran 	} else {
1207b72d5b75SMichael Corcoran 		kmem_free(temp, sizeof (*temp));
1208b72d5b75SMichael Corcoran 		rc = AE_OK;
1209b72d5b75SMichael Corcoran 	}
1210b72d5b75SMichael Corcoran 	rw_exit(&acpidev_class_lock);
1211b72d5b75SMichael Corcoran 
1212b72d5b75SMichael Corcoran 	return (rc);
1213b72d5b75SMichael Corcoran }
1214