xref: /onnv-gate/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_drv.c (revision 12004:93f274d4a367)
110394SMichael.Corcoran@Sun.COM /*
210394SMichael.Corcoran@Sun.COM  * CDDL HEADER START
310394SMichael.Corcoran@Sun.COM  *
410394SMichael.Corcoran@Sun.COM  * The contents of this file are subject to the terms of the
510394SMichael.Corcoran@Sun.COM  * Common Development and Distribution License (the "License").
610394SMichael.Corcoran@Sun.COM  * You may not use this file except in compliance with the License.
710394SMichael.Corcoran@Sun.COM  *
810394SMichael.Corcoran@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910394SMichael.Corcoran@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1010394SMichael.Corcoran@Sun.COM  * See the License for the specific language governing permissions
1110394SMichael.Corcoran@Sun.COM  * and limitations under the License.
1210394SMichael.Corcoran@Sun.COM  *
1310394SMichael.Corcoran@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1410394SMichael.Corcoran@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510394SMichael.Corcoran@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1610394SMichael.Corcoran@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1710394SMichael.Corcoran@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1810394SMichael.Corcoran@Sun.COM  *
1910394SMichael.Corcoran@Sun.COM  * CDDL HEADER END
2010394SMichael.Corcoran@Sun.COM  */
2110394SMichael.Corcoran@Sun.COM /*
22*12004Sjiang.liu@intel.com  * Copyright (c) 2009-2010, Intel Corporation.
2310394SMichael.Corcoran@Sun.COM  * All rights reserved.
2410394SMichael.Corcoran@Sun.COM  */
2510394SMichael.Corcoran@Sun.COM 
2610394SMichael.Corcoran@Sun.COM /*
2710394SMichael.Corcoran@Sun.COM  * Platform specific device enumerator for ACPI specific devices.
2810394SMichael.Corcoran@Sun.COM  * "x86 system devices" refers to the suite of hardware components which are
2910394SMichael.Corcoran@Sun.COM  * common to the x86 platform and play important roles in the system
3010394SMichael.Corcoran@Sun.COM  * architecture but can't be enumerated/discovered through industry-standard
3110394SMichael.Corcoran@Sun.COM  * bus specifications. Examples of these x86 system devices include:
3210394SMichael.Corcoran@Sun.COM  *   * Logical processor/CPU
3310394SMichael.Corcoran@Sun.COM  *   * Memory device
3410394SMichael.Corcoran@Sun.COM  *   * Non-PCI discoverable IOMMU or DMA Remapping Engine
3510394SMichael.Corcoran@Sun.COM  *   * Non-PCI discoverable IOxAPIC
3610394SMichael.Corcoran@Sun.COM  *   * Non-PCI discoverable HPET (High Precision Event Timer)
3710394SMichael.Corcoran@Sun.COM  *   * ACPI defined devices, including power button, sleep button, battery etc.
3810394SMichael.Corcoran@Sun.COM  *
3910394SMichael.Corcoran@Sun.COM  * X86 system devices may be discovered through BIOS/Firmware interfaces, such
4010394SMichael.Corcoran@Sun.COM  * as SMBIOS tables, MPS tables and ACPI tables since their discovery isn't
4110394SMichael.Corcoran@Sun.COM  * covered by any industry-standard bus specifications.
4210394SMichael.Corcoran@Sun.COM  *
4310394SMichael.Corcoran@Sun.COM  * In order to aid Solaris in flexibly managing x86 system devices,
4410394SMichael.Corcoran@Sun.COM  * x86 system devices are placed into a specific firmware device
4510394SMichael.Corcoran@Sun.COM  * subtree whose device path is '/devices/fw'.
4610394SMichael.Corcoran@Sun.COM  *
4710394SMichael.Corcoran@Sun.COM  * This driver populates the firmware device subtree with ACPI-discoverable
4810394SMichael.Corcoran@Sun.COM  * system devices if possible. To achieve that, the ACPI object
4910394SMichael.Corcoran@Sun.COM  * namespace is abstracted as ACPI virtual buses which host system devices.
5010394SMichael.Corcoran@Sun.COM  * Another nexus driver for the ACPI virtual bus will manage all devices
5110394SMichael.Corcoran@Sun.COM  * connected to it.
5210394SMichael.Corcoran@Sun.COM  *
5310394SMichael.Corcoran@Sun.COM  * For more detailed information, please refer to PSARC/2009/104.
5410394SMichael.Corcoran@Sun.COM  */
5510394SMichael.Corcoran@Sun.COM 
5610394SMichael.Corcoran@Sun.COM #include <sys/types.h>
5710394SMichael.Corcoran@Sun.COM #include <sys/bitmap.h>
5810394SMichael.Corcoran@Sun.COM #include <sys/cmn_err.h>
5910394SMichael.Corcoran@Sun.COM #include <sys/ddi_subrdefs.h>
6010394SMichael.Corcoran@Sun.COM #include <sys/errno.h>
6110394SMichael.Corcoran@Sun.COM #include <sys/modctl.h>
6210394SMichael.Corcoran@Sun.COM #include <sys/mutex.h>
63*12004Sjiang.liu@intel.com #include <sys/note.h>
6410394SMichael.Corcoran@Sun.COM #include <sys/obpdefs.h>
6510394SMichael.Corcoran@Sun.COM #include <sys/sunddi.h>
6610394SMichael.Corcoran@Sun.COM #include <sys/sunndi.h>
6710394SMichael.Corcoran@Sun.COM #include <sys/acpi/acpi.h>
6810394SMichael.Corcoran@Sun.COM #include <sys/acpica.h>
6910394SMichael.Corcoran@Sun.COM #include <sys/acpidev.h>
70*12004Sjiang.liu@intel.com #include <sys/acpidev_dr.h>
7110394SMichael.Corcoran@Sun.COM #include <sys/acpidev_impl.h>
7210394SMichael.Corcoran@Sun.COM 
7310394SMichael.Corcoran@Sun.COM /* Patchable through /etc/system */
7410394SMichael.Corcoran@Sun.COM int acpidev_options = 0;
7510394SMichael.Corcoran@Sun.COM int acpidev_debug = 0;
7610394SMichael.Corcoran@Sun.COM 
77*12004Sjiang.liu@intel.com krwlock_t acpidev_class_lock;
7810394SMichael.Corcoran@Sun.COM acpidev_class_list_t *acpidev_class_list_root = NULL;
79*12004Sjiang.liu@intel.com ulong_t acpidev_object_type_mask[BT_BITOUL(ACPI_TYPE_NS_NODE_MAX + 1)];
8010394SMichael.Corcoran@Sun.COM 
8110394SMichael.Corcoran@Sun.COM /* ACPI device autoconfig global status */
8210394SMichael.Corcoran@Sun.COM typedef enum acpidev_status {
8310394SMichael.Corcoran@Sun.COM 	ACPIDEV_STATUS_FAILED = -2,	/* ACPI device autoconfig failed */
8410394SMichael.Corcoran@Sun.COM 	ACPIDEV_STATUS_DISABLED = -1,	/* ACPI device autoconfig disabled */
8510394SMichael.Corcoran@Sun.COM 	ACPIDEV_STATUS_UNKNOWN = 0,	/* initial status */
8610394SMichael.Corcoran@Sun.COM 	ACPIDEV_STATUS_INITIALIZED,	/* ACPI device autoconfig initialized */
8710394SMichael.Corcoran@Sun.COM 	ACPIDEV_STATUS_FIRST_PASS,	/* first probing finished */
8810394SMichael.Corcoran@Sun.COM 	ACPIDEV_STATUS_READY		/* second probing finished */
8910394SMichael.Corcoran@Sun.COM } acpidev_status_t;
9010394SMichael.Corcoran@Sun.COM 
9110394SMichael.Corcoran@Sun.COM static acpidev_status_t acpidev_status = ACPIDEV_STATUS_UNKNOWN;
9210394SMichael.Corcoran@Sun.COM static kmutex_t	acpidev_drv_lock;
9310394SMichael.Corcoran@Sun.COM static dev_info_t *acpidev_root_dip = NULL;
9410394SMichael.Corcoran@Sun.COM 
9510394SMichael.Corcoran@Sun.COM /* Boot time ACPI device enumerator. */
9610394SMichael.Corcoran@Sun.COM static void acpidev_boot_probe(int type);
9710394SMichael.Corcoran@Sun.COM 
9810394SMichael.Corcoran@Sun.COM /* DDI module auto configuration interface */
9910394SMichael.Corcoran@Sun.COM extern struct mod_ops mod_miscops;
10010394SMichael.Corcoran@Sun.COM 
10110394SMichael.Corcoran@Sun.COM static struct modlmisc modlmisc = {
10210394SMichael.Corcoran@Sun.COM 	&mod_miscops,
10310394SMichael.Corcoran@Sun.COM 	"ACPI device enumerator"
10410394SMichael.Corcoran@Sun.COM };
10510394SMichael.Corcoran@Sun.COM 
10610394SMichael.Corcoran@Sun.COM static struct modlinkage modlinkage = {
10710394SMichael.Corcoran@Sun.COM 	MODREV_1,
10810394SMichael.Corcoran@Sun.COM 	(void *)&modlmisc,
10910394SMichael.Corcoran@Sun.COM 	NULL
11010394SMichael.Corcoran@Sun.COM };
11110394SMichael.Corcoran@Sun.COM 
11210394SMichael.Corcoran@Sun.COM int
_init(void)11310394SMichael.Corcoran@Sun.COM _init(void)
11410394SMichael.Corcoran@Sun.COM {
11510394SMichael.Corcoran@Sun.COM 	int err;
11610394SMichael.Corcoran@Sun.COM 
11710394SMichael.Corcoran@Sun.COM 	if ((err = mod_install(&modlinkage)) == 0) {
11810394SMichael.Corcoran@Sun.COM 		bzero(acpidev_object_type_mask,
11910394SMichael.Corcoran@Sun.COM 		    sizeof (acpidev_object_type_mask));
12010394SMichael.Corcoran@Sun.COM 		mutex_init(&acpidev_drv_lock, NULL, MUTEX_DRIVER, NULL);
12110394SMichael.Corcoran@Sun.COM 		rw_init(&acpidev_class_lock, NULL, RW_DEFAULT, NULL);
122*12004Sjiang.liu@intel.com 		acpidev_dr_init();
12310394SMichael.Corcoran@Sun.COM 		impl_bus_add_probe(acpidev_boot_probe);
12410394SMichael.Corcoran@Sun.COM 	} else {
12510394SMichael.Corcoran@Sun.COM 		cmn_err(CE_WARN, "!acpidev: failed to install driver.");
12610394SMichael.Corcoran@Sun.COM 	}
12710394SMichael.Corcoran@Sun.COM 
12810394SMichael.Corcoran@Sun.COM 	return (err);
12910394SMichael.Corcoran@Sun.COM }
13010394SMichael.Corcoran@Sun.COM 
13110394SMichael.Corcoran@Sun.COM int
_fini(void)13210394SMichael.Corcoran@Sun.COM _fini(void)
13310394SMichael.Corcoran@Sun.COM {
13410394SMichael.Corcoran@Sun.COM 	/* No support for module unload. */
13510394SMichael.Corcoran@Sun.COM 	return (EBUSY);
13610394SMichael.Corcoran@Sun.COM }
13710394SMichael.Corcoran@Sun.COM 
13810394SMichael.Corcoran@Sun.COM int
_info(struct modinfo * modinfop)13910394SMichael.Corcoran@Sun.COM _info(struct modinfo *modinfop)
14010394SMichael.Corcoran@Sun.COM {
14110394SMichael.Corcoran@Sun.COM 	return (mod_info(&modlinkage, modinfop));
14210394SMichael.Corcoran@Sun.COM }
14310394SMichael.Corcoran@Sun.COM 
14410394SMichael.Corcoran@Sun.COM /* Check blacklists and load platform specific driver modules. */
14510394SMichael.Corcoran@Sun.COM static ACPI_STATUS
acpidev_load_plat_modules(void)14610394SMichael.Corcoran@Sun.COM acpidev_load_plat_modules(void)
14710394SMichael.Corcoran@Sun.COM {
14810394SMichael.Corcoran@Sun.COM 	return (AE_OK);
14910394SMichael.Corcoran@Sun.COM }
15010394SMichael.Corcoran@Sun.COM 
15110394SMichael.Corcoran@Sun.COM /* Unload platform specific driver modules. */
15210394SMichael.Corcoran@Sun.COM static void
acpidev_unload_plat_modules(void)15310394SMichael.Corcoran@Sun.COM acpidev_unload_plat_modules(void)
15410394SMichael.Corcoran@Sun.COM {
15510394SMichael.Corcoran@Sun.COM }
15610394SMichael.Corcoran@Sun.COM 
15710394SMichael.Corcoran@Sun.COM /* Unregister all device class drivers from the device driver lists. */
15810394SMichael.Corcoran@Sun.COM static void
acpidev_class_list_fini(void)15910394SMichael.Corcoran@Sun.COM acpidev_class_list_fini(void)
16010394SMichael.Corcoran@Sun.COM {
16110394SMichael.Corcoran@Sun.COM 	acpidev_unload_plat_modules();
16210394SMichael.Corcoran@Sun.COM 
163*12004Sjiang.liu@intel.com 	if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) {
164*12004Sjiang.liu@intel.com 		(void) acpidev_unregister_class(&acpidev_class_list_scope,
165*12004Sjiang.liu@intel.com 		    &acpidev_class_pci);
166*12004Sjiang.liu@intel.com 		(void) acpidev_unregister_class(&acpidev_class_list_device,
167*12004Sjiang.liu@intel.com 		    &acpidev_class_pci);
168*12004Sjiang.liu@intel.com 	}
169*12004Sjiang.liu@intel.com 
17010394SMichael.Corcoran@Sun.COM 	if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) {
17110394SMichael.Corcoran@Sun.COM 		(void) acpidev_unregister_class(&acpidev_class_list_device,
17210394SMichael.Corcoran@Sun.COM 		    &acpidev_class_memory);
17310394SMichael.Corcoran@Sun.COM 	}
17410394SMichael.Corcoran@Sun.COM 
17510394SMichael.Corcoran@Sun.COM 	if (acpidev_options & ACPIDEV_OUSER_NO_CPU) {
17610394SMichael.Corcoran@Sun.COM 		(void) acpidev_unregister_class(&acpidev_class_list_device,
17710394SMichael.Corcoran@Sun.COM 		    &acpidev_class_cpu);
17810394SMichael.Corcoran@Sun.COM 		(void) acpidev_unregister_class(&acpidev_class_list_scope,
17910394SMichael.Corcoran@Sun.COM 		    &acpidev_class_cpu);
18010394SMichael.Corcoran@Sun.COM 		(void) acpidev_unregister_class(&acpidev_class_list_root,
18110394SMichael.Corcoran@Sun.COM 		    &acpidev_class_cpu);
18210394SMichael.Corcoran@Sun.COM 	}
18310394SMichael.Corcoran@Sun.COM 
18410394SMichael.Corcoran@Sun.COM 	if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) {
18510394SMichael.Corcoran@Sun.COM 		(void) acpidev_unregister_class(&acpidev_class_list_device,
18610394SMichael.Corcoran@Sun.COM 		    &acpidev_class_container);
18710394SMichael.Corcoran@Sun.COM 	}
18810394SMichael.Corcoran@Sun.COM 
18910394SMichael.Corcoran@Sun.COM 	(void) acpidev_unregister_class(&acpidev_class_list_device,
19010394SMichael.Corcoran@Sun.COM 	    &acpidev_class_device);
19110394SMichael.Corcoran@Sun.COM 	(void) acpidev_unregister_class(&acpidev_class_list_root,
19210394SMichael.Corcoran@Sun.COM 	    &acpidev_class_device);
19310394SMichael.Corcoran@Sun.COM 
19410394SMichael.Corcoran@Sun.COM 	(void) acpidev_unregister_class(&acpidev_class_list_root,
19510394SMichael.Corcoran@Sun.COM 	    &acpidev_class_scope);
19610394SMichael.Corcoran@Sun.COM }
19710394SMichael.Corcoran@Sun.COM 
19810394SMichael.Corcoran@Sun.COM /* Register all device class drivers onto the driver lists. */
19910394SMichael.Corcoran@Sun.COM static ACPI_STATUS
acpidev_class_list_init(uint64_t * fp)20010394SMichael.Corcoran@Sun.COM acpidev_class_list_init(uint64_t *fp)
20110394SMichael.Corcoran@Sun.COM {
20210394SMichael.Corcoran@Sun.COM 	ACPI_STATUS rc = AE_OK;
20310394SMichael.Corcoran@Sun.COM 
20410394SMichael.Corcoran@Sun.COM 	/* Set bit in mask for supported object types. */
20510394SMichael.Corcoran@Sun.COM 	BT_SET(acpidev_object_type_mask, ACPI_TYPE_LOCAL_SCOPE);
20610394SMichael.Corcoran@Sun.COM 	BT_SET(acpidev_object_type_mask, ACPI_TYPE_DEVICE);
20710394SMichael.Corcoran@Sun.COM 
20810394SMichael.Corcoran@Sun.COM 	/*
20910394SMichael.Corcoran@Sun.COM 	 * Register the ACPI scope class driver onto the class driver lists.
21010394SMichael.Corcoran@Sun.COM 	 * Currently only ACPI scope objects under ACPI root node, such as _PR,
21110394SMichael.Corcoran@Sun.COM 	 * _SB, _TZ etc, need to be handled, so only register the scope class
21210394SMichael.Corcoran@Sun.COM 	 * driver onto the root list.
21310394SMichael.Corcoran@Sun.COM 	 */
21410394SMichael.Corcoran@Sun.COM 	if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_root,
21510394SMichael.Corcoran@Sun.COM 	    &acpidev_class_scope, B_FALSE))) {
21610394SMichael.Corcoran@Sun.COM 		goto error_out;
21710394SMichael.Corcoran@Sun.COM 	}
21810394SMichael.Corcoran@Sun.COM 
21910394SMichael.Corcoran@Sun.COM 	/*
22010394SMichael.Corcoran@Sun.COM 	 * Register the ACPI device class driver onto the class driver lists.
22110394SMichael.Corcoran@Sun.COM 	 * The ACPI device class driver should be registered at the tail to
22210394SMichael.Corcoran@Sun.COM 	 * handle all device objects which haven't been handled by other
22310394SMichael.Corcoran@Sun.COM 	 * HID/CID specific device class drivers.
22410394SMichael.Corcoran@Sun.COM 	 */
22510394SMichael.Corcoran@Sun.COM 	if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_root,
22610394SMichael.Corcoran@Sun.COM 	    &acpidev_class_device, B_TRUE))) {
22710394SMichael.Corcoran@Sun.COM 		goto error_root_device;
22810394SMichael.Corcoran@Sun.COM 	}
22910394SMichael.Corcoran@Sun.COM 	if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_device,
23010394SMichael.Corcoran@Sun.COM 	    &acpidev_class_device, B_TRUE))) {
23110394SMichael.Corcoran@Sun.COM 		goto error_device_device;
23210394SMichael.Corcoran@Sun.COM 	}
23310394SMichael.Corcoran@Sun.COM 
23410394SMichael.Corcoran@Sun.COM 	/* Check and register support for ACPI container device. */
23510394SMichael.Corcoran@Sun.COM 	if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) {
23610394SMichael.Corcoran@Sun.COM 		if (ACPI_FAILURE(acpidev_register_class(
23710394SMichael.Corcoran@Sun.COM 		    &acpidev_class_list_device, &acpidev_class_container,
23810394SMichael.Corcoran@Sun.COM 		    B_FALSE))) {
23910394SMichael.Corcoran@Sun.COM 			goto error_device_container;
24010394SMichael.Corcoran@Sun.COM 		}
24110394SMichael.Corcoran@Sun.COM 		*fp |= ACPI_DEVCFG_CONTAINER;
24210394SMichael.Corcoran@Sun.COM 	}
24310394SMichael.Corcoran@Sun.COM 
24410394SMichael.Corcoran@Sun.COM 	/* Check and register support for ACPI CPU device. */
24510394SMichael.Corcoran@Sun.COM 	if ((acpidev_options & ACPIDEV_OUSER_NO_CPU) == 0) {
24610394SMichael.Corcoran@Sun.COM 		/* Handle ACPI CPU Device */
24710394SMichael.Corcoran@Sun.COM 		if (ACPI_FAILURE(acpidev_register_class(
24810394SMichael.Corcoran@Sun.COM 		    &acpidev_class_list_device, &acpidev_class_cpu, B_FALSE))) {
24910394SMichael.Corcoran@Sun.COM 			goto error_device_cpu;
25010394SMichael.Corcoran@Sun.COM 		}
25110394SMichael.Corcoran@Sun.COM 		/* Handle ACPI Processor under _PR */
25210394SMichael.Corcoran@Sun.COM 		if (ACPI_FAILURE(acpidev_register_class(
25310394SMichael.Corcoran@Sun.COM 		    &acpidev_class_list_scope, &acpidev_class_cpu, B_FALSE))) {
25410394SMichael.Corcoran@Sun.COM 			goto error_scope_cpu;
25510394SMichael.Corcoran@Sun.COM 		}
25610394SMichael.Corcoran@Sun.COM 		/* House-keeping for CPU scan */
25710394SMichael.Corcoran@Sun.COM 		if (ACPI_FAILURE(acpidev_register_class(
25810394SMichael.Corcoran@Sun.COM 		    &acpidev_class_list_root, &acpidev_class_cpu, B_FALSE))) {
25910394SMichael.Corcoran@Sun.COM 			goto error_root_cpu;
26010394SMichael.Corcoran@Sun.COM 		}
26110394SMichael.Corcoran@Sun.COM 		BT_SET(acpidev_object_type_mask, ACPI_TYPE_PROCESSOR);
26210394SMichael.Corcoran@Sun.COM 		*fp |= ACPI_DEVCFG_CPU;
26310394SMichael.Corcoran@Sun.COM 	}
26410394SMichael.Corcoran@Sun.COM 
265*12004Sjiang.liu@intel.com 	/* Check support of ACPI memory devices. */
26610394SMichael.Corcoran@Sun.COM 	if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) {
26710394SMichael.Corcoran@Sun.COM 		/*
26810394SMichael.Corcoran@Sun.COM 		 * Register the ACPI memory class driver onto the
26910394SMichael.Corcoran@Sun.COM 		 * acpidev_class_list_device list because ACPI module
27010394SMichael.Corcoran@Sun.COM 		 * class driver uses that list.
27110394SMichael.Corcoran@Sun.COM 		 */
27210394SMichael.Corcoran@Sun.COM 		if (ACPI_FAILURE(acpidev_register_class(
27310394SMichael.Corcoran@Sun.COM 		    &acpidev_class_list_device, &acpidev_class_memory,
27410394SMichael.Corcoran@Sun.COM 		    B_FALSE))) {
27510394SMichael.Corcoran@Sun.COM 			goto error_device_memory;
27610394SMichael.Corcoran@Sun.COM 		}
27710394SMichael.Corcoran@Sun.COM 		*fp |= ACPI_DEVCFG_MEMORY;
27810394SMichael.Corcoran@Sun.COM 	}
27910394SMichael.Corcoran@Sun.COM 
280*12004Sjiang.liu@intel.com 	/* Check support of PCI/PCIex Host Bridge devices. */
281*12004Sjiang.liu@intel.com 	if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) {
282*12004Sjiang.liu@intel.com 		/*
283*12004Sjiang.liu@intel.com 		 * Register pci/pciex class drivers onto
284*12004Sjiang.liu@intel.com 		 * the acpidev_class_list_device class list because ACPI
285*12004Sjiang.liu@intel.com 		 * module class driver uses that list.
286*12004Sjiang.liu@intel.com 		 */
287*12004Sjiang.liu@intel.com 		if (ACPI_FAILURE(acpidev_register_class(
288*12004Sjiang.liu@intel.com 		    &acpidev_class_list_device, &acpidev_class_pci,
289*12004Sjiang.liu@intel.com 		    B_FALSE))) {
290*12004Sjiang.liu@intel.com 			goto error_device_pci;
291*12004Sjiang.liu@intel.com 		}
292*12004Sjiang.liu@intel.com 
293*12004Sjiang.liu@intel.com 		/*
294*12004Sjiang.liu@intel.com 		 * Register pci/pciex class drivers onto the
295*12004Sjiang.liu@intel.com 		 * acpidev_class_list_scope class list.
296*12004Sjiang.liu@intel.com 		 */
297*12004Sjiang.liu@intel.com 		if (ACPI_FAILURE(acpidev_register_class(
298*12004Sjiang.liu@intel.com 		    &acpidev_class_list_scope, &acpidev_class_pci,
299*12004Sjiang.liu@intel.com 		    B_FALSE))) {
300*12004Sjiang.liu@intel.com 			goto error_scope_pci;
301*12004Sjiang.liu@intel.com 		}
302*12004Sjiang.liu@intel.com 
303*12004Sjiang.liu@intel.com 		*fp |= ACPI_DEVCFG_PCI;
304*12004Sjiang.liu@intel.com 	}
305*12004Sjiang.liu@intel.com 
30610394SMichael.Corcoran@Sun.COM 	/* Check blacklist and load platform specific modules. */
30710394SMichael.Corcoran@Sun.COM 	rc = acpidev_load_plat_modules();
30810394SMichael.Corcoran@Sun.COM 	if (ACPI_FAILURE(rc)) {
309*12004Sjiang.liu@intel.com 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to check blacklist "
31010394SMichael.Corcoran@Sun.COM 		    "or load pratform modules.");
31110394SMichael.Corcoran@Sun.COM 		goto error_plat;
31210394SMichael.Corcoran@Sun.COM 	}
31310394SMichael.Corcoran@Sun.COM 
31410394SMichael.Corcoran@Sun.COM 	return (AE_OK);
31510394SMichael.Corcoran@Sun.COM 
31610394SMichael.Corcoran@Sun.COM error_plat:
317*12004Sjiang.liu@intel.com 	if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) {
318*12004Sjiang.liu@intel.com 		(void) acpidev_unregister_class(&acpidev_class_list_scope,
319*12004Sjiang.liu@intel.com 		    &acpidev_class_pci);
320*12004Sjiang.liu@intel.com 	}
321*12004Sjiang.liu@intel.com error_scope_pci:
322*12004Sjiang.liu@intel.com 	if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) {
323*12004Sjiang.liu@intel.com 		(void) acpidev_unregister_class(&acpidev_class_list_device,
324*12004Sjiang.liu@intel.com 		    &acpidev_class_pci);
325*12004Sjiang.liu@intel.com 	}
326*12004Sjiang.liu@intel.com error_device_pci:
32710394SMichael.Corcoran@Sun.COM 	if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) {
32810394SMichael.Corcoran@Sun.COM 		(void) acpidev_unregister_class(&acpidev_class_list_device,
32910394SMichael.Corcoran@Sun.COM 		    &acpidev_class_memory);
33010394SMichael.Corcoran@Sun.COM 	}
33110394SMichael.Corcoran@Sun.COM error_device_memory:
33210394SMichael.Corcoran@Sun.COM 	if (acpidev_options & ACPIDEV_OUSER_NO_CPU) {
33310394SMichael.Corcoran@Sun.COM 		(void) acpidev_unregister_class(&acpidev_class_list_root,
33410394SMichael.Corcoran@Sun.COM 		    &acpidev_class_cpu);
33510394SMichael.Corcoran@Sun.COM 	}
33610394SMichael.Corcoran@Sun.COM error_root_cpu:
33710394SMichael.Corcoran@Sun.COM 	if (acpidev_options & ACPIDEV_OUSER_NO_CPU) {
33810394SMichael.Corcoran@Sun.COM 		(void) acpidev_unregister_class(&acpidev_class_list_scope,
33910394SMichael.Corcoran@Sun.COM 		    &acpidev_class_cpu);
34010394SMichael.Corcoran@Sun.COM 	}
34110394SMichael.Corcoran@Sun.COM error_scope_cpu:
34210394SMichael.Corcoran@Sun.COM 	if (acpidev_options & ACPIDEV_OUSER_NO_CPU) {
34310394SMichael.Corcoran@Sun.COM 		(void) acpidev_unregister_class(&acpidev_class_list_device,
34410394SMichael.Corcoran@Sun.COM 		    &acpidev_class_cpu);
34510394SMichael.Corcoran@Sun.COM 	}
34610394SMichael.Corcoran@Sun.COM error_device_cpu:
34710394SMichael.Corcoran@Sun.COM 	if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) {
34810394SMichael.Corcoran@Sun.COM 		(void) acpidev_unregister_class(&acpidev_class_list_device,
34910394SMichael.Corcoran@Sun.COM 		    &acpidev_class_container);
35010394SMichael.Corcoran@Sun.COM 	}
35110394SMichael.Corcoran@Sun.COM error_device_container:
35210394SMichael.Corcoran@Sun.COM 	(void) acpidev_unregister_class(&acpidev_class_list_device,
35310394SMichael.Corcoran@Sun.COM 	    &acpidev_class_device);
35410394SMichael.Corcoran@Sun.COM error_device_device:
35510394SMichael.Corcoran@Sun.COM 	(void) acpidev_unregister_class(&acpidev_class_list_root,
35610394SMichael.Corcoran@Sun.COM 	    &acpidev_class_device);
35710394SMichael.Corcoran@Sun.COM error_root_device:
35810394SMichael.Corcoran@Sun.COM 	(void) acpidev_unregister_class(&acpidev_class_list_root,
35910394SMichael.Corcoran@Sun.COM 	    &acpidev_class_scope);
36010394SMichael.Corcoran@Sun.COM error_out:
36110394SMichael.Corcoran@Sun.COM 	ACPIDEV_DEBUG(CE_WARN,
362*12004Sjiang.liu@intel.com 	    "!acpidev: failed to register built-in class drivers.");
36310394SMichael.Corcoran@Sun.COM 	*fp = 0;
36410394SMichael.Corcoran@Sun.COM 
36510394SMichael.Corcoran@Sun.COM 	return (AE_ERROR);
36610394SMichael.Corcoran@Sun.COM }
36710394SMichael.Corcoran@Sun.COM 
36810394SMichael.Corcoran@Sun.COM /*
36910394SMichael.Corcoran@Sun.COM  * Called in single threaded context during boot, no protection for
37010394SMichael.Corcoran@Sun.COM  * reentrance.
37110394SMichael.Corcoran@Sun.COM  */
37210394SMichael.Corcoran@Sun.COM static ACPI_STATUS
acpidev_create_root_node(void)37310394SMichael.Corcoran@Sun.COM acpidev_create_root_node(void)
37410394SMichael.Corcoran@Sun.COM {
37510394SMichael.Corcoran@Sun.COM 	int circ, rv = AE_OK;
37610394SMichael.Corcoran@Sun.COM 	dev_info_t *dip = NULL;
37710394SMichael.Corcoran@Sun.COM 	acpidev_data_handle_t objhdl;
37810394SMichael.Corcoran@Sun.COM 	char *compatibles[] = {
37910394SMichael.Corcoran@Sun.COM 		ACPIDEV_HID_ROOTNEX,
38010394SMichael.Corcoran@Sun.COM 		ACPIDEV_TYPE_ROOTNEX,
38110394SMichael.Corcoran@Sun.COM 		ACPIDEV_HID_VIRTNEX,
38210394SMichael.Corcoran@Sun.COM 		ACPIDEV_TYPE_VIRTNEX,
38310394SMichael.Corcoran@Sun.COM 	};
38410394SMichael.Corcoran@Sun.COM 
38510394SMichael.Corcoran@Sun.COM 	ndi_devi_enter(ddi_root_node(), &circ);
38610394SMichael.Corcoran@Sun.COM 	ASSERT(acpidev_root_dip == NULL);
38710394SMichael.Corcoran@Sun.COM 
38810394SMichael.Corcoran@Sun.COM 	/* Query whether device node already exists. */
38910394SMichael.Corcoran@Sun.COM 	dip = ddi_find_devinfo(ACPIDEV_NODE_NAME_ROOT, -1, 0);
39010394SMichael.Corcoran@Sun.COM 	if (dip != NULL && ddi_get_parent(dip) == ddi_root_node()) {
39110394SMichael.Corcoran@Sun.COM 		ndi_devi_exit(ddi_root_node(), circ);
39210394SMichael.Corcoran@Sun.COM 		cmn_err(CE_WARN, "!acpidev: node /devices/%s already exists, "
39310394SMichael.Corcoran@Sun.COM 		    "disable driver.", ACPIDEV_NODE_NAME_ROOT);
39410394SMichael.Corcoran@Sun.COM 		return (AE_ALREADY_EXISTS);
39510394SMichael.Corcoran@Sun.COM 	}
39610394SMichael.Corcoran@Sun.COM 
39710394SMichael.Corcoran@Sun.COM 	/* Create the device node if it doesn't exist. */
39810394SMichael.Corcoran@Sun.COM 	rv = ndi_devi_alloc(ddi_root_node(), ACPIDEV_NODE_NAME_ROOT,
39910394SMichael.Corcoran@Sun.COM 	    (pnode_t)DEVI_SID_NODEID, &dip);
40010394SMichael.Corcoran@Sun.COM 	if (rv != NDI_SUCCESS) {
40110394SMichael.Corcoran@Sun.COM 		ndi_devi_exit(ddi_root_node(), circ);
402*12004Sjiang.liu@intel.com 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to create device node "
40310394SMichael.Corcoran@Sun.COM 		    "for ACPI root with errcode %d.", rv);
40410394SMichael.Corcoran@Sun.COM 		return (AE_ERROR);
40510394SMichael.Corcoran@Sun.COM 	}
40610394SMichael.Corcoran@Sun.COM 
40710394SMichael.Corcoran@Sun.COM 	/* Build cross reference between dip and ACPI object. */
40810394SMichael.Corcoran@Sun.COM 	if (ACPI_FAILURE(acpica_tag_devinfo(dip, ACPI_ROOT_OBJECT))) {
40910394SMichael.Corcoran@Sun.COM 		(void) ddi_remove_child(dip, 0);
41010394SMichael.Corcoran@Sun.COM 		ndi_devi_exit(ddi_root_node(), circ);
411*12004Sjiang.liu@intel.com 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to tag object %s.",
41210394SMichael.Corcoran@Sun.COM 		    ACPIDEV_OBJECT_NAME_SB);
41310394SMichael.Corcoran@Sun.COM 		return (AE_ERROR);
41410394SMichael.Corcoran@Sun.COM 	}
41510394SMichael.Corcoran@Sun.COM 
41610394SMichael.Corcoran@Sun.COM 	/* Set device properties. */
41710394SMichael.Corcoran@Sun.COM 	rv = ndi_prop_update_string_array(DDI_DEV_T_NONE, dip,
41810394SMichael.Corcoran@Sun.COM 	    OBP_COMPATIBLE, ACPIDEV_ARRAY_PARAM(compatibles));
41910394SMichael.Corcoran@Sun.COM 	if (rv == NDI_SUCCESS) {
42010394SMichael.Corcoran@Sun.COM 		rv = ndi_prop_update_string(DDI_DEV_T_NONE, dip,
42110394SMichael.Corcoran@Sun.COM 		    OBP_DEVICETYPE, ACPIDEV_TYPE_ROOTNEX);
42210394SMichael.Corcoran@Sun.COM 	}
42310394SMichael.Corcoran@Sun.COM 	if (rv != DDI_SUCCESS) {
42410394SMichael.Corcoran@Sun.COM 		ACPIDEV_DEBUG(CE_WARN,
425*12004Sjiang.liu@intel.com 		    "!acpidev: failed to set device property for /devices/%s.",
42610394SMichael.Corcoran@Sun.COM 		    ACPIDEV_NODE_NAME_ROOT);
42710394SMichael.Corcoran@Sun.COM 		goto error_out;
42810394SMichael.Corcoran@Sun.COM 	}
42910394SMichael.Corcoran@Sun.COM 
43010394SMichael.Corcoran@Sun.COM 	/* Manually create an object handle for the root node */
43110394SMichael.Corcoran@Sun.COM 	objhdl = acpidev_data_create_handle(ACPI_ROOT_OBJECT);
43210394SMichael.Corcoran@Sun.COM 	if (objhdl == NULL) {
433*12004Sjiang.liu@intel.com 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to create object "
43410394SMichael.Corcoran@Sun.COM 		    "handle for the root node.");
43510394SMichael.Corcoran@Sun.COM 		goto error_out;
43610394SMichael.Corcoran@Sun.COM 	}
43710394SMichael.Corcoran@Sun.COM 	objhdl->aod_level = 0;
43810394SMichael.Corcoran@Sun.COM 	objhdl->aod_hdl = ACPI_ROOT_OBJECT;
43910394SMichael.Corcoran@Sun.COM 	objhdl->aod_dip = dip;
44010394SMichael.Corcoran@Sun.COM 	objhdl->aod_class = &acpidev_class_scope;
44110394SMichael.Corcoran@Sun.COM 	objhdl->aod_status = acpidev_query_device_status(ACPI_ROOT_OBJECT);
44210394SMichael.Corcoran@Sun.COM 	objhdl->aod_iflag = ACPIDEV_ODF_STATUS_VALID |
44310394SMichael.Corcoran@Sun.COM 	    ACPIDEV_ODF_DEVINFO_CREATED | ACPIDEV_ODF_DEVINFO_TAGGED;
44410394SMichael.Corcoran@Sun.COM 
44510394SMichael.Corcoran@Sun.COM 	/* Bind device driver. */
44610394SMichael.Corcoran@Sun.COM 	(void) ndi_devi_bind_driver(dip, 0);
44710394SMichael.Corcoran@Sun.COM 
44810394SMichael.Corcoran@Sun.COM 	acpidev_root_dip = dip;
44910394SMichael.Corcoran@Sun.COM 	ndi_devi_exit(ddi_root_node(), circ);
45010394SMichael.Corcoran@Sun.COM 
45110394SMichael.Corcoran@Sun.COM 	return (AE_OK);
45210394SMichael.Corcoran@Sun.COM 
45310394SMichael.Corcoran@Sun.COM error_out:
45410394SMichael.Corcoran@Sun.COM 	(void) acpica_untag_devinfo(dip, ACPI_ROOT_OBJECT);
45510394SMichael.Corcoran@Sun.COM 	(void) ddi_remove_child(dip, 0);
45610394SMichael.Corcoran@Sun.COM 	ndi_devi_exit(ddi_root_node(), circ);
45710394SMichael.Corcoran@Sun.COM 	return (AE_ERROR);
45810394SMichael.Corcoran@Sun.COM }
45910394SMichael.Corcoran@Sun.COM 
46010394SMichael.Corcoran@Sun.COM static void
acpidev_initialize(void)46110394SMichael.Corcoran@Sun.COM acpidev_initialize(void)
46210394SMichael.Corcoran@Sun.COM {
46310394SMichael.Corcoran@Sun.COM 	int rc;
46410394SMichael.Corcoran@Sun.COM 	char *str = NULL;
46510394SMichael.Corcoran@Sun.COM 	uint64_t features = 0;
46610394SMichael.Corcoran@Sun.COM 
46710394SMichael.Corcoran@Sun.COM 	/* Check whether it has already been initialized. */
46810394SMichael.Corcoran@Sun.COM 	if (acpidev_status == ACPIDEV_STATUS_DISABLED) {
46910394SMichael.Corcoran@Sun.COM 		cmn_err(CE_CONT, "?acpidev: ACPI device autoconfig "
47010394SMichael.Corcoran@Sun.COM 		    "disabled by user.\n");
47110394SMichael.Corcoran@Sun.COM 		return;
47210394SMichael.Corcoran@Sun.COM 	} else if (acpidev_status != ACPIDEV_STATUS_UNKNOWN) {
47310394SMichael.Corcoran@Sun.COM 		ACPIDEV_DEBUG(CE_NOTE,
474*12004Sjiang.liu@intel.com 		    "!acpidev: initialization called more than once.");
47510394SMichael.Corcoran@Sun.COM 		return;
47610394SMichael.Corcoran@Sun.COM 	}
47710394SMichael.Corcoran@Sun.COM 
47810394SMichael.Corcoran@Sun.COM 	/* Check whether ACPI device autoconfig has been disabled by user. */
47910394SMichael.Corcoran@Sun.COM 	rc = ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
48010394SMichael.Corcoran@Sun.COM 	    DDI_PROP_DONTPASS, "acpidev-autoconfig", &str);
48110394SMichael.Corcoran@Sun.COM 	if (rc == DDI_SUCCESS) {
48210394SMichael.Corcoran@Sun.COM 		if (strcasecmp(str, "off") == 0 || strcasecmp(str, "no") == 0) {
48310394SMichael.Corcoran@Sun.COM 			cmn_err(CE_CONT, "?acpidev: ACPI device autoconfig "
48410394SMichael.Corcoran@Sun.COM 			    "disabled by user.\n");
48510394SMichael.Corcoran@Sun.COM 			ddi_prop_free(str);
48610394SMichael.Corcoran@Sun.COM 			acpidev_status = ACPIDEV_STATUS_DISABLED;
48710394SMichael.Corcoran@Sun.COM 			return;
48810394SMichael.Corcoran@Sun.COM 		}
48910394SMichael.Corcoran@Sun.COM 		ddi_prop_free(str);
49010394SMichael.Corcoran@Sun.COM 	}
49110394SMichael.Corcoran@Sun.COM 
49210394SMichael.Corcoran@Sun.COM 	/* Initialize acpica subsystem. */
49310394SMichael.Corcoran@Sun.COM 	if (ACPI_FAILURE(acpica_init())) {
49410394SMichael.Corcoran@Sun.COM 		cmn_err(CE_WARN,
49510394SMichael.Corcoran@Sun.COM 		    "!acpidev: failed to initialize acpica subsystem.");
49610394SMichael.Corcoran@Sun.COM 		acpidev_status = ACPIDEV_STATUS_FAILED;
49710394SMichael.Corcoran@Sun.COM 		return;
49810394SMichael.Corcoran@Sun.COM 	}
49910394SMichael.Corcoran@Sun.COM 
50010394SMichael.Corcoran@Sun.COM 	/* Check ACPICA subsystem status. */
50110394SMichael.Corcoran@Sun.COM 	if (!acpica_get_core_feature(ACPI_FEATURE_FULL_INIT)) {
50210394SMichael.Corcoran@Sun.COM 		cmn_err(CE_WARN, "!acpidev: ACPICA hasn't been fully "
50310394SMichael.Corcoran@Sun.COM 		    "initialized, ACPI device autoconfig will be disabled.");
50410394SMichael.Corcoran@Sun.COM 		acpidev_status = ACPIDEV_STATUS_DISABLED;
50510394SMichael.Corcoran@Sun.COM 		return;
50610394SMichael.Corcoran@Sun.COM 	}
50710394SMichael.Corcoran@Sun.COM 
50810394SMichael.Corcoran@Sun.COM 	/* Converts acpidev-options from type string to int, if any */
50910394SMichael.Corcoran@Sun.COM 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
51010394SMichael.Corcoran@Sun.COM 	    DDI_PROP_DONTPASS, "acpidev-options", &str) == DDI_PROP_SUCCESS) {
51110394SMichael.Corcoran@Sun.COM 		long data;
51210394SMichael.Corcoran@Sun.COM 		rc = ddi_strtol(str, NULL, 0, &data);
51310394SMichael.Corcoran@Sun.COM 		if (rc == 0) {
51410394SMichael.Corcoran@Sun.COM 			(void) e_ddi_prop_remove(DDI_DEV_T_NONE,
51510394SMichael.Corcoran@Sun.COM 			    ddi_root_node(), "acpidev-options");
51610394SMichael.Corcoran@Sun.COM 			(void) e_ddi_prop_update_int(DDI_DEV_T_NONE,
51710394SMichael.Corcoran@Sun.COM 			    ddi_root_node(), "acpidev-options", data);
51810394SMichael.Corcoran@Sun.COM 		}
51910394SMichael.Corcoran@Sun.COM 		ddi_prop_free(str);
52010394SMichael.Corcoran@Sun.COM 	}
52110394SMichael.Corcoran@Sun.COM 	/* Get acpidev_options user options. */
52210394SMichael.Corcoran@Sun.COM 	acpidev_options = ddi_prop_get_int(DDI_DEV_T_ANY, ddi_root_node(),
52310394SMichael.Corcoran@Sun.COM 	    DDI_PROP_DONTPASS, "acpidev-options", acpidev_options);
52410394SMichael.Corcoran@Sun.COM 
525*12004Sjiang.liu@intel.com 	/* Check whether ACPI based DR has been disabled by user. */
526*12004Sjiang.liu@intel.com 	rc = ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
527*12004Sjiang.liu@intel.com 	    DDI_PROP_DONTPASS, "acpidev-dr", &str);
528*12004Sjiang.liu@intel.com 	if (rc == DDI_SUCCESS) {
529*12004Sjiang.liu@intel.com 		if (strcasecmp(str, "off") == 0 || strcasecmp(str, "no") == 0) {
530*12004Sjiang.liu@intel.com 			cmn_err(CE_CONT, "?acpidev: ACPI based DR has been "
531*12004Sjiang.liu@intel.com 			    "disabled by user.\n");
532*12004Sjiang.liu@intel.com 			acpidev_dr_enable = 0;
533*12004Sjiang.liu@intel.com 		}
534*12004Sjiang.liu@intel.com 		ddi_prop_free(str);
535*12004Sjiang.liu@intel.com 	}
536*12004Sjiang.liu@intel.com 
53710394SMichael.Corcoran@Sun.COM 	/* Register all device class drivers. */
53810394SMichael.Corcoran@Sun.COM 	if (ACPI_FAILURE(acpidev_class_list_init(&features))) {
53910394SMichael.Corcoran@Sun.COM 		cmn_err(CE_WARN,
54010394SMichael.Corcoran@Sun.COM 		    "!acpidev: failed to initalize class driver lists.");
54110394SMichael.Corcoran@Sun.COM 		acpidev_status = ACPIDEV_STATUS_FAILED;
54210394SMichael.Corcoran@Sun.COM 		return;
54310394SMichael.Corcoran@Sun.COM 	}
54410394SMichael.Corcoran@Sun.COM 
54510394SMichael.Corcoran@Sun.COM 	/* Create root node for ACPI/firmware device subtree. */
54610394SMichael.Corcoran@Sun.COM 	if (ACPI_FAILURE(acpidev_create_root_node())) {
54710394SMichael.Corcoran@Sun.COM 		cmn_err(CE_WARN, "!acpidev: failed to create root node "
54810394SMichael.Corcoran@Sun.COM 		    "for acpi device tree.");
54910394SMichael.Corcoran@Sun.COM 		acpidev_class_list_fini();
55010394SMichael.Corcoran@Sun.COM 		acpidev_status = ACPIDEV_STATUS_FAILED;
55110394SMichael.Corcoran@Sun.COM 		return;
55210394SMichael.Corcoran@Sun.COM 	}
55310394SMichael.Corcoran@Sun.COM 
55410394SMichael.Corcoran@Sun.COM 	/* Notify acpica to enable ACPI device auto configuration. */
55510394SMichael.Corcoran@Sun.COM 	acpica_set_core_feature(ACPI_FEATURE_DEVCFG);
55610394SMichael.Corcoran@Sun.COM 	acpica_set_devcfg_feature(features);
55710394SMichael.Corcoran@Sun.COM 
55810394SMichael.Corcoran@Sun.COM 	ACPIDEV_DEBUG(CE_NOTE, "!acpidev: ACPI device autoconfig initialized.");
55910394SMichael.Corcoran@Sun.COM 	acpidev_status = ACPIDEV_STATUS_INITIALIZED;
56010394SMichael.Corcoran@Sun.COM }
56110394SMichael.Corcoran@Sun.COM 
56210394SMichael.Corcoran@Sun.COM /*
56310394SMichael.Corcoran@Sun.COM  * Probe devices in ACPI namespace which can't be enumerated by other methods
56410394SMichael.Corcoran@Sun.COM  * at boot time.
56510394SMichael.Corcoran@Sun.COM  */
56610394SMichael.Corcoran@Sun.COM static ACPI_STATUS
acpidev_boot_probe_device(acpidev_op_type_t op_type)56710394SMichael.Corcoran@Sun.COM acpidev_boot_probe_device(acpidev_op_type_t op_type)
56810394SMichael.Corcoran@Sun.COM {
56910394SMichael.Corcoran@Sun.COM 	ACPI_STATUS rc = AE_OK;
57010394SMichael.Corcoran@Sun.COM 	acpidev_walk_info_t *infop;
57110394SMichael.Corcoran@Sun.COM 
57210394SMichael.Corcoran@Sun.COM 	ASSERT(acpidev_root_dip != NULL);
57310394SMichael.Corcoran@Sun.COM 	ASSERT(op_type == ACPIDEV_OP_BOOT_PROBE ||
57410394SMichael.Corcoran@Sun.COM 	    op_type == ACPIDEV_OP_BOOT_REPROBE);
57510394SMichael.Corcoran@Sun.COM 
57610394SMichael.Corcoran@Sun.COM 	infop = acpidev_alloc_walk_info(op_type, 0, ACPI_ROOT_OBJECT,
57710394SMichael.Corcoran@Sun.COM 	    &acpidev_class_list_root, NULL);
57810394SMichael.Corcoran@Sun.COM 	if (infop == NULL) {
579*12004Sjiang.liu@intel.com 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to allocate walk info "
58010394SMichael.Corcoran@Sun.COM 		    "object in acpi_boot_probe_device().");
58110394SMichael.Corcoran@Sun.COM 		return (AE_ERROR);
58210394SMichael.Corcoran@Sun.COM 	}
58310394SMichael.Corcoran@Sun.COM 	/* Enumerate ACPI devices. */
58410394SMichael.Corcoran@Sun.COM 	rc = acpidev_probe_child(infop);
58510394SMichael.Corcoran@Sun.COM 	if (ACPI_FAILURE(rc)) {
58610394SMichael.Corcoran@Sun.COM 		cmn_err(CE_WARN, "!acpidev: failed to probe child object "
58710394SMichael.Corcoran@Sun.COM 		    "under ACPI root node.");
58810394SMichael.Corcoran@Sun.COM 	}
58910394SMichael.Corcoran@Sun.COM 	acpidev_free_walk_info(infop);
59010394SMichael.Corcoran@Sun.COM 
59110394SMichael.Corcoran@Sun.COM 	return (rc);
59210394SMichael.Corcoran@Sun.COM }
59310394SMichael.Corcoran@Sun.COM 
59410394SMichael.Corcoran@Sun.COM /*
59510394SMichael.Corcoran@Sun.COM  * Platform specific device prober for ACPI virtual bus.
59610394SMichael.Corcoran@Sun.COM  * It will be called in single-threaded environment to enumerate devices in
59710394SMichael.Corcoran@Sun.COM  * ACPI namespace at boot time.
59810394SMichael.Corcoran@Sun.COM  */
59910394SMichael.Corcoran@Sun.COM static void
acpidev_boot_probe(int type)60010394SMichael.Corcoran@Sun.COM acpidev_boot_probe(int type)
60110394SMichael.Corcoran@Sun.COM {
60210394SMichael.Corcoran@Sun.COM 	ACPI_STATUS rc;
60310394SMichael.Corcoran@Sun.COM 
60410394SMichael.Corcoran@Sun.COM 	/* Initialize subsystem on first pass. */
60510394SMichael.Corcoran@Sun.COM 	mutex_enter(&acpidev_drv_lock);
60610394SMichael.Corcoran@Sun.COM 	if (type == 0) {
60710394SMichael.Corcoran@Sun.COM 		acpidev_initialize();
60810394SMichael.Corcoran@Sun.COM 		if (acpidev_status != ACPIDEV_STATUS_INITIALIZED &&
60910394SMichael.Corcoran@Sun.COM 		    acpidev_status != ACPIDEV_STATUS_DISABLED) {
61010394SMichael.Corcoran@Sun.COM 			cmn_err(CE_WARN, "!acpidev: driver disabled due to "
61110394SMichael.Corcoran@Sun.COM 			    "initalization failure.");
61210394SMichael.Corcoran@Sun.COM 		}
61310394SMichael.Corcoran@Sun.COM 	}
61410394SMichael.Corcoran@Sun.COM 
61510394SMichael.Corcoran@Sun.COM 	/* Probe ACPI devices */
61610394SMichael.Corcoran@Sun.COM 	if (type == 0 && acpidev_status == ACPIDEV_STATUS_INITIALIZED) {
61710394SMichael.Corcoran@Sun.COM 		rc = acpidev_boot_probe_device(ACPIDEV_OP_BOOT_PROBE);
61810394SMichael.Corcoran@Sun.COM 		if (ACPI_SUCCESS(rc)) {
619*12004Sjiang.liu@intel.com 			/*
620*12004Sjiang.liu@intel.com 			 * Support of DR operations will be disabled
621*12004Sjiang.liu@intel.com 			 * if failed to initialize DR subsystem.
622*12004Sjiang.liu@intel.com 			 */
623*12004Sjiang.liu@intel.com 			rc = acpidev_dr_initialize(acpidev_root_dip);
624*12004Sjiang.liu@intel.com 			if (ACPI_FAILURE(rc) && rc != AE_SUPPORT) {
625*12004Sjiang.liu@intel.com 				cmn_err(CE_CONT, "?acpidev: failed to "
626*12004Sjiang.liu@intel.com 				    "initialize DR subsystem.");
627*12004Sjiang.liu@intel.com 			}
62810394SMichael.Corcoran@Sun.COM 			acpidev_status = ACPIDEV_STATUS_FIRST_PASS;
62910394SMichael.Corcoran@Sun.COM 		} else {
630*12004Sjiang.liu@intel.com 			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to probe ACPI "
63110394SMichael.Corcoran@Sun.COM 			    "devices during boot.");
63210394SMichael.Corcoran@Sun.COM 			acpidev_status = ACPIDEV_STATUS_FAILED;
63310394SMichael.Corcoran@Sun.COM 		}
63410394SMichael.Corcoran@Sun.COM 	} else if (type != 0 && acpidev_status == ACPIDEV_STATUS_FIRST_PASS) {
63510394SMichael.Corcoran@Sun.COM 		rc = acpidev_boot_probe_device(ACPIDEV_OP_BOOT_REPROBE);
63610394SMichael.Corcoran@Sun.COM 		if (ACPI_SUCCESS(rc)) {
63710394SMichael.Corcoran@Sun.COM 			acpidev_status = ACPIDEV_STATUS_READY;
63810394SMichael.Corcoran@Sun.COM 		} else {
639*12004Sjiang.liu@intel.com 			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to reprobe "
64010394SMichael.Corcoran@Sun.COM 			    "ACPI devices during boot.");
64110394SMichael.Corcoran@Sun.COM 			acpidev_status = ACPIDEV_STATUS_FAILED;
64210394SMichael.Corcoran@Sun.COM 		}
64310394SMichael.Corcoran@Sun.COM 	} else if (acpidev_status != ACPIDEV_STATUS_FAILED &&
64410394SMichael.Corcoran@Sun.COM 	    acpidev_status != ACPIDEV_STATUS_DISABLED &&
64510394SMichael.Corcoran@Sun.COM 	    acpidev_status != ACPIDEV_STATUS_READY) {
64610394SMichael.Corcoran@Sun.COM 		cmn_err(CE_WARN,
64710394SMichael.Corcoran@Sun.COM 		    "!acpidev: invalid ACPI device autoconfig global status.");
64810394SMichael.Corcoran@Sun.COM 	}
64910394SMichael.Corcoran@Sun.COM 	mutex_exit(&acpidev_drv_lock);
65010394SMichael.Corcoran@Sun.COM }
65110394SMichael.Corcoran@Sun.COM 
65210394SMichael.Corcoran@Sun.COM ACPI_STATUS
acpidev_probe_child(acpidev_walk_info_t * infop)65310394SMichael.Corcoran@Sun.COM acpidev_probe_child(acpidev_walk_info_t *infop)
65410394SMichael.Corcoran@Sun.COM {
65510394SMichael.Corcoran@Sun.COM 	int circ;
65610394SMichael.Corcoran@Sun.COM 	dev_info_t *pdip;
65710394SMichael.Corcoran@Sun.COM 	ACPI_STATUS res, rc = AE_OK;
65810394SMichael.Corcoran@Sun.COM 	ACPI_HANDLE child;
65910394SMichael.Corcoran@Sun.COM 	ACPI_OBJECT_TYPE type;
66010394SMichael.Corcoran@Sun.COM 	acpidev_class_list_t *it;
66110394SMichael.Corcoran@Sun.COM 	acpidev_walk_info_t *cinfop;
66210394SMichael.Corcoran@Sun.COM 	acpidev_data_handle_t datap;
66310394SMichael.Corcoran@Sun.COM 
66410394SMichael.Corcoran@Sun.COM 	/* Validate parameter first. */
66510394SMichael.Corcoran@Sun.COM 	ASSERT(infop != NULL);
66610394SMichael.Corcoran@Sun.COM 	if (infop == NULL) {
66710394SMichael.Corcoran@Sun.COM 		ACPIDEV_DEBUG(CE_WARN,
668*12004Sjiang.liu@intel.com 		    "!acpidev: infop is NULL in acpidev_probe_child().");
66910394SMichael.Corcoran@Sun.COM 		return (AE_BAD_PARAMETER);
67010394SMichael.Corcoran@Sun.COM 	}
67110394SMichael.Corcoran@Sun.COM 	ASSERT(infop->awi_level < ACPIDEV_MAX_ENUM_LEVELS - 1);
67210394SMichael.Corcoran@Sun.COM 	if (infop->awi_level >= ACPIDEV_MAX_ENUM_LEVELS - 1) {
673*12004Sjiang.liu@intel.com 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: recursive level is too deep "
67410394SMichael.Corcoran@Sun.COM 		    "in acpidev_probe_child().");
67510394SMichael.Corcoran@Sun.COM 		return (AE_BAD_PARAMETER);
67610394SMichael.Corcoran@Sun.COM 	}
67710394SMichael.Corcoran@Sun.COM 	ASSERT(infop->awi_class_list != NULL);
67810394SMichael.Corcoran@Sun.COM 	ASSERT(infop->awi_hdl != NULL);
67910394SMichael.Corcoran@Sun.COM 	ASSERT(infop->awi_info != NULL);
68010394SMichael.Corcoran@Sun.COM 	ASSERT(infop->awi_name != NULL);
68110394SMichael.Corcoran@Sun.COM 	ASSERT(infop->awi_data != NULL);
68210394SMichael.Corcoran@Sun.COM 	if (infop->awi_class_list == NULL || infop->awi_hdl == NULL ||
68310394SMichael.Corcoran@Sun.COM 	    infop->awi_info == NULL || infop->awi_name == NULL ||
68410394SMichael.Corcoran@Sun.COM 	    infop->awi_data == NULL) {
685*12004Sjiang.liu@intel.com 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: infop has NULL fields in "
686*12004Sjiang.liu@intel.com 		    "acpidev_probe_child().");
68710394SMichael.Corcoran@Sun.COM 		return (AE_BAD_PARAMETER);
68810394SMichael.Corcoran@Sun.COM 	}
68910394SMichael.Corcoran@Sun.COM 	pdip = acpidev_walk_info_get_pdip(infop);
69010394SMichael.Corcoran@Sun.COM 	if (pdip == NULL) {
69110394SMichael.Corcoran@Sun.COM 		ACPIDEV_DEBUG(CE_WARN,
692*12004Sjiang.liu@intel.com 		    "!acpidev: pdip is NULL in acpidev_probe_child().");
69310394SMichael.Corcoran@Sun.COM 		return (AE_BAD_PARAMETER);
69410394SMichael.Corcoran@Sun.COM 	}
69510394SMichael.Corcoran@Sun.COM 
69610394SMichael.Corcoran@Sun.COM 	ndi_devi_enter(pdip, &circ);
69710394SMichael.Corcoran@Sun.COM 	rw_enter(&acpidev_class_lock, RW_READER);
69810394SMichael.Corcoran@Sun.COM 
69910394SMichael.Corcoran@Sun.COM 	/* Call pre-probe callback functions. */
70010394SMichael.Corcoran@Sun.COM 	for (it = *(infop->awi_class_list); it != NULL; it = it->acl_next) {
70110394SMichael.Corcoran@Sun.COM 		if (it->acl_class->adc_pre_probe == NULL) {
70210394SMichael.Corcoran@Sun.COM 			continue;
70310394SMichael.Corcoran@Sun.COM 		}
70410394SMichael.Corcoran@Sun.COM 		infop->awi_class_curr = it->acl_class;
70510394SMichael.Corcoran@Sun.COM 		if (ACPI_FAILURE(it->acl_class->adc_pre_probe(infop))) {
706*12004Sjiang.liu@intel.com 			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to pre-probe "
70710394SMichael.Corcoran@Sun.COM 			    "device of type %s under %s.",
70810394SMichael.Corcoran@Sun.COM 			    it->acl_class->adc_class_name, infop->awi_name);
70910394SMichael.Corcoran@Sun.COM 		}
71010394SMichael.Corcoran@Sun.COM 	}
71110394SMichael.Corcoran@Sun.COM 
71210394SMichael.Corcoran@Sun.COM 	/* Walk child objects. */
71310394SMichael.Corcoran@Sun.COM 	child = NULL;
71410394SMichael.Corcoran@Sun.COM 	while (ACPI_SUCCESS(AcpiGetNextObject(ACPI_TYPE_ANY,
71510394SMichael.Corcoran@Sun.COM 	    infop->awi_hdl, child, &child))) {
71610394SMichael.Corcoran@Sun.COM 		/* Skip object if we're not interested in it. */
71710394SMichael.Corcoran@Sun.COM 		if (ACPI_FAILURE(AcpiGetType(child, &type)) ||
71810394SMichael.Corcoran@Sun.COM 		    type > ACPI_TYPE_NS_NODE_MAX ||
71910394SMichael.Corcoran@Sun.COM 		    BT_TEST(acpidev_object_type_mask, type) == 0) {
72010394SMichael.Corcoran@Sun.COM 			continue;
72110394SMichael.Corcoran@Sun.COM 		}
72210394SMichael.Corcoran@Sun.COM 
723*12004Sjiang.liu@intel.com 		/* It's another hotplug-capable board, skip it. */
724*12004Sjiang.liu@intel.com 		if (infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE &&
725*12004Sjiang.liu@intel.com 		    acpidev_dr_device_is_board(child)) {
726*12004Sjiang.liu@intel.com 			continue;
727*12004Sjiang.liu@intel.com 		}
728*12004Sjiang.liu@intel.com 
72910394SMichael.Corcoran@Sun.COM 		/* Allocate the walk info structure. */
73010394SMichael.Corcoran@Sun.COM 		cinfop = acpidev_alloc_walk_info(infop->awi_op_type,
73110394SMichael.Corcoran@Sun.COM 		    infop->awi_level + 1, child, NULL, infop);
73210394SMichael.Corcoran@Sun.COM 		if (cinfop == NULL) {
733*12004Sjiang.liu@intel.com 			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to allocate "
73410394SMichael.Corcoran@Sun.COM 			    "walk info child object of %s.",
73510394SMichael.Corcoran@Sun.COM 			    infop->awi_name);
73610394SMichael.Corcoran@Sun.COM 			/* Mark error and continue to handle next child. */
73710394SMichael.Corcoran@Sun.COM 			rc = AE_ERROR;
73810394SMichael.Corcoran@Sun.COM 			continue;
73910394SMichael.Corcoran@Sun.COM 		}
74010394SMichael.Corcoran@Sun.COM 
74110394SMichael.Corcoran@Sun.COM 		/*
74210394SMichael.Corcoran@Sun.COM 		 * Remember the class list used to handle this object.
74310394SMichael.Corcoran@Sun.COM 		 * It should be the same list for different passes of scans.
74410394SMichael.Corcoran@Sun.COM 		 */
74510394SMichael.Corcoran@Sun.COM 		ASSERT(cinfop->awi_data != NULL);
74610394SMichael.Corcoran@Sun.COM 		datap = cinfop->awi_data;
74710394SMichael.Corcoran@Sun.COM 		if (cinfop->awi_op_type == ACPIDEV_OP_BOOT_PROBE) {
74810394SMichael.Corcoran@Sun.COM 			datap->aod_class_list = infop->awi_class_list;
74910394SMichael.Corcoran@Sun.COM 		}
75010394SMichael.Corcoran@Sun.COM 
75110394SMichael.Corcoran@Sun.COM 		/* Call registered process callbacks. */
75210394SMichael.Corcoran@Sun.COM 		for (it = *(infop->awi_class_list); it != NULL;
75310394SMichael.Corcoran@Sun.COM 		    it = it->acl_next) {
75410394SMichael.Corcoran@Sun.COM 			if (it->acl_class->adc_probe == NULL) {
75510394SMichael.Corcoran@Sun.COM 				continue;
75610394SMichael.Corcoran@Sun.COM 			}
75710394SMichael.Corcoran@Sun.COM 			cinfop->awi_class_curr = it->acl_class;
75810394SMichael.Corcoran@Sun.COM 			res = it->acl_class->adc_probe(cinfop);
75910394SMichael.Corcoran@Sun.COM 			if (ACPI_FAILURE(res)) {
76010394SMichael.Corcoran@Sun.COM 				rc = res;
761*12004Sjiang.liu@intel.com 				ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to "
76210394SMichael.Corcoran@Sun.COM 				    "process object of type %s under %s.",
76310394SMichael.Corcoran@Sun.COM 				    it->acl_class->adc_class_name,
76410394SMichael.Corcoran@Sun.COM 				    infop->awi_name);
76510394SMichael.Corcoran@Sun.COM 			}
76610394SMichael.Corcoran@Sun.COM 		}
76710394SMichael.Corcoran@Sun.COM 
76810394SMichael.Corcoran@Sun.COM 		/* Free resources. */
76910394SMichael.Corcoran@Sun.COM 		acpidev_free_walk_info(cinfop);
77010394SMichael.Corcoran@Sun.COM 	}
77110394SMichael.Corcoran@Sun.COM 
77210394SMichael.Corcoran@Sun.COM 	/* Call post-probe callback functions. */
77310394SMichael.Corcoran@Sun.COM 	for (it = *(infop->awi_class_list); it != NULL; it = it->acl_next) {
77410394SMichael.Corcoran@Sun.COM 		if (it->acl_class->adc_post_probe == NULL) {
77510394SMichael.Corcoran@Sun.COM 			continue;
77610394SMichael.Corcoran@Sun.COM 		}
77710394SMichael.Corcoran@Sun.COM 		infop->awi_class_curr = it->acl_class;
77810394SMichael.Corcoran@Sun.COM 		if (ACPI_FAILURE(it->acl_class->adc_post_probe(infop))) {
779*12004Sjiang.liu@intel.com 			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to post-probe "
78010394SMichael.Corcoran@Sun.COM 			    "device of type %s under %s.",
78110394SMichael.Corcoran@Sun.COM 			    it->acl_class->adc_class_name, infop->awi_name);
78210394SMichael.Corcoran@Sun.COM 		}
78310394SMichael.Corcoran@Sun.COM 	}
78410394SMichael.Corcoran@Sun.COM 
78510394SMichael.Corcoran@Sun.COM 	rw_exit(&acpidev_class_lock);
78610394SMichael.Corcoran@Sun.COM 	ndi_devi_exit(pdip, circ);
78710394SMichael.Corcoran@Sun.COM 
78810394SMichael.Corcoran@Sun.COM 	return (rc);
78910394SMichael.Corcoran@Sun.COM }
79010394SMichael.Corcoran@Sun.COM 
79110394SMichael.Corcoran@Sun.COM ACPI_STATUS
acpidev_process_object(acpidev_walk_info_t * infop,int flags)79210394SMichael.Corcoran@Sun.COM acpidev_process_object(acpidev_walk_info_t *infop, int flags)
79310394SMichael.Corcoran@Sun.COM {
79410394SMichael.Corcoran@Sun.COM 	ACPI_STATUS rc = AE_OK;
79510394SMichael.Corcoran@Sun.COM 	char *devname;
79610394SMichael.Corcoran@Sun.COM 	dev_info_t *dip, *pdip;
79710394SMichael.Corcoran@Sun.COM 	ACPI_HANDLE hdl;
79810394SMichael.Corcoran@Sun.COM 	ACPI_DEVICE_INFO *adip;
79910394SMichael.Corcoran@Sun.COM 	acpidev_class_t *clsp;
80010394SMichael.Corcoran@Sun.COM 	acpidev_data_handle_t datap;
80110394SMichael.Corcoran@Sun.COM 	acpidev_filter_result_t res;
80210394SMichael.Corcoran@Sun.COM 
80310394SMichael.Corcoran@Sun.COM 	/* Validate parameters first. */
80410394SMichael.Corcoran@Sun.COM 	ASSERT(infop != NULL);
80510394SMichael.Corcoran@Sun.COM 	if (infop == NULL) {
80610394SMichael.Corcoran@Sun.COM 		ACPIDEV_DEBUG(CE_WARN,
807*12004Sjiang.liu@intel.com 		    "!acpidev: infop is NULL in acpidev_process_object().");
80810394SMichael.Corcoran@Sun.COM 		return (AE_BAD_PARAMETER);
80910394SMichael.Corcoran@Sun.COM 	}
81010394SMichael.Corcoran@Sun.COM 	ASSERT(infop->awi_hdl != NULL);
81110394SMichael.Corcoran@Sun.COM 	ASSERT(infop->awi_info != NULL);
81210394SMichael.Corcoran@Sun.COM 	ASSERT(infop->awi_data != NULL);
81310394SMichael.Corcoran@Sun.COM 	ASSERT(infop->awi_class_curr != NULL);
81410394SMichael.Corcoran@Sun.COM 	ASSERT(infop->awi_class_curr->adc_filter != NULL);
81510394SMichael.Corcoran@Sun.COM 	hdl = infop->awi_hdl;
81610394SMichael.Corcoran@Sun.COM 	adip = infop->awi_info;
81710394SMichael.Corcoran@Sun.COM 	datap = infop->awi_data;
81810394SMichael.Corcoran@Sun.COM 	clsp = infop->awi_class_curr;
81910394SMichael.Corcoran@Sun.COM 	if (hdl == NULL || datap == NULL || adip == NULL || clsp == NULL ||
82010394SMichael.Corcoran@Sun.COM 	    clsp->adc_filter == NULL) {
821*12004Sjiang.liu@intel.com 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: infop has NULL pointer in "
82210394SMichael.Corcoran@Sun.COM 		    "acpidev_process_object().");
82310394SMichael.Corcoran@Sun.COM 		return (AE_BAD_PARAMETER);
82410394SMichael.Corcoran@Sun.COM 	}
82510394SMichael.Corcoran@Sun.COM 	pdip = acpidev_walk_info_get_pdip(infop);
82610394SMichael.Corcoran@Sun.COM 	if (pdip == NULL) {
827*12004Sjiang.liu@intel.com 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to get pdip for %s "
82810394SMichael.Corcoran@Sun.COM 		    "in acpidev_process_object().", infop->awi_name);
82910394SMichael.Corcoran@Sun.COM 		return (AE_BAD_PARAMETER);
83010394SMichael.Corcoran@Sun.COM 	}
83110394SMichael.Corcoran@Sun.COM 
83210394SMichael.Corcoran@Sun.COM 	/*
83310394SMichael.Corcoran@Sun.COM 	 * Check whether the object has already been handled.
83410394SMichael.Corcoran@Sun.COM 	 * Tag and child dip pointer are used to indicate the object has been
83510394SMichael.Corcoran@Sun.COM 	 * handled by the ACPI auto configure driver. It has the
83610394SMichael.Corcoran@Sun.COM 	 * following usages:
83710394SMichael.Corcoran@Sun.COM 	 * 1) Prevent creating dip for objects which already have a dip
83810394SMichael.Corcoran@Sun.COM 	 *    when reloading the ACPI auto configure driver.
83910394SMichael.Corcoran@Sun.COM 	 * 2) Prevent creating multiple dips for ACPI objects with ACPI
84010394SMichael.Corcoran@Sun.COM 	 *    aliases. Currently ACPICA framework has no way to tell whether
84110394SMichael.Corcoran@Sun.COM 	 *    an object is an alias or not for some types of object. So tag
84210394SMichael.Corcoran@Sun.COM 	 *    is used to indicate that the object has been handled.
84310394SMichael.Corcoran@Sun.COM 	 * 3) Prevent multiple class drivers from creating multiple devices for
84410394SMichael.Corcoran@Sun.COM 	 *    the same ACPI object.
84510394SMichael.Corcoran@Sun.COM 	 */
84610394SMichael.Corcoran@Sun.COM 	if ((flags & ACPIDEV_PROCESS_FLAG_CREATE) &&
84710394SMichael.Corcoran@Sun.COM 	    (flags & ACPIDEV_PROCESS_FLAG_CHECK) &&
84810394SMichael.Corcoran@Sun.COM 	    !(infop->awi_flags & ACPIDEV_WI_DISABLE_CREATE) &&
84910394SMichael.Corcoran@Sun.COM 	    (infop->awi_flags & ACPIDEV_WI_DEVICE_CREATED)) {
85010394SMichael.Corcoran@Sun.COM 		ASSERT(infop->awi_dip != NULL);
85110394SMichael.Corcoran@Sun.COM 		ACPIDEV_DEBUG(CE_NOTE,
852*12004Sjiang.liu@intel.com 		    "!acpidev: device has already been created for object %s.",
85310394SMichael.Corcoran@Sun.COM 		    infop->awi_name);
85410394SMichael.Corcoran@Sun.COM 		return (AE_ALREADY_EXISTS);
85510394SMichael.Corcoran@Sun.COM 	}
85610394SMichael.Corcoran@Sun.COM 
85710394SMichael.Corcoran@Sun.COM 	/*
85810394SMichael.Corcoran@Sun.COM 	 * Determine action according to following rules based on device
85910394SMichael.Corcoran@Sun.COM 	 * status returned by _STA method. Please refer to ACPI3.0b section
86010394SMichael.Corcoran@Sun.COM 	 * 6.3.1 and 6.5.1.
86110394SMichael.Corcoran@Sun.COM 	 * present functioning enabled	Action
86210394SMichael.Corcoran@Sun.COM 	 *	0	0	x	Do nothing
863*12004Sjiang.liu@intel.com 	 *	1	x	0	Do nothing
86410394SMichael.Corcoran@Sun.COM 	 *	1	x	1	Create node and scan child
865*12004Sjiang.liu@intel.com 	 *	x	1	0	Do nothing
86610394SMichael.Corcoran@Sun.COM 	 *	x	1	1	Create node and scan child
86710394SMichael.Corcoran@Sun.COM 	 */
86810394SMichael.Corcoran@Sun.COM 	if ((datap->aod_iflag & ACPIDEV_ODF_STATUS_VALID) == 0 ||
86910394SMichael.Corcoran@Sun.COM 	    (flags & ACPIDEV_PROCESS_FLAG_SYNCSTATUS)) {
87010394SMichael.Corcoran@Sun.COM 		if (adip->Valid & ACPI_VALID_STA) {
87110394SMichael.Corcoran@Sun.COM 			datap->aod_status = adip->CurrentStatus;
87210394SMichael.Corcoran@Sun.COM 		} else {
87310394SMichael.Corcoran@Sun.COM 			datap->aod_status = acpidev_query_device_status(hdl);
87410394SMichael.Corcoran@Sun.COM 		}
87510394SMichael.Corcoran@Sun.COM 		datap->aod_iflag |= ACPIDEV_ODF_STATUS_VALID;
87610394SMichael.Corcoran@Sun.COM 	}
877*12004Sjiang.liu@intel.com 	if (!acpidev_check_device_enabled(datap->aod_status)) {
878*12004Sjiang.liu@intel.com 		ACPIDEV_DEBUG(CE_NOTE, "!acpidev: object %s doesn't exist.",
87910394SMichael.Corcoran@Sun.COM 		    infop->awi_name);
880*12004Sjiang.liu@intel.com 		/*
881*12004Sjiang.liu@intel.com 		 * Need to scan for hotplug-capable boards even if object
882*12004Sjiang.liu@intel.com 		 * doesn't exist or has been disabled during the first pass.
883*12004Sjiang.liu@intel.com 		 * So just disable creating device node and keep on scanning.
884*12004Sjiang.liu@intel.com 		 */
885*12004Sjiang.liu@intel.com 		if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE) {
886*12004Sjiang.liu@intel.com 			flags &= ~ACPIDEV_PROCESS_FLAG_CREATE;
887*12004Sjiang.liu@intel.com 		} else {
888*12004Sjiang.liu@intel.com 			return (AE_NOT_EXIST);
889*12004Sjiang.liu@intel.com 		}
89010394SMichael.Corcoran@Sun.COM 	}
89110394SMichael.Corcoran@Sun.COM 
89210394SMichael.Corcoran@Sun.COM 	ASSERT(infop->awi_data != NULL);
89310394SMichael.Corcoran@Sun.COM 	ASSERT(infop->awi_parent != NULL);
89410394SMichael.Corcoran@Sun.COM 	ASSERT(infop->awi_parent->awi_data != NULL);
895*12004Sjiang.liu@intel.com 	if (flags & ACPIDEV_PROCESS_FLAG_CREATE) {
896*12004Sjiang.liu@intel.com 		mutex_enter(&(DEVI(pdip)->devi_lock));
897*12004Sjiang.liu@intel.com 		/*
898*12004Sjiang.liu@intel.com 		 * Put the device into offline state if its parent is in
899*12004Sjiang.liu@intel.com 		 * offline state.
900*12004Sjiang.liu@intel.com 		 */
901*12004Sjiang.liu@intel.com 		if (DEVI_IS_DEVICE_OFFLINE(pdip)) {
902*12004Sjiang.liu@intel.com 			flags |= ACPIDEV_PROCESS_FLAG_OFFLINE;
903*12004Sjiang.liu@intel.com 		}
904*12004Sjiang.liu@intel.com 		mutex_exit(&(DEVI(pdip)->devi_lock));
90510394SMichael.Corcoran@Sun.COM 	}
90610394SMichael.Corcoran@Sun.COM 
90710394SMichael.Corcoran@Sun.COM 	/* Evaluate filtering rules and generate device name. */
90810394SMichael.Corcoran@Sun.COM 	devname = kmem_zalloc(ACPIDEV_MAX_NAMELEN + 1, KM_SLEEP);
90910394SMichael.Corcoran@Sun.COM 	(void) memcpy(devname, (char *)&adip->Name, sizeof (adip->Name));
91010394SMichael.Corcoran@Sun.COM 	if (flags & ACPIDEV_PROCESS_FLAG_CREATE) {
91110394SMichael.Corcoran@Sun.COM 		res = clsp->adc_filter(infop, devname, ACPIDEV_MAX_NAMELEN);
91210394SMichael.Corcoran@Sun.COM 	} else {
91310394SMichael.Corcoran@Sun.COM 		res = clsp->adc_filter(infop, NULL, 0);
91410394SMichael.Corcoran@Sun.COM 	}
91510394SMichael.Corcoran@Sun.COM 
91610394SMichael.Corcoran@Sun.COM 	/* Create device if requested. */
91710394SMichael.Corcoran@Sun.COM 	if ((flags & ACPIDEV_PROCESS_FLAG_CREATE) &&
91810394SMichael.Corcoran@Sun.COM 	    !(infop->awi_flags & ACPIDEV_WI_DISABLE_CREATE) &&
91910394SMichael.Corcoran@Sun.COM 	    !(infop->awi_flags & ACPIDEV_WI_DEVICE_CREATED) &&
92010394SMichael.Corcoran@Sun.COM 	    (res == ACPIDEV_FILTER_DEFAULT || res == ACPIDEV_FILTER_CREATE)) {
92110394SMichael.Corcoran@Sun.COM 		int ret;
92210394SMichael.Corcoran@Sun.COM 
92310394SMichael.Corcoran@Sun.COM 		/*
92410394SMichael.Corcoran@Sun.COM 		 * Allocate dip and set default properties.
92510394SMichael.Corcoran@Sun.COM 		 * Properties can be overriden in class specific init routines.
92610394SMichael.Corcoran@Sun.COM 		 */
92710394SMichael.Corcoran@Sun.COM 		ASSERT(infop->awi_dip == NULL);
92810394SMichael.Corcoran@Sun.COM 		ndi_devi_alloc_sleep(pdip, devname, (pnode_t)DEVI_SID_NODEID,
92910394SMichael.Corcoran@Sun.COM 		    &dip);
93010394SMichael.Corcoran@Sun.COM 		infop->awi_dip = dip;
93110394SMichael.Corcoran@Sun.COM 		ret = ndi_prop_update_string(DDI_DEV_T_NONE, dip,
93210394SMichael.Corcoran@Sun.COM 		    OBP_DEVICETYPE, clsp->adc_dev_type);
93310394SMichael.Corcoran@Sun.COM 		if (ret != NDI_SUCCESS) {
93410394SMichael.Corcoran@Sun.COM 			ACPIDEV_DEBUG(CE_WARN,
935*12004Sjiang.liu@intel.com 			    "!acpidev: failed to set device property for %s.",
93610394SMichael.Corcoran@Sun.COM 			    infop->awi_name);
93710394SMichael.Corcoran@Sun.COM 			(void) ddi_remove_child(dip, 0);
93810394SMichael.Corcoran@Sun.COM 			infop->awi_dip = NULL;
93910394SMichael.Corcoran@Sun.COM 			kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1);
94010394SMichael.Corcoran@Sun.COM 			return (AE_ERROR);
94110394SMichael.Corcoran@Sun.COM 		}
94210394SMichael.Corcoran@Sun.COM 
94310394SMichael.Corcoran@Sun.COM 		/* Build cross reference between dip and ACPI object. */
94410394SMichael.Corcoran@Sun.COM 		if ((flags & ACPIDEV_PROCESS_FLAG_NOTAG) == 0 &&
94510394SMichael.Corcoran@Sun.COM 		    ACPI_FAILURE(acpica_tag_devinfo(dip, hdl))) {
94610394SMichael.Corcoran@Sun.COM 			cmn_err(CE_WARN,
94710394SMichael.Corcoran@Sun.COM 			    "!acpidev: failed to tag object %s.",
94810394SMichael.Corcoran@Sun.COM 			    infop->awi_name);
94910394SMichael.Corcoran@Sun.COM 			(void) ddi_remove_child(dip, 0);
95010394SMichael.Corcoran@Sun.COM 			infop->awi_dip = NULL;
95110394SMichael.Corcoran@Sun.COM 			kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1);
95210394SMichael.Corcoran@Sun.COM 			return (AE_ERROR);
95310394SMichael.Corcoran@Sun.COM 		}
95410394SMichael.Corcoran@Sun.COM 
95510394SMichael.Corcoran@Sun.COM 		/* Call class specific initialization callback. */
95610394SMichael.Corcoran@Sun.COM 		if (clsp->adc_init != NULL &&
95710394SMichael.Corcoran@Sun.COM 		    ACPI_FAILURE(clsp->adc_init(infop))) {
95810394SMichael.Corcoran@Sun.COM 			ACPIDEV_DEBUG(CE_WARN,
959*12004Sjiang.liu@intel.com 			    "!acpidev: failed to initialize device %s.",
96010394SMichael.Corcoran@Sun.COM 			    infop->awi_name);
96110394SMichael.Corcoran@Sun.COM 			if ((flags & ACPIDEV_PROCESS_FLAG_NOTAG) == 0) {
96210394SMichael.Corcoran@Sun.COM 				(void) acpica_untag_devinfo(dip, hdl);
96310394SMichael.Corcoran@Sun.COM 			}
96410394SMichael.Corcoran@Sun.COM 			(void) ddi_remove_child(dip, 0);
96510394SMichael.Corcoran@Sun.COM 			infop->awi_dip = NULL;
96610394SMichael.Corcoran@Sun.COM 			kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1);
96710394SMichael.Corcoran@Sun.COM 			return (AE_ERROR);
96810394SMichael.Corcoran@Sun.COM 		}
96910394SMichael.Corcoran@Sun.COM 
97010394SMichael.Corcoran@Sun.COM 		/* Set device into offline state if requested. */
97110394SMichael.Corcoran@Sun.COM 		if (flags & ACPIDEV_PROCESS_FLAG_OFFLINE) {
97210394SMichael.Corcoran@Sun.COM 			mutex_enter(&(DEVI(dip)->devi_lock));
97310394SMichael.Corcoran@Sun.COM 			DEVI_SET_DEVICE_OFFLINE(dip);
97410394SMichael.Corcoran@Sun.COM 			mutex_exit(&(DEVI(dip)->devi_lock));
97510394SMichael.Corcoran@Sun.COM 		}
97610394SMichael.Corcoran@Sun.COM 
97710394SMichael.Corcoran@Sun.COM 		/* Mark status */
97810394SMichael.Corcoran@Sun.COM 		infop->awi_flags |= ACPIDEV_WI_DEVICE_CREATED;
97910394SMichael.Corcoran@Sun.COM 		datap->aod_iflag |= ACPIDEV_ODF_DEVINFO_CREATED;
98010394SMichael.Corcoran@Sun.COM 		datap->aod_dip = dip;
98110394SMichael.Corcoran@Sun.COM 		datap->aod_class = clsp;
98210394SMichael.Corcoran@Sun.COM 		/* Hold reference count on class driver. */
98310394SMichael.Corcoran@Sun.COM 		atomic_inc_32(&clsp->adc_refcnt);
98410394SMichael.Corcoran@Sun.COM 		if ((flags & ACPIDEV_PROCESS_FLAG_NOTAG) == 0) {
98510394SMichael.Corcoran@Sun.COM 			datap->aod_iflag |= ACPIDEV_ODF_DEVINFO_TAGGED;
98610394SMichael.Corcoran@Sun.COM 		}
98710394SMichael.Corcoran@Sun.COM 
98810394SMichael.Corcoran@Sun.COM 		/* Bind device driver. */
98910394SMichael.Corcoran@Sun.COM 		if ((flags & ACPIDEV_PROCESS_FLAG_NOBIND) != 0) {
99010394SMichael.Corcoran@Sun.COM 			mutex_enter(&(DEVI(dip)->devi_lock));
99110394SMichael.Corcoran@Sun.COM 			DEVI(dip)->devi_flags |= DEVI_NO_BIND;
99210394SMichael.Corcoran@Sun.COM 			mutex_exit(&(DEVI(dip)->devi_lock));
99310394SMichael.Corcoran@Sun.COM 		} else {
99410394SMichael.Corcoran@Sun.COM 			(void) ndi_devi_bind_driver(dip, 0);
99510394SMichael.Corcoran@Sun.COM 		}
996*12004Sjiang.liu@intel.com 
997*12004Sjiang.liu@intel.com 		/* Hold reference on branch when hot-adding devices. */
998*12004Sjiang.liu@intel.com 		if (flags & ACPIDEV_PROCESS_FLAG_HOLDBRANCH) {
999*12004Sjiang.liu@intel.com 			e_ddi_branch_hold(dip);
1000*12004Sjiang.liu@intel.com 		}
100110394SMichael.Corcoran@Sun.COM 	}
100210394SMichael.Corcoran@Sun.COM 
100310394SMichael.Corcoran@Sun.COM 	/* Free resources */
100410394SMichael.Corcoran@Sun.COM 	kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1);
100510394SMichael.Corcoran@Sun.COM 	rc = AE_OK;
100610394SMichael.Corcoran@Sun.COM 
100710394SMichael.Corcoran@Sun.COM 	/* Recursively scan child objects if requested. */
100810394SMichael.Corcoran@Sun.COM 	switch (res) {
100910394SMichael.Corcoran@Sun.COM 	case ACPIDEV_FILTER_DEFAULT:
101010394SMichael.Corcoran@Sun.COM 		/* FALLTHROUGH */
101110394SMichael.Corcoran@Sun.COM 	case ACPIDEV_FILTER_SCAN:
101210394SMichael.Corcoran@Sun.COM 		/* Check if we need to scan child. */
101310394SMichael.Corcoran@Sun.COM 		if ((flags & ACPIDEV_PROCESS_FLAG_SCAN) &&
101410394SMichael.Corcoran@Sun.COM 		    !(infop->awi_flags & ACPIDEV_WI_DISABLE_SCAN) &&
101510394SMichael.Corcoran@Sun.COM 		    !(infop->awi_flags & ACPIDEV_WI_CHILD_SCANNED)) {
101610394SMichael.Corcoran@Sun.COM 			/* probe child object. */
101710394SMichael.Corcoran@Sun.COM 			rc = acpidev_probe_child(infop);
101810394SMichael.Corcoran@Sun.COM 			if (ACPI_FAILURE(rc)) {
101910394SMichael.Corcoran@Sun.COM 				ACPIDEV_DEBUG(CE_WARN,
1020*12004Sjiang.liu@intel.com 				    "!acpidev: failed to probe subtree of %s.",
102110394SMichael.Corcoran@Sun.COM 				    infop->awi_name);
102210394SMichael.Corcoran@Sun.COM 				rc = AE_ERROR;
102310394SMichael.Corcoran@Sun.COM 			}
102410394SMichael.Corcoran@Sun.COM 			/* Mark object as scanned. */
102510394SMichael.Corcoran@Sun.COM 			infop->awi_flags |= ACPIDEV_WI_CHILD_SCANNED;
102610394SMichael.Corcoran@Sun.COM 		}
102710394SMichael.Corcoran@Sun.COM 		break;
102810394SMichael.Corcoran@Sun.COM 
102910394SMichael.Corcoran@Sun.COM 	case ACPIDEV_FILTER_CREATE:
103010394SMichael.Corcoran@Sun.COM 		/* FALLTHROUGH */
103110394SMichael.Corcoran@Sun.COM 	case ACPIDEV_FILTER_CONTINUE:
103210394SMichael.Corcoran@Sun.COM 		/* FALLTHROUGH */
103310394SMichael.Corcoran@Sun.COM 	case ACPIDEV_FILTER_SKIP:
103410394SMichael.Corcoran@Sun.COM 		break;
103510394SMichael.Corcoran@Sun.COM 
103610394SMichael.Corcoran@Sun.COM 	case ACPIDEV_FILTER_FAILED:
103710394SMichael.Corcoran@Sun.COM 		ACPIDEV_DEBUG(CE_WARN,
1038*12004Sjiang.liu@intel.com 		    "!acpidev: failed to probe device for %s.",
103910394SMichael.Corcoran@Sun.COM 		    infop->awi_name);
104010394SMichael.Corcoran@Sun.COM 		rc = AE_ERROR;
104110394SMichael.Corcoran@Sun.COM 		break;
104210394SMichael.Corcoran@Sun.COM 
104310394SMichael.Corcoran@Sun.COM 	default:
104410394SMichael.Corcoran@Sun.COM 		cmn_err(CE_WARN,
104510394SMichael.Corcoran@Sun.COM 		    "!acpidev: unknown filter result code %d.", res);
104610394SMichael.Corcoran@Sun.COM 		rc = AE_ERROR;
104710394SMichael.Corcoran@Sun.COM 		break;
104810394SMichael.Corcoran@Sun.COM 	}
104910394SMichael.Corcoran@Sun.COM 
105010394SMichael.Corcoran@Sun.COM 	return (rc);
105110394SMichael.Corcoran@Sun.COM }
105210394SMichael.Corcoran@Sun.COM 
105310394SMichael.Corcoran@Sun.COM acpidev_filter_result_t
acpidev_filter_default(acpidev_walk_info_t * infop,ACPI_HANDLE hdl,acpidev_filter_rule_t * afrp,char * devname,int len)105410394SMichael.Corcoran@Sun.COM acpidev_filter_default(acpidev_walk_info_t *infop, ACPI_HANDLE hdl,
105510394SMichael.Corcoran@Sun.COM     acpidev_filter_rule_t *afrp, char *devname, int len)
105610394SMichael.Corcoran@Sun.COM {
1057*12004Sjiang.liu@intel.com 	_NOTE(ARGUNUSED(hdl));
1058*12004Sjiang.liu@intel.com 
105910394SMichael.Corcoran@Sun.COM 	ASSERT(afrp != NULL);
106010394SMichael.Corcoran@Sun.COM 	ASSERT(devname == NULL || len >= ACPIDEV_MAX_NAMELEN);
106110394SMichael.Corcoran@Sun.COM 	if (infop->awi_level < afrp->adf_minlvl ||
106210394SMichael.Corcoran@Sun.COM 	    infop->awi_level > afrp->adf_maxlvl) {
106310394SMichael.Corcoran@Sun.COM 		return (ACPIDEV_FILTER_CONTINUE);
106410394SMichael.Corcoran@Sun.COM 	} else if (afrp->adf_pattern != NULL &&
106510394SMichael.Corcoran@Sun.COM 	    strncmp(afrp->adf_pattern,
106610394SMichael.Corcoran@Sun.COM 	    (char *)&infop->awi_info->Name,
106710394SMichael.Corcoran@Sun.COM 	    sizeof (infop->awi_info->Name))) {
106810394SMichael.Corcoran@Sun.COM 		return (ACPIDEV_FILTER_CONTINUE);
106910394SMichael.Corcoran@Sun.COM 	}
107010394SMichael.Corcoran@Sun.COM 	if (afrp->adf_replace != NULL && devname != NULL) {
1071*12004Sjiang.liu@intel.com 		(void) strlcpy(devname, afrp->adf_replace, len);
107210394SMichael.Corcoran@Sun.COM 	}
107310394SMichael.Corcoran@Sun.COM 
107410394SMichael.Corcoran@Sun.COM 	return (afrp->adf_retcode);
107510394SMichael.Corcoran@Sun.COM }
107610394SMichael.Corcoran@Sun.COM 
107710394SMichael.Corcoran@Sun.COM 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)107810394SMichael.Corcoran@Sun.COM acpidev_filter_device(acpidev_walk_info_t *infop, ACPI_HANDLE hdl,
107910394SMichael.Corcoran@Sun.COM     acpidev_filter_rule_t *afrp, int entries, char *devname, int len)
108010394SMichael.Corcoran@Sun.COM {
108110394SMichael.Corcoran@Sun.COM 	acpidev_filter_result_t res;
108210394SMichael.Corcoran@Sun.COM 
108310394SMichael.Corcoran@Sun.COM 	/* Evaluate filtering rules. */
108410394SMichael.Corcoran@Sun.COM 	for (; entries > 0; entries--, afrp++) {
108510394SMichael.Corcoran@Sun.COM 		if (afrp->adf_filter_func != NULL) {
108610394SMichael.Corcoran@Sun.COM 			res = afrp->adf_filter_func(infop, hdl, afrp,
108710394SMichael.Corcoran@Sun.COM 			    devname, len);
108810394SMichael.Corcoran@Sun.COM 		} else {
108910394SMichael.Corcoran@Sun.COM 			res = acpidev_filter_default(infop, hdl, afrp,
109010394SMichael.Corcoran@Sun.COM 			    devname, len);
109110394SMichael.Corcoran@Sun.COM 		}
109210394SMichael.Corcoran@Sun.COM 		if (res == ACPIDEV_FILTER_DEFAULT ||
109310394SMichael.Corcoran@Sun.COM 		    res == ACPIDEV_FILTER_SCAN) {
109410394SMichael.Corcoran@Sun.COM 			infop->awi_class_list = afrp->adf_class_list;
109510394SMichael.Corcoran@Sun.COM 			break;
109610394SMichael.Corcoran@Sun.COM 		}
109710394SMichael.Corcoran@Sun.COM 	}
109810394SMichael.Corcoran@Sun.COM 
109910394SMichael.Corcoran@Sun.COM 	return (res);
110010394SMichael.Corcoran@Sun.COM }
110110394SMichael.Corcoran@Sun.COM 
110210394SMichael.Corcoran@Sun.COM dev_info_t *
acpidev_root_node(void)110310394SMichael.Corcoran@Sun.COM acpidev_root_node(void)
110410394SMichael.Corcoran@Sun.COM {
110510394SMichael.Corcoran@Sun.COM 	return (acpidev_root_dip);
110610394SMichael.Corcoran@Sun.COM }
110710394SMichael.Corcoran@Sun.COM 
110810394SMichael.Corcoran@Sun.COM ACPI_STATUS
acpidev_register_class(acpidev_class_list_t ** listpp,acpidev_class_t * clsp,boolean_t tail)110910394SMichael.Corcoran@Sun.COM acpidev_register_class(acpidev_class_list_t **listpp, acpidev_class_t *clsp,
111010394SMichael.Corcoran@Sun.COM     boolean_t tail)
111110394SMichael.Corcoran@Sun.COM {
111210394SMichael.Corcoran@Sun.COM 	ACPI_STATUS rc;
111310394SMichael.Corcoran@Sun.COM 	acpidev_class_list_t *item;
111410394SMichael.Corcoran@Sun.COM 	acpidev_class_list_t *temp;
111510394SMichael.Corcoran@Sun.COM 
111610394SMichael.Corcoran@Sun.COM 	ASSERT(clsp != NULL);
111710394SMichael.Corcoran@Sun.COM 	ASSERT(listpp != NULL);
111810394SMichael.Corcoran@Sun.COM 	if (listpp == NULL || clsp == NULL) {
111910394SMichael.Corcoran@Sun.COM 		ACPIDEV_DEBUG(CE_WARN,
1120*12004Sjiang.liu@intel.com 		    "!acpidev: invalid parameter in acpidev_register_class().");
112110394SMichael.Corcoran@Sun.COM 		return (AE_BAD_PARAMETER);
112210394SMichael.Corcoran@Sun.COM 	} else if (clsp->adc_version != ACPIDEV_CLASS_REV) {
112310394SMichael.Corcoran@Sun.COM 		cmn_err(CE_WARN,
112410394SMichael.Corcoran@Sun.COM 		    "!acpidev: class driver %s version mismatch.",
112510394SMichael.Corcoran@Sun.COM 		    clsp->adc_class_name);
112610394SMichael.Corcoran@Sun.COM 		return (AE_BAD_DATA);
112710394SMichael.Corcoran@Sun.COM 	}
112810394SMichael.Corcoran@Sun.COM 
112910394SMichael.Corcoran@Sun.COM 	rc = AE_OK;
113010394SMichael.Corcoran@Sun.COM 	item = kmem_zalloc(sizeof (*item), KM_SLEEP);
113110394SMichael.Corcoran@Sun.COM 	item->acl_class = clsp;
113210394SMichael.Corcoran@Sun.COM 	rw_enter(&acpidev_class_lock, RW_WRITER);
113310394SMichael.Corcoran@Sun.COM 	/* Check for duplicated item. */
113410394SMichael.Corcoran@Sun.COM 	for (temp = *listpp; temp != NULL; temp = temp->acl_next) {
113510394SMichael.Corcoran@Sun.COM 		if (temp->acl_class == clsp) {
113610394SMichael.Corcoran@Sun.COM 			cmn_err(CE_WARN,
113710394SMichael.Corcoran@Sun.COM 			    "!acpidev: register duplicate class driver %s.",
113810394SMichael.Corcoran@Sun.COM 			    clsp->adc_class_name);
113910394SMichael.Corcoran@Sun.COM 			rc = AE_ALREADY_EXISTS;
114010394SMichael.Corcoran@Sun.COM 			break;
114110394SMichael.Corcoran@Sun.COM 		}
114210394SMichael.Corcoran@Sun.COM 	}
114310394SMichael.Corcoran@Sun.COM 	if (ACPI_SUCCESS(rc)) {
114410394SMichael.Corcoran@Sun.COM 		if (tail) {
114510394SMichael.Corcoran@Sun.COM 			while (*listpp) {
114610394SMichael.Corcoran@Sun.COM 				listpp = &(*listpp)->acl_next;
114710394SMichael.Corcoran@Sun.COM 			}
114810394SMichael.Corcoran@Sun.COM 		}
114910394SMichael.Corcoran@Sun.COM 		item->acl_next = *listpp;
115010394SMichael.Corcoran@Sun.COM 		*listpp = item;
115110394SMichael.Corcoran@Sun.COM 	}
115210394SMichael.Corcoran@Sun.COM 	rw_exit(&acpidev_class_lock);
115310394SMichael.Corcoran@Sun.COM 	if (ACPI_FAILURE(rc)) {
115410394SMichael.Corcoran@Sun.COM 		kmem_free(item, sizeof (*item));
115510394SMichael.Corcoran@Sun.COM 	}
115610394SMichael.Corcoran@Sun.COM 
115710394SMichael.Corcoran@Sun.COM 	return (rc);
115810394SMichael.Corcoran@Sun.COM }
115910394SMichael.Corcoran@Sun.COM 
116010394SMichael.Corcoran@Sun.COM ACPI_STATUS
acpidev_unregister_class(acpidev_class_list_t ** listpp,acpidev_class_t * clsp)116110394SMichael.Corcoran@Sun.COM acpidev_unregister_class(acpidev_class_list_t **listpp,
116210394SMichael.Corcoran@Sun.COM     acpidev_class_t *clsp)
116310394SMichael.Corcoran@Sun.COM {
116410394SMichael.Corcoran@Sun.COM 	ACPI_STATUS rc = AE_NOT_FOUND;
116510394SMichael.Corcoran@Sun.COM 	acpidev_class_list_t *temp;
116610394SMichael.Corcoran@Sun.COM 
116710394SMichael.Corcoran@Sun.COM 	ASSERT(clsp != NULL);
116810394SMichael.Corcoran@Sun.COM 	ASSERT(listpp != NULL);
116910394SMichael.Corcoran@Sun.COM 	if (listpp == NULL || clsp == NULL) {
1170*12004Sjiang.liu@intel.com 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter "
117110394SMichael.Corcoran@Sun.COM 		    "in acpidev_unregister_class().");
117210394SMichael.Corcoran@Sun.COM 		return (AE_BAD_PARAMETER);
117310394SMichael.Corcoran@Sun.COM 	}
117410394SMichael.Corcoran@Sun.COM 
117510394SMichael.Corcoran@Sun.COM 	rw_enter(&acpidev_class_lock, RW_WRITER);
117610394SMichael.Corcoran@Sun.COM 	for (temp = NULL; *listpp; listpp = &(*listpp)->acl_next) {
117710394SMichael.Corcoran@Sun.COM 		if ((*listpp)->acl_class == clsp) {
117810394SMichael.Corcoran@Sun.COM 			temp = *listpp;
117910394SMichael.Corcoran@Sun.COM 			*listpp = (*listpp)->acl_next;
118010394SMichael.Corcoran@Sun.COM 			break;
118110394SMichael.Corcoran@Sun.COM 		}
118210394SMichael.Corcoran@Sun.COM 	}
118310394SMichael.Corcoran@Sun.COM 	if (temp == NULL) {
1184*12004Sjiang.liu@intel.com 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: class %p(%s) doesn't exist "
118510394SMichael.Corcoran@Sun.COM 		    "in acpidev_unregister_class().",
118610394SMichael.Corcoran@Sun.COM 		    (void *)clsp, clsp->adc_class_name);
118710394SMichael.Corcoran@Sun.COM 		rc = AE_NOT_FOUND;
118810394SMichael.Corcoran@Sun.COM 	} else if (temp->acl_class->adc_refcnt != 0) {
1189*12004Sjiang.liu@intel.com 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: class %p(%s) is still in use "
119010394SMichael.Corcoran@Sun.COM 		    "in acpidev_unregister_class()..",
119110394SMichael.Corcoran@Sun.COM 		    (void *)clsp, clsp->adc_class_name);
119210394SMichael.Corcoran@Sun.COM 		rc = AE_ERROR;
119310394SMichael.Corcoran@Sun.COM 	} else {
119410394SMichael.Corcoran@Sun.COM 		kmem_free(temp, sizeof (*temp));
119510394SMichael.Corcoran@Sun.COM 		rc = AE_OK;
119610394SMichael.Corcoran@Sun.COM 	}
119710394SMichael.Corcoran@Sun.COM 	rw_exit(&acpidev_class_lock);
119810394SMichael.Corcoran@Sun.COM 
119910394SMichael.Corcoran@Sun.COM 	return (rc);
120010394SMichael.Corcoran@Sun.COM }
1201