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