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