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