4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright (c) 2009-2010, Intel Corporation.
23 * All rights reserved.
27 * Platform specific device enumerator for ACPI specific devices.
28 * "x86 system devices" refers to the suite of hardware components which are
29 * common to the x86 platform and play important roles in the system
30 * architecture but can't be enumerated/discovered through industry-standard
31 * bus specifications. Examples of these x86 system devices include:
32 * * Logical processor/CPU
34 * * Non-PCI discoverable IOMMU or DMA Remapping Engine
35 * * Non-PCI discoverable IOxAPIC
36 * * Non-PCI discoverable HPET (High Precision Event Timer)
37 * * ACPI defined devices, including power button, sleep button, battery etc.
39 * X86 system devices may be discovered through BIOS/Firmware interfaces, such
40 * as SMBIOS tables, MPS tables and ACPI tables since their discovery isn't
41 * covered by any industry-standard bus specifications.
43 * In order to aid Solaris in flexibly managing x86 system devices,
44 * x86 system devices are placed into a specific firmware device
45 * subtree whose device path is '/devices/fw'.
47 * This driver populates the firmware device subtree with ACPI-discoverable
48 * system devices if possible. To achieve that, the ACPI object
49 * namespace is abstracted as ACPI virtual buses which host system devices.
50 * Another nexus driver for the ACPI virtual bus will manage all devices
53 * For more detailed information, please refer to PSARC/2009/104.
56 #include <sys/types.h>
57 #include <sys/bitmap.h>
58 #include <sys/cmn_err.h>
59 #include <sys/ddi_subrdefs.h>
60 #include <sys/errno.h>
61 #include <sys/modctl.h>
62 #include <sys/mutex.h>
64 #include <sys/obpdefs.h>
65 #include <sys/sunddi.h>
66 #include <sys/sunndi.h>
67 #include <sys/acpi/acpi.h>
68 #include <sys/acpica.h>
69 #include <sys/acpidev.h>
70 #include <sys/acpidev_dr.h>
71 #include <sys/acpidev_impl.h>
73 /* Patchable through /etc/system */
74 int acpidev_options
= 0;
75 int acpidev_debug
= 0;
77 krwlock_t acpidev_class_lock
;
78 acpidev_class_list_t
*acpidev_class_list_root
= NULL
;
79 ulong_t acpidev_object_type_mask
[BT_BITOUL(ACPI_TYPE_NS_NODE_MAX
+ 1)];
81 /* ACPI device autoconfig global status */
82 typedef enum acpidev_status
{
83 ACPIDEV_STATUS_FAILED
= -2, /* ACPI device autoconfig failed */
84 ACPIDEV_STATUS_DISABLED
= -1, /* ACPI device autoconfig disabled */
85 ACPIDEV_STATUS_UNKNOWN
= 0, /* initial status */
86 ACPIDEV_STATUS_INITIALIZED
, /* ACPI device autoconfig initialized */
87 ACPIDEV_STATUS_FIRST_PASS
, /* first probing finished */
88 ACPIDEV_STATUS_READY
/* second probing finished */
91 static acpidev_status_t acpidev_status
= ACPIDEV_STATUS_UNKNOWN
;
92 static kmutex_t acpidev_drv_lock
;
93 static dev_info_t
*acpidev_root_dip
= NULL
;
95 /* Boot time ACPI device enumerator. */
96 static void acpidev_boot_probe(int type
);
98 /* DDI module auto configuration interface */
99 extern struct mod_ops mod_miscops
;
101 static struct modlmisc modlmisc
= {
103 "ACPI device enumerator"
106 static struct modlinkage modlinkage
= {
117 if ((err
= mod_install(&modlinkage
)) == 0) {
118 bzero(acpidev_object_type_mask
,
119 sizeof (acpidev_object_type_mask
));
120 mutex_init(&acpidev_drv_lock
, NULL
, MUTEX_DRIVER
, NULL
);
121 rw_init(&acpidev_class_lock
, NULL
, RW_DEFAULT
, NULL
);
123 impl_bus_add_probe(acpidev_boot_probe
);
125 cmn_err(CE_WARN
, "!acpidev: failed to install driver.");
134 /* No support for module unload. */
139 _info(struct modinfo
*modinfop
)
141 return (mod_info(&modlinkage
, modinfop
));
144 /* Check blacklists and load platform specific driver modules. */
146 acpidev_load_plat_modules(void)
151 /* Unload platform specific driver modules. */
153 acpidev_unload_plat_modules(void)
157 /* Unregister all device class drivers from the device driver lists. */
159 acpidev_class_list_fini(void)
161 acpidev_unload_plat_modules();
163 if ((acpidev_options
& ACPIDEV_OUSER_NO_PCI
) == 0) {
164 (void) acpidev_unregister_class(&acpidev_class_list_scope
,
166 (void) acpidev_unregister_class(&acpidev_class_list_device
,
170 if ((acpidev_options
& ACPIDEV_OUSER_NO_MEM
) == 0) {
171 (void) acpidev_unregister_class(&acpidev_class_list_device
,
172 &acpidev_class_memory
);
175 if (acpidev_options
& ACPIDEV_OUSER_NO_CPU
) {
176 (void) acpidev_unregister_class(&acpidev_class_list_device
,
178 (void) acpidev_unregister_class(&acpidev_class_list_scope
,
180 (void) acpidev_unregister_class(&acpidev_class_list_root
,
184 if ((acpidev_options
& ACPIDEV_OUSER_NO_CONTAINER
) == 0) {
185 (void) acpidev_unregister_class(&acpidev_class_list_device
,
186 &acpidev_class_container
);
189 (void) acpidev_unregister_class(&acpidev_class_list_device
,
190 &acpidev_class_device
);
191 (void) acpidev_unregister_class(&acpidev_class_list_root
,
192 &acpidev_class_device
);
194 (void) acpidev_unregister_class(&acpidev_class_list_root
,
195 &acpidev_class_scope
);
198 /* Register all device class drivers onto the driver lists. */
200 acpidev_class_list_init(uint64_t *fp
)
202 ACPI_STATUS rc
= AE_OK
;
204 /* Set bit in mask for supported object types. */
205 BT_SET(acpidev_object_type_mask
, ACPI_TYPE_LOCAL_SCOPE
);
206 BT_SET(acpidev_object_type_mask
, ACPI_TYPE_DEVICE
);
209 * Register the ACPI scope class driver onto the class driver lists.
210 * Currently only ACPI scope objects under ACPI root node, such as _PR,
211 * _SB, _TZ etc, need to be handled, so only register the scope class
212 * driver onto the root list.
214 if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_root
,
215 &acpidev_class_scope
, B_FALSE
))) {
220 * Register the ACPI device class driver onto the class driver lists.
221 * The ACPI device class driver should be registered at the tail to
222 * handle all device objects which haven't been handled by other
223 * HID/CID specific device class drivers.
225 if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_root
,
226 &acpidev_class_device
, B_TRUE
))) {
227 goto error_root_device
;
229 if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_device
,
230 &acpidev_class_device
, B_TRUE
))) {
231 goto error_device_device
;
234 /* Check and register support for ACPI container device. */
235 if ((acpidev_options
& ACPIDEV_OUSER_NO_CONTAINER
) == 0) {
236 if (ACPI_FAILURE(acpidev_register_class(
237 &acpidev_class_list_device
, &acpidev_class_container
,
239 goto error_device_container
;
241 *fp
|= ACPI_DEVCFG_CONTAINER
;
244 /* Check and register support for ACPI CPU device. */
245 if ((acpidev_options
& ACPIDEV_OUSER_NO_CPU
) == 0) {
246 /* Handle ACPI CPU Device */
247 if (ACPI_FAILURE(acpidev_register_class(
248 &acpidev_class_list_device
, &acpidev_class_cpu
, B_FALSE
))) {
249 goto error_device_cpu
;
251 /* Handle ACPI Processor under _PR */
252 if (ACPI_FAILURE(acpidev_register_class(
253 &acpidev_class_list_scope
, &acpidev_class_cpu
, B_FALSE
))) {
254 goto error_scope_cpu
;
256 /* House-keeping for CPU scan */
257 if (ACPI_FAILURE(acpidev_register_class(
258 &acpidev_class_list_root
, &acpidev_class_cpu
, B_FALSE
))) {
261 BT_SET(acpidev_object_type_mask
, ACPI_TYPE_PROCESSOR
);
262 *fp
|= ACPI_DEVCFG_CPU
;
265 /* Check support of ACPI memory devices. */
266 if ((acpidev_options
& ACPIDEV_OUSER_NO_MEM
) == 0) {
268 * Register the ACPI memory class driver onto the
269 * acpidev_class_list_device list because ACPI module
270 * class driver uses that list.
272 if (ACPI_FAILURE(acpidev_register_class(
273 &acpidev_class_list_device
, &acpidev_class_memory
,
275 goto error_device_memory
;
277 *fp
|= ACPI_DEVCFG_MEMORY
;
280 /* Check support of PCI/PCIex Host Bridge devices. */
281 if ((acpidev_options
& ACPIDEV_OUSER_NO_PCI
) == 0) {
283 * Register pci/pciex class drivers onto
284 * the acpidev_class_list_device class list because ACPI
285 * module class driver uses that list.
287 if (ACPI_FAILURE(acpidev_register_class(
288 &acpidev_class_list_device
, &acpidev_class_pci
,
290 goto error_device_pci
;
294 * Register pci/pciex class drivers onto the
295 * acpidev_class_list_scope class list.
297 if (ACPI_FAILURE(acpidev_register_class(
298 &acpidev_class_list_scope
, &acpidev_class_pci
,
300 goto error_scope_pci
;
303 *fp
|= ACPI_DEVCFG_PCI
;
306 /* Check blacklist and load platform specific modules. */
307 rc
= acpidev_load_plat_modules();
308 if (ACPI_FAILURE(rc
)) {
309 ACPIDEV_DEBUG(CE_WARN
, "!acpidev: failed to check blacklist "
310 "or load pratform modules.");
317 if ((acpidev_options
& ACPIDEV_OUSER_NO_PCI
) == 0) {
318 (void) acpidev_unregister_class(&acpidev_class_list_scope
,
322 if ((acpidev_options
& ACPIDEV_OUSER_NO_PCI
) == 0) {
323 (void) acpidev_unregister_class(&acpidev_class_list_device
,
327 if ((acpidev_options
& ACPIDEV_OUSER_NO_MEM
) == 0) {
328 (void) acpidev_unregister_class(&acpidev_class_list_device
,
329 &acpidev_class_memory
);
332 if (acpidev_options
& ACPIDEV_OUSER_NO_CPU
) {
333 (void) acpidev_unregister_class(&acpidev_class_list_root
,
337 if (acpidev_options
& ACPIDEV_OUSER_NO_CPU
) {
338 (void) acpidev_unregister_class(&acpidev_class_list_scope
,
342 if (acpidev_options
& ACPIDEV_OUSER_NO_CPU
) {
343 (void) acpidev_unregister_class(&acpidev_class_list_device
,
347 if ((acpidev_options
& ACPIDEV_OUSER_NO_CONTAINER
) == 0) {
348 (void) acpidev_unregister_class(&acpidev_class_list_device
,
349 &acpidev_class_container
);
351 error_device_container
:
352 (void) acpidev_unregister_class(&acpidev_class_list_device
,
353 &acpidev_class_device
);
355 (void) acpidev_unregister_class(&acpidev_class_list_root
,
356 &acpidev_class_device
);
358 (void) acpidev_unregister_class(&acpidev_class_list_root
,
359 &acpidev_class_scope
);
361 ACPIDEV_DEBUG(CE_WARN
,
362 "!acpidev: failed to register built-in class drivers.");
369 * Called in single threaded context during boot, no protection for
373 acpidev_create_root_node(void)
375 int circ
, rv
= AE_OK
;
376 dev_info_t
*dip
= NULL
;
377 acpidev_data_handle_t objhdl
;
378 char *compatibles
[] = {
380 ACPIDEV_TYPE_ROOTNEX
,
382 ACPIDEV_TYPE_VIRTNEX
,
385 ndi_devi_enter(ddi_root_node(), &circ
);
386 ASSERT(acpidev_root_dip
== NULL
);
388 /* Query whether device node already exists. */
389 dip
= ddi_find_devinfo(ACPIDEV_NODE_NAME_ROOT
, -1, 0);
390 if (dip
!= NULL
&& ddi_get_parent(dip
) == ddi_root_node()) {
391 ndi_devi_exit(ddi_root_node(), circ
);
392 cmn_err(CE_WARN
, "!acpidev: node /devices/%s already exists, "
393 "disable driver.", ACPIDEV_NODE_NAME_ROOT
);
394 return (AE_ALREADY_EXISTS
);
397 /* Create the device node if it doesn't exist. */
398 rv
= ndi_devi_alloc(ddi_root_node(), ACPIDEV_NODE_NAME_ROOT
,
399 (pnode_t
)DEVI_SID_NODEID
, &dip
);
400 if (rv
!= NDI_SUCCESS
) {
401 ndi_devi_exit(ddi_root_node(), circ
);
402 ACPIDEV_DEBUG(CE_WARN
, "!acpidev: failed to create device node "
403 "for ACPI root with errcode %d.", rv
);
407 /* Build cross reference between dip and ACPI object. */
408 if (ACPI_FAILURE(acpica_tag_devinfo(dip
, ACPI_ROOT_OBJECT
))) {
409 (void) ddi_remove_child(dip
, 0);
410 ndi_devi_exit(ddi_root_node(), circ
);
411 ACPIDEV_DEBUG(CE_WARN
, "!acpidev: failed to tag object %s.",
412 ACPIDEV_OBJECT_NAME_SB
);
416 /* Set device properties. */
417 rv
= ndi_prop_update_string_array(DDI_DEV_T_NONE
, dip
,
418 OBP_COMPATIBLE
, ACPIDEV_ARRAY_PARAM(compatibles
));
419 if (rv
== NDI_SUCCESS
) {
420 rv
= ndi_prop_update_string(DDI_DEV_T_NONE
, dip
,
421 OBP_DEVICETYPE
, ACPIDEV_TYPE_ROOTNEX
);
423 if (rv
!= DDI_SUCCESS
) {
424 ACPIDEV_DEBUG(CE_WARN
,
425 "!acpidev: failed to set device property for /devices/%s.",
426 ACPIDEV_NODE_NAME_ROOT
);
430 /* Manually create an object handle for the root node */
431 objhdl
= acpidev_data_create_handle(ACPI_ROOT_OBJECT
);
432 if (objhdl
== NULL
) {
433 ACPIDEV_DEBUG(CE_WARN
, "!acpidev: failed to create object "
434 "handle for the root node.");
437 objhdl
->aod_level
= 0;
438 objhdl
->aod_hdl
= ACPI_ROOT_OBJECT
;
439 objhdl
->aod_dip
= dip
;
440 objhdl
->aod_class
= &acpidev_class_scope
;
441 objhdl
->aod_status
= acpidev_query_device_status(ACPI_ROOT_OBJECT
);
442 objhdl
->aod_iflag
= ACPIDEV_ODF_STATUS_VALID
|
443 ACPIDEV_ODF_DEVINFO_CREATED
| ACPIDEV_ODF_DEVINFO_TAGGED
;
445 /* Bind device driver. */
446 (void) ndi_devi_bind_driver(dip
, 0);
448 acpidev_root_dip
= dip
;
449 ndi_devi_exit(ddi_root_node(), circ
);
454 (void) acpica_untag_devinfo(dip
, ACPI_ROOT_OBJECT
);
455 (void) ddi_remove_child(dip
, 0);
456 ndi_devi_exit(ddi_root_node(), circ
);
461 acpidev_initialize(void)
465 uint64_t features
= 0;
467 /* Check whether it has already been initialized. */
468 if (acpidev_status
== ACPIDEV_STATUS_DISABLED
) {
469 cmn_err(CE_CONT
, "?acpidev: ACPI device autoconfig "
470 "disabled by user.\n");
472 } else if (acpidev_status
!= ACPIDEV_STATUS_UNKNOWN
) {
473 ACPIDEV_DEBUG(CE_NOTE
,
474 "!acpidev: initialization called more than once.");
478 /* Check whether ACPI device autoconfig has been disabled by user. */
479 rc
= ddi_prop_lookup_string(DDI_DEV_T_ANY
, ddi_root_node(),
480 DDI_PROP_DONTPASS
, "acpidev-autoconfig", &str
);
481 if (rc
== DDI_SUCCESS
) {
482 if (strcasecmp(str
, "off") == 0 || strcasecmp(str
, "no") == 0) {
483 cmn_err(CE_CONT
, "?acpidev: ACPI device autoconfig "
484 "disabled by user.\n");
486 acpidev_status
= ACPIDEV_STATUS_DISABLED
;
492 /* Initialize acpica subsystem. */
493 if (ACPI_FAILURE(acpica_init())) {
495 "!acpidev: failed to initialize acpica subsystem.");
496 acpidev_status
= ACPIDEV_STATUS_FAILED
;
500 /* Check ACPICA subsystem status. */
501 if (!acpica_get_core_feature(ACPI_FEATURE_FULL_INIT
)) {
502 cmn_err(CE_WARN
, "!acpidev: ACPICA hasn't been fully "
503 "initialized, ACPI device autoconfig will be disabled.");
504 acpidev_status
= ACPIDEV_STATUS_DISABLED
;
508 /* Converts acpidev-options from type string to int, if any */
509 if (ddi_prop_lookup_string(DDI_DEV_T_ANY
, ddi_root_node(),
510 DDI_PROP_DONTPASS
, "acpidev-options", &str
) == DDI_PROP_SUCCESS
) {
512 rc
= ddi_strtol(str
, NULL
, 0, &data
);
514 (void) e_ddi_prop_remove(DDI_DEV_T_NONE
,
515 ddi_root_node(), "acpidev-options");
516 (void) e_ddi_prop_update_int(DDI_DEV_T_NONE
,
517 ddi_root_node(), "acpidev-options", data
);
521 /* Get acpidev_options user options. */
522 acpidev_options
= ddi_prop_get_int(DDI_DEV_T_ANY
, ddi_root_node(),
523 DDI_PROP_DONTPASS
, "acpidev-options", acpidev_options
);
525 /* Check whether ACPI based DR has been disabled by user. */
526 rc
= ddi_prop_lookup_string(DDI_DEV_T_ANY
, ddi_root_node(),
527 DDI_PROP_DONTPASS
, "acpidev-dr", &str
);
528 if (rc
== DDI_SUCCESS
) {
529 if (strcasecmp(str
, "off") == 0 || strcasecmp(str
, "no") == 0) {
530 cmn_err(CE_CONT
, "?acpidev: ACPI based DR has been "
531 "disabled by user.\n");
532 acpidev_dr_enable
= 0;
537 /* Register all device class drivers. */
538 if (ACPI_FAILURE(acpidev_class_list_init(&features
))) {
540 "!acpidev: failed to initalize class driver lists.");
541 acpidev_status
= ACPIDEV_STATUS_FAILED
;
545 /* Create root node for ACPI/firmware device subtree. */
546 if (ACPI_FAILURE(acpidev_create_root_node())) {
547 cmn_err(CE_WARN
, "!acpidev: failed to create root node "
548 "for acpi device tree.");
549 acpidev_class_list_fini();
550 acpidev_status
= ACPIDEV_STATUS_FAILED
;
554 /* Notify acpica to enable ACPI device auto configuration. */
555 acpica_set_core_feature(ACPI_FEATURE_DEVCFG
);
556 acpica_set_devcfg_feature(features
);
558 ACPIDEV_DEBUG(CE_NOTE
, "!acpidev: ACPI device autoconfig initialized.");
559 acpidev_status
= ACPIDEV_STATUS_INITIALIZED
;
563 * Probe devices in ACPI namespace which can't be enumerated by other methods
567 acpidev_boot_probe_device(acpidev_op_type_t op_type
)
569 ACPI_STATUS rc
= AE_OK
;
570 acpidev_walk_info_t
*infop
;
572 ASSERT(acpidev_root_dip
!= NULL
);
573 ASSERT(op_type
== ACPIDEV_OP_BOOT_PROBE
||
574 op_type
== ACPIDEV_OP_BOOT_REPROBE
);
576 infop
= acpidev_alloc_walk_info(op_type
, 0, ACPI_ROOT_OBJECT
,
577 &acpidev_class_list_root
, NULL
);
579 ACPIDEV_DEBUG(CE_WARN
, "!acpidev: failed to allocate walk info "
580 "object in acpi_boot_probe_device().");
583 /* Enumerate ACPI devices. */
584 rc
= acpidev_probe_child(infop
);
585 if (ACPI_FAILURE(rc
)) {
586 cmn_err(CE_WARN
, "!acpidev: failed to probe child object "
587 "under ACPI root node.");
589 acpidev_free_walk_info(infop
);
595 * Platform specific device prober for ACPI virtual bus.
596 * It will be called in single-threaded environment to enumerate devices in
597 * ACPI namespace at boot time.
600 acpidev_boot_probe(int type
)
604 /* Initialize subsystem on first pass. */
605 mutex_enter(&acpidev_drv_lock
);
607 acpidev_initialize();
608 if (acpidev_status
!= ACPIDEV_STATUS_INITIALIZED
&&
609 acpidev_status
!= ACPIDEV_STATUS_DISABLED
) {
610 cmn_err(CE_WARN
, "!acpidev: driver disabled due to "
611 "initalization failure.");
615 /* Probe ACPI devices */
616 if (type
== 0 && acpidev_status
== ACPIDEV_STATUS_INITIALIZED
) {
617 rc
= acpidev_boot_probe_device(ACPIDEV_OP_BOOT_PROBE
);
618 if (ACPI_SUCCESS(rc
)) {
620 * Support of DR operations will be disabled
621 * if failed to initialize DR subsystem.
623 rc
= acpidev_dr_initialize(acpidev_root_dip
);
624 if (ACPI_FAILURE(rc
) && rc
!= AE_SUPPORT
) {
625 cmn_err(CE_CONT
, "?acpidev: failed to "
626 "initialize DR subsystem.");
628 acpidev_status
= ACPIDEV_STATUS_FIRST_PASS
;
630 ACPIDEV_DEBUG(CE_WARN
, "!acpidev: failed to probe ACPI "
631 "devices during boot.");
632 acpidev_status
= ACPIDEV_STATUS_FAILED
;
634 } else if (type
!= 0 && acpidev_status
== ACPIDEV_STATUS_FIRST_PASS
) {
635 rc
= acpidev_boot_probe_device(ACPIDEV_OP_BOOT_REPROBE
);
636 if (ACPI_SUCCESS(rc
)) {
637 acpidev_status
= ACPIDEV_STATUS_READY
;
639 ACPIDEV_DEBUG(CE_WARN
, "!acpidev: failed to reprobe "
640 "ACPI devices during boot.");
641 acpidev_status
= ACPIDEV_STATUS_FAILED
;
643 } else if (acpidev_status
!= ACPIDEV_STATUS_FAILED
&&
644 acpidev_status
!= ACPIDEV_STATUS_DISABLED
&&
645 acpidev_status
!= ACPIDEV_STATUS_READY
) {
647 "!acpidev: invalid ACPI device autoconfig global status.");
649 mutex_exit(&acpidev_drv_lock
);
653 acpidev_probe_child(acpidev_walk_info_t
*infop
)
657 ACPI_STATUS res
, rc
= AE_OK
;
659 ACPI_OBJECT_TYPE type
;
660 acpidev_class_list_t
*it
;
661 acpidev_walk_info_t
*cinfop
;
662 acpidev_data_handle_t datap
;
664 /* Validate parameter first. */
665 ASSERT(infop
!= NULL
);
667 ACPIDEV_DEBUG(CE_WARN
,
668 "!acpidev: infop is NULL in acpidev_probe_child().");
669 return (AE_BAD_PARAMETER
);
671 ASSERT(infop
->awi_level
< ACPIDEV_MAX_ENUM_LEVELS
- 1);
672 if (infop
->awi_level
>= ACPIDEV_MAX_ENUM_LEVELS
- 1) {
673 ACPIDEV_DEBUG(CE_WARN
, "!acpidev: recursive level is too deep "
674 "in acpidev_probe_child().");
675 return (AE_BAD_PARAMETER
);
677 ASSERT(infop
->awi_class_list
!= NULL
);
678 ASSERT(infop
->awi_hdl
!= NULL
);
679 ASSERT(infop
->awi_info
!= NULL
);
680 ASSERT(infop
->awi_name
!= NULL
);
681 ASSERT(infop
->awi_data
!= NULL
);
682 if (infop
->awi_class_list
== NULL
|| infop
->awi_hdl
== NULL
||
683 infop
->awi_info
== NULL
|| infop
->awi_name
== NULL
||
684 infop
->awi_data
== NULL
) {
685 ACPIDEV_DEBUG(CE_WARN
, "!acpidev: infop has NULL fields in "
686 "acpidev_probe_child().");
687 return (AE_BAD_PARAMETER
);
689 pdip
= acpidev_walk_info_get_pdip(infop
);
691 ACPIDEV_DEBUG(CE_WARN
,
692 "!acpidev: pdip is NULL in acpidev_probe_child().");
693 return (AE_BAD_PARAMETER
);
696 ndi_devi_enter(pdip
, &circ
);
697 rw_enter(&acpidev_class_lock
, RW_READER
);
699 /* Call pre-probe callback functions. */
700 for (it
= *(infop
->awi_class_list
); it
!= NULL
; it
= it
->acl_next
) {
701 if (it
->acl_class
->adc_pre_probe
== NULL
) {
704 infop
->awi_class_curr
= it
->acl_class
;
705 if (ACPI_FAILURE(it
->acl_class
->adc_pre_probe(infop
))) {
706 ACPIDEV_DEBUG(CE_WARN
, "!acpidev: failed to pre-probe "
707 "device of type %s under %s.",
708 it
->acl_class
->adc_class_name
, infop
->awi_name
);
712 /* Walk child objects. */
714 while (ACPI_SUCCESS(AcpiGetNextObject(ACPI_TYPE_ANY
,
715 infop
->awi_hdl
, child
, &child
))) {
716 /* Skip object if we're not interested in it. */
717 if (ACPI_FAILURE(AcpiGetType(child
, &type
)) ||
718 type
> ACPI_TYPE_NS_NODE_MAX
||
719 BT_TEST(acpidev_object_type_mask
, type
) == 0) {
723 /* It's another hotplug-capable board, skip it. */
724 if (infop
->awi_op_type
== ACPIDEV_OP_HOTPLUG_PROBE
&&
725 acpidev_dr_device_is_board(child
)) {
729 /* Allocate the walk info structure. */
730 cinfop
= acpidev_alloc_walk_info(infop
->awi_op_type
,
731 infop
->awi_level
+ 1, child
, NULL
, infop
);
732 if (cinfop
== NULL
) {
733 ACPIDEV_DEBUG(CE_WARN
, "!acpidev: failed to allocate "
734 "walk info child object of %s.",
736 /* Mark error and continue to handle next child. */
742 * Remember the class list used to handle this object.
743 * It should be the same list for different passes of scans.
745 ASSERT(cinfop
->awi_data
!= NULL
);
746 datap
= cinfop
->awi_data
;
747 if (cinfop
->awi_op_type
== ACPIDEV_OP_BOOT_PROBE
) {
748 datap
->aod_class_list
= infop
->awi_class_list
;
751 /* Call registered process callbacks. */
752 for (it
= *(infop
->awi_class_list
); it
!= NULL
;
754 if (it
->acl_class
->adc_probe
== NULL
) {
757 cinfop
->awi_class_curr
= it
->acl_class
;
758 res
= it
->acl_class
->adc_probe(cinfop
);
759 if (ACPI_FAILURE(res
)) {
761 ACPIDEV_DEBUG(CE_WARN
, "!acpidev: failed to "
762 "process object of type %s under %s.",
763 it
->acl_class
->adc_class_name
,
768 /* Free resources. */
769 acpidev_free_walk_info(cinfop
);
772 /* Call post-probe callback functions. */
773 for (it
= *(infop
->awi_class_list
); it
!= NULL
; it
= it
->acl_next
) {
774 if (it
->acl_class
->adc_post_probe
== NULL
) {
777 infop
->awi_class_curr
= it
->acl_class
;
778 if (ACPI_FAILURE(it
->acl_class
->adc_post_probe(infop
))) {
779 ACPIDEV_DEBUG(CE_WARN
, "!acpidev: failed to post-probe "
780 "device of type %s under %s.",
781 it
->acl_class
->adc_class_name
, infop
->awi_name
);
785 rw_exit(&acpidev_class_lock
);
786 ndi_devi_exit(pdip
, circ
);
792 acpidev_process_object(acpidev_walk_info_t
*infop
, int flags
)
794 ACPI_STATUS rc
= AE_OK
;
796 dev_info_t
*dip
, *pdip
;
798 ACPI_DEVICE_INFO
*adip
;
799 acpidev_class_t
*clsp
;
800 acpidev_data_handle_t datap
;
801 acpidev_filter_result_t res
;
803 /* Validate parameters first. */
804 ASSERT(infop
!= NULL
);
806 ACPIDEV_DEBUG(CE_WARN
,
807 "!acpidev: infop is NULL in acpidev_process_object().");
808 return (AE_BAD_PARAMETER
);
810 ASSERT(infop
->awi_hdl
!= NULL
);
811 ASSERT(infop
->awi_info
!= NULL
);
812 ASSERT(infop
->awi_data
!= NULL
);
813 ASSERT(infop
->awi_class_curr
!= NULL
);
814 ASSERT(infop
->awi_class_curr
->adc_filter
!= NULL
);
815 hdl
= infop
->awi_hdl
;
816 adip
= infop
->awi_info
;
817 datap
= infop
->awi_data
;
818 clsp
= infop
->awi_class_curr
;
819 if (hdl
== NULL
|| datap
== NULL
|| adip
== NULL
|| clsp
== NULL
||
820 clsp
->adc_filter
== NULL
) {
821 ACPIDEV_DEBUG(CE_WARN
, "!acpidev: infop has NULL pointer in "
822 "acpidev_process_object().");
823 return (AE_BAD_PARAMETER
);
825 pdip
= acpidev_walk_info_get_pdip(infop
);
827 ACPIDEV_DEBUG(CE_WARN
, "!acpidev: failed to get pdip for %s "
828 "in acpidev_process_object().", infop
->awi_name
);
829 return (AE_BAD_PARAMETER
);
833 * Check whether the object has already been handled.
834 * Tag and child dip pointer are used to indicate the object has been
835 * handled by the ACPI auto configure driver. It has the
837 * 1) Prevent creating dip for objects which already have a dip
838 * when reloading the ACPI auto configure driver.
839 * 2) Prevent creating multiple dips for ACPI objects with ACPI
840 * aliases. Currently ACPICA framework has no way to tell whether
841 * an object is an alias or not for some types of object. So tag
842 * is used to indicate that the object has been handled.
843 * 3) Prevent multiple class drivers from creating multiple devices for
844 * the same ACPI object.
846 if ((flags
& ACPIDEV_PROCESS_FLAG_CREATE
) &&
847 (flags
& ACPIDEV_PROCESS_FLAG_CHECK
) &&
848 !(infop
->awi_flags
& ACPIDEV_WI_DISABLE_CREATE
) &&
849 (infop
->awi_flags
& ACPIDEV_WI_DEVICE_CREATED
)) {
850 ASSERT(infop
->awi_dip
!= NULL
);
851 ACPIDEV_DEBUG(CE_NOTE
,
852 "!acpidev: device has already been created for object %s.",
854 return (AE_ALREADY_EXISTS
);
858 * Determine action according to following rules based on device
859 * status returned by _STA method. Please refer to ACPI3.0b section
861 * present functioning enabled Action
864 * 1 x 1 Create node and scan child
866 * x 1 1 Create node and scan child
868 if ((datap
->aod_iflag
& ACPIDEV_ODF_STATUS_VALID
) == 0 ||
869 (flags
& ACPIDEV_PROCESS_FLAG_SYNCSTATUS
)) {
870 if (adip
->Valid
& ACPI_VALID_STA
) {
871 datap
->aod_status
= adip
->CurrentStatus
;
873 datap
->aod_status
= acpidev_query_device_status(hdl
);
875 datap
->aod_iflag
|= ACPIDEV_ODF_STATUS_VALID
;
877 if (!acpidev_check_device_enabled(datap
->aod_status
)) {
878 ACPIDEV_DEBUG(CE_NOTE
, "!acpidev: object %s doesn't exist.",
881 * Need to scan for hotplug-capable boards even if object
882 * doesn't exist or has been disabled during the first pass.
883 * So just disable creating device node and keep on scanning.
885 if (infop
->awi_op_type
== ACPIDEV_OP_BOOT_PROBE
) {
886 flags
&= ~ACPIDEV_PROCESS_FLAG_CREATE
;
888 return (AE_NOT_EXIST
);
892 ASSERT(infop
->awi_data
!= NULL
);
893 ASSERT(infop
->awi_parent
!= NULL
);
894 ASSERT(infop
->awi_parent
->awi_data
!= NULL
);
895 if (flags
& ACPIDEV_PROCESS_FLAG_CREATE
) {
896 mutex_enter(&(DEVI(pdip
)->devi_lock
));
898 * Put the device into offline state if its parent is in
901 if (DEVI_IS_DEVICE_OFFLINE(pdip
)) {
902 flags
|= ACPIDEV_PROCESS_FLAG_OFFLINE
;
904 mutex_exit(&(DEVI(pdip
)->devi_lock
));
907 /* Evaluate filtering rules and generate device name. */
908 devname
= kmem_zalloc(ACPIDEV_MAX_NAMELEN
+ 1, KM_SLEEP
);
909 (void) memcpy(devname
, (char *)&adip
->Name
, sizeof (adip
->Name
));
910 if (flags
& ACPIDEV_PROCESS_FLAG_CREATE
) {
911 res
= clsp
->adc_filter(infop
, devname
, ACPIDEV_MAX_NAMELEN
);
913 res
= clsp
->adc_filter(infop
, NULL
, 0);
916 /* Create device if requested. */
917 if ((flags
& ACPIDEV_PROCESS_FLAG_CREATE
) &&
918 !(infop
->awi_flags
& ACPIDEV_WI_DISABLE_CREATE
) &&
919 !(infop
->awi_flags
& ACPIDEV_WI_DEVICE_CREATED
) &&
920 (res
== ACPIDEV_FILTER_DEFAULT
|| res
== ACPIDEV_FILTER_CREATE
)) {
924 * Allocate dip and set default properties.
925 * Properties can be overriden in class specific init routines.
927 ASSERT(infop
->awi_dip
== NULL
);
928 ndi_devi_alloc_sleep(pdip
, devname
, (pnode_t
)DEVI_SID_NODEID
,
930 infop
->awi_dip
= dip
;
931 ret
= ndi_prop_update_string(DDI_DEV_T_NONE
, dip
,
932 OBP_DEVICETYPE
, clsp
->adc_dev_type
);
933 if (ret
!= NDI_SUCCESS
) {
934 ACPIDEV_DEBUG(CE_WARN
,
935 "!acpidev: failed to set device property for %s.",
937 (void) ddi_remove_child(dip
, 0);
938 infop
->awi_dip
= NULL
;
939 kmem_free(devname
, ACPIDEV_MAX_NAMELEN
+ 1);
943 /* Build cross reference between dip and ACPI object. */
944 if ((flags
& ACPIDEV_PROCESS_FLAG_NOTAG
) == 0 &&
945 ACPI_FAILURE(acpica_tag_devinfo(dip
, hdl
))) {
947 "!acpidev: failed to tag object %s.",
949 (void) ddi_remove_child(dip
, 0);
950 infop
->awi_dip
= NULL
;
951 kmem_free(devname
, ACPIDEV_MAX_NAMELEN
+ 1);
955 /* Call class specific initialization callback. */
956 if (clsp
->adc_init
!= NULL
&&
957 ACPI_FAILURE(clsp
->adc_init(infop
))) {
958 ACPIDEV_DEBUG(CE_WARN
,
959 "!acpidev: failed to initialize device %s.",
961 if ((flags
& ACPIDEV_PROCESS_FLAG_NOTAG
) == 0) {
962 (void) acpica_untag_devinfo(dip
, hdl
);
964 (void) ddi_remove_child(dip
, 0);
965 infop
->awi_dip
= NULL
;
966 kmem_free(devname
, ACPIDEV_MAX_NAMELEN
+ 1);
970 /* Set device into offline state if requested. */
971 if (flags
& ACPIDEV_PROCESS_FLAG_OFFLINE
) {
972 mutex_enter(&(DEVI(dip
)->devi_lock
));
973 DEVI_SET_DEVICE_OFFLINE(dip
);
974 mutex_exit(&(DEVI(dip
)->devi_lock
));
978 infop
->awi_flags
|= ACPIDEV_WI_DEVICE_CREATED
;
979 datap
->aod_iflag
|= ACPIDEV_ODF_DEVINFO_CREATED
;
980 datap
->aod_dip
= dip
;
981 datap
->aod_class
= clsp
;
982 /* Hold reference count on class driver. */
983 atomic_inc_32(&clsp
->adc_refcnt
);
984 if ((flags
& ACPIDEV_PROCESS_FLAG_NOTAG
) == 0) {
985 datap
->aod_iflag
|= ACPIDEV_ODF_DEVINFO_TAGGED
;
988 /* Bind device driver. */
989 if ((flags
& ACPIDEV_PROCESS_FLAG_NOBIND
) != 0) {
990 mutex_enter(&(DEVI(dip
)->devi_lock
));
991 DEVI(dip
)->devi_flags
|= DEVI_NO_BIND
;
992 mutex_exit(&(DEVI(dip
)->devi_lock
));
994 (void) ndi_devi_bind_driver(dip
, 0);
997 /* Hold reference on branch when hot-adding devices. */
998 if (flags
& ACPIDEV_PROCESS_FLAG_HOLDBRANCH
) {
999 e_ddi_branch_hold(dip
);
1003 /* Free resources */
1004 kmem_free(devname
, ACPIDEV_MAX_NAMELEN
+ 1);
1007 /* Recursively scan child objects if requested. */
1009 case ACPIDEV_FILTER_DEFAULT
:
1011 case ACPIDEV_FILTER_SCAN
:
1012 /* Check if we need to scan child. */
1013 if ((flags
& ACPIDEV_PROCESS_FLAG_SCAN
) &&
1014 !(infop
->awi_flags
& ACPIDEV_WI_DISABLE_SCAN
) &&
1015 !(infop
->awi_flags
& ACPIDEV_WI_CHILD_SCANNED
)) {
1016 /* probe child object. */
1017 rc
= acpidev_probe_child(infop
);
1018 if (ACPI_FAILURE(rc
)) {
1019 ACPIDEV_DEBUG(CE_WARN
,
1020 "!acpidev: failed to probe subtree of %s.",
1024 /* Mark object as scanned. */
1025 infop
->awi_flags
|= ACPIDEV_WI_CHILD_SCANNED
;
1029 case ACPIDEV_FILTER_CREATE
:
1031 case ACPIDEV_FILTER_CONTINUE
:
1033 case ACPIDEV_FILTER_SKIP
:
1036 case ACPIDEV_FILTER_FAILED
:
1037 ACPIDEV_DEBUG(CE_WARN
,
1038 "!acpidev: failed to probe device for %s.",
1045 "!acpidev: unknown filter result code %d.", res
);
1053 acpidev_filter_result_t
1054 acpidev_filter_default(acpidev_walk_info_t
*infop
, ACPI_HANDLE hdl
,
1055 acpidev_filter_rule_t
*afrp
, char *devname
, int len
)
1057 _NOTE(ARGUNUSED(hdl
));
1059 ASSERT(afrp
!= NULL
);
1060 ASSERT(devname
== NULL
|| len
>= ACPIDEV_MAX_NAMELEN
);
1061 if (infop
->awi_level
< afrp
->adf_minlvl
||
1062 infop
->awi_level
> afrp
->adf_maxlvl
) {
1063 return (ACPIDEV_FILTER_CONTINUE
);
1064 } else if (afrp
->adf_pattern
!= NULL
&&
1065 strncmp(afrp
->adf_pattern
,
1066 (char *)&infop
->awi_info
->Name
,
1067 sizeof (infop
->awi_info
->Name
))) {
1068 return (ACPIDEV_FILTER_CONTINUE
);
1070 if (afrp
->adf_replace
!= NULL
&& devname
!= NULL
) {
1071 (void) strlcpy(devname
, afrp
->adf_replace
, len
);
1074 return (afrp
->adf_retcode
);
1077 acpidev_filter_result_t
1078 acpidev_filter_device(acpidev_walk_info_t
*infop
, ACPI_HANDLE hdl
,
1079 acpidev_filter_rule_t
*afrp
, int entries
, char *devname
, int len
)
1081 acpidev_filter_result_t res
;
1083 /* Evaluate filtering rules. */
1084 for (; entries
> 0; entries
--, afrp
++) {
1085 if (afrp
->adf_filter_func
!= NULL
) {
1086 res
= afrp
->adf_filter_func(infop
, hdl
, afrp
,
1089 res
= acpidev_filter_default(infop
, hdl
, afrp
,
1092 if (res
== ACPIDEV_FILTER_DEFAULT
||
1093 res
== ACPIDEV_FILTER_SCAN
) {
1094 infop
->awi_class_list
= afrp
->adf_class_list
;
1103 acpidev_root_node(void)
1105 return (acpidev_root_dip
);
1109 acpidev_register_class(acpidev_class_list_t
**listpp
, acpidev_class_t
*clsp
,
1113 acpidev_class_list_t
*item
;
1114 acpidev_class_list_t
*temp
;
1116 ASSERT(clsp
!= NULL
);
1117 ASSERT(listpp
!= NULL
);
1118 if (listpp
== NULL
|| clsp
== NULL
) {
1119 ACPIDEV_DEBUG(CE_WARN
,
1120 "!acpidev: invalid parameter in acpidev_register_class().");
1121 return (AE_BAD_PARAMETER
);
1122 } else if (clsp
->adc_version
!= ACPIDEV_CLASS_REV
) {
1124 "!acpidev: class driver %s version mismatch.",
1125 clsp
->adc_class_name
);
1126 return (AE_BAD_DATA
);
1130 item
= kmem_zalloc(sizeof (*item
), KM_SLEEP
);
1131 item
->acl_class
= clsp
;
1132 rw_enter(&acpidev_class_lock
, RW_WRITER
);
1133 /* Check for duplicated item. */
1134 for (temp
= *listpp
; temp
!= NULL
; temp
= temp
->acl_next
) {
1135 if (temp
->acl_class
== clsp
) {
1137 "!acpidev: register duplicate class driver %s.",
1138 clsp
->adc_class_name
);
1139 rc
= AE_ALREADY_EXISTS
;
1143 if (ACPI_SUCCESS(rc
)) {
1146 listpp
= &(*listpp
)->acl_next
;
1149 item
->acl_next
= *listpp
;
1152 rw_exit(&acpidev_class_lock
);
1153 if (ACPI_FAILURE(rc
)) {
1154 kmem_free(item
, sizeof (*item
));
1161 acpidev_unregister_class(acpidev_class_list_t
**listpp
,
1162 acpidev_class_t
*clsp
)
1164 ACPI_STATUS rc
= AE_NOT_FOUND
;
1165 acpidev_class_list_t
*temp
;
1167 ASSERT(clsp
!= NULL
);
1168 ASSERT(listpp
!= NULL
);
1169 if (listpp
== NULL
|| clsp
== NULL
) {
1170 ACPIDEV_DEBUG(CE_WARN
, "!acpidev: invalid parameter "
1171 "in acpidev_unregister_class().");
1172 return (AE_BAD_PARAMETER
);
1175 rw_enter(&acpidev_class_lock
, RW_WRITER
);
1176 for (temp
= NULL
; *listpp
; listpp
= &(*listpp
)->acl_next
) {
1177 if ((*listpp
)->acl_class
== clsp
) {
1179 *listpp
= (*listpp
)->acl_next
;
1184 ACPIDEV_DEBUG(CE_WARN
, "!acpidev: class %p(%s) doesn't exist "
1185 "in acpidev_unregister_class().",
1186 (void *)clsp
, clsp
->adc_class_name
);
1188 } else if (temp
->acl_class
->adc_refcnt
!= 0) {
1189 ACPIDEV_DEBUG(CE_WARN
, "!acpidev: class %p(%s) is still in use "
1190 "in acpidev_unregister_class()..",
1191 (void *)clsp
, clsp
->adc_class_name
);
1194 kmem_free(temp
, sizeof (*temp
));
1197 rw_exit(&acpidev_class_lock
);