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