xref: /illumos-gate/usr/src/uts/intel/io/acpica/acpi_enum.c (revision b6805bf78d2bbbeeaea8909a05623587b42d58b3)
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) 2012 Gary Mills
23  *
24  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 /*
29  * ACPI enumerator
30  */
31 
32 #include <sys/ddi.h>
33 #include <sys/sunddi.h>
34 #include <sys/sunndi.h>
35 #include <sys/note.h>
36 #include <sys/acpi/acpi.h>
37 #include <sys/acpica.h>
38 #include <util/sscanf.h>
39 
40 
41 static char keyboard_alias[] = "keyboard";
42 static char mouse_alias[] = "mouse";
43 #define	ACPI_ENUM_DEBUG		"acpi_enum_debug"
44 #define	PARSE_RESOURCES_DEBUG	0x0001
45 #define	MASTER_LOOKUP_DEBUG	0x0002
46 #define	DEVICES_NOT_ENUMED	0x0004
47 #define	PARSE_RES_IRQ		0x0008
48 #define	PARSE_RES_DMA		0x0010
49 #define	PARSE_RES_MEMORY	0x0020
50 #define	PARSE_RES_IO		0x0040
51 #define	PARSE_RES_ADDRESS	0x0080
52 #define	ISA_DEVICE_ENUM		0x1000
53 #define	PROCESS_CIDS		0x2000
54 static unsigned long acpi_enum_debug = 0x00;
55 
56 static char USED_RESOURCES[] = "used-resources";
57 static dev_info_t *usedrdip = NULL;
58 static unsigned short used_interrupts = 0;
59 static unsigned short used_dmas = 0;
60 typedef struct used_io_mem {
61 	unsigned int start_addr;
62 	unsigned int length;
63 	struct used_io_mem *next;
64 } used_io_mem_t;
65 static used_io_mem_t *used_io_head = NULL;
66 static used_io_mem_t *used_mem_head = NULL;
67 static int used_io_count = 0;
68 static int used_mem_count = 0;
69 
70 #define	MAX_PARSED_ACPI_RESOURCES	255
71 #define	ACPI_ISA_LIMIT	16
72 static int interrupt[ACPI_ISA_LIMIT], dma[ACPI_ISA_LIMIT];
73 #define	ACPI_ELEMENT_PACKAGE_LIMIT	32
74 #define	EISA_ID_SIZE	7
75 
76 /*
77  * insert used io/mem in increasing order
78  */
79 static void
80 insert_used_resource(used_io_mem_t *used, int *used_count, used_io_mem_t **head)
81 {
82 	used_io_mem_t *curr, *prev;
83 
84 	(*used_count)++;
85 	if (*head == NULL) {
86 		*head = used;
87 		return;
88 	}
89 	curr = prev = *head;
90 	/* find a place to insert */
91 	while ((curr != NULL) &&
92 	    (curr->start_addr < used->start_addr)) {
93 		prev = curr;
94 		curr = curr->next;
95 	}
96 	if (prev == curr) {
97 		/* head */
98 		*head = used;
99 		used->next = curr;
100 		return;
101 	} else {
102 		prev->next = used;
103 	}
104 	used->next = curr;
105 }
106 
107 static void
108 add_used_io_mem(struct regspec *io, int io_count)
109 {
110 	int i;
111 	used_io_mem_t *used;
112 
113 	for (i = 0; i < io_count; i++) {
114 		used = (used_io_mem_t *)kmem_zalloc(sizeof (used_io_mem_t),
115 		    KM_SLEEP);
116 		used->start_addr = io[i].regspec_addr;
117 		used->length = io[i].regspec_size;
118 		if (io[i].regspec_bustype == 1) {
119 			insert_used_resource(used, &used_io_count,
120 			    &used_io_head);
121 		} else {
122 			insert_used_resource(used, &used_mem_count,
123 			    &used_mem_head);
124 		}
125 	}
126 }
127 
128 static void
129 parse_resources_irq(ACPI_RESOURCE *resource_ptr, int *interrupt_count)
130 {
131 	int i;
132 
133 	for (i = 0; i < resource_ptr->Data.Irq.InterruptCount; i++) {
134 		interrupt[(*interrupt_count)++] =
135 		    resource_ptr->Data.Irq.Interrupts[i];
136 		used_interrupts |= 1 << resource_ptr->Data.Irq.Interrupts[i];
137 		if (acpi_enum_debug & PARSE_RES_IRQ) {
138 			cmn_err(CE_NOTE, "parse_resources() "\
139 			    "IRQ num %u, intr # = %u",
140 			    i, resource_ptr->Data.Irq.Interrupts[i]);
141 		}
142 	}
143 }
144 
145 static void
146 parse_resources_dma(ACPI_RESOURCE *resource_ptr, int *dma_count)
147 {
148 	int i;
149 
150 	for (i = 0; i < resource_ptr->Data.Dma.ChannelCount; i++) {
151 		dma[(*dma_count)++] = resource_ptr->Data.Dma.Channels[i];
152 		used_dmas |= 1 << resource_ptr->Data.Dma.Channels[i];
153 		if (acpi_enum_debug & PARSE_RES_DMA) {
154 			cmn_err(CE_NOTE, "parse_resources() "\
155 			    "DMA num %u, channel # = %u",
156 			    i, resource_ptr->Data.Dma.Channels[i]);
157 		}
158 	}
159 }
160 
161 static void
162 parse_resources_io(ACPI_RESOURCE *resource_ptr, struct regspec *io,
163     int *io_count)
164 {
165 	ACPI_RESOURCE_IO acpi_io = resource_ptr->Data.Io;
166 
167 	if (acpi_io.AddressLength == 0)
168 		return;
169 
170 	io[*io_count].regspec_bustype = 1; /* io */
171 	io[*io_count].regspec_size = acpi_io.AddressLength;
172 	io[*io_count].regspec_addr = acpi_io.Minimum;
173 	if (acpi_enum_debug & PARSE_RES_IO) {
174 		cmn_err(CE_NOTE, "parse_resources() "\
175 		    "IO min 0x%X, max 0x%X, length: 0x%X",
176 		    acpi_io.Minimum,
177 		    acpi_io.Maximum,
178 		    acpi_io.AddressLength);
179 	}
180 	(*io_count)++;
181 }
182 
183 static void
184 parse_resources_fixed_io(ACPI_RESOURCE *resource_ptr, struct regspec *io,
185     int *io_count)
186 {
187 	ACPI_RESOURCE_FIXED_IO fixed_io = resource_ptr->Data.FixedIo;
188 
189 	if (fixed_io.AddressLength == 0)
190 		return;
191 
192 	io[*io_count].regspec_bustype = 1; /* io */
193 	io[*io_count].regspec_addr = fixed_io.Address;
194 	io[*io_count].regspec_size = fixed_io.AddressLength;
195 	if (acpi_enum_debug & PARSE_RES_IO) {
196 		cmn_err(CE_NOTE, "parse_resources() "\
197 		    "Fixed IO 0x%X, length: 0x%X",
198 		    fixed_io.Address, fixed_io.AddressLength);
199 	}
200 	(*io_count)++;
201 }
202 
203 static void
204 parse_resources_fixed_mem32(ACPI_RESOURCE *resource_ptr, struct regspec *io,
205     int *io_count)
206 {
207 	ACPI_RESOURCE_FIXED_MEMORY32 fixed_mem32 =
208 	    resource_ptr->Data.FixedMemory32;
209 
210 	if (fixed_mem32.AddressLength == 0)
211 		return;
212 
213 	io[*io_count].regspec_bustype = 0; /* memory */
214 	io[*io_count].regspec_addr = fixed_mem32.Address;
215 	io[*io_count].regspec_size = fixed_mem32.AddressLength;
216 	if (acpi_enum_debug & PARSE_RES_MEMORY) {
217 		cmn_err(CE_NOTE, "parse_resources() "\
218 		    "Fixed Mem 32 %ul, length: %ul",
219 		    fixed_mem32.Address, fixed_mem32.AddressLength);
220 	}
221 	(*io_count)++;
222 }
223 
224 static void
225 parse_resources_mem32(ACPI_RESOURCE *resource_ptr, struct regspec *io,
226     int *io_count)
227 {
228 	ACPI_RESOURCE_MEMORY32 mem32 = resource_ptr->Data.Memory32;
229 
230 	if (mem32.AddressLength == 0)
231 		return;
232 
233 	if (resource_ptr->Data.Memory32.Minimum ==
234 	    resource_ptr->Data.Memory32.Maximum) {
235 		io[*io_count].regspec_bustype = 0; /* memory */
236 		io[*io_count].regspec_addr = mem32.Minimum;
237 		io[*io_count].regspec_size = mem32.AddressLength;
238 		(*io_count)++;
239 		if (acpi_enum_debug & PARSE_RES_MEMORY) {
240 			cmn_err(CE_NOTE, "parse_resources() "\
241 			    "Mem 32 0x%X, length: 0x%X",
242 			    mem32.Minimum, mem32.AddressLength);
243 		}
244 		return;
245 	}
246 	if (acpi_enum_debug & PARSE_RES_MEMORY) {
247 		cmn_err(CE_NOTE, "parse_resources() "\
248 		    "MEM32 Min Max not equal!");
249 		cmn_err(CE_NOTE, "parse_resources() "\
250 		    "Mem 32 Minimum 0x%X, Maximum: 0x%X",
251 		    mem32.Minimum, mem32.Maximum);
252 	}
253 }
254 
255 static void
256 parse_resources_addr16(ACPI_RESOURCE *resource_ptr, struct regspec *io,
257     int *io_count)
258 {
259 	ACPI_RESOURCE_ADDRESS16 addr16 =
260 	    resource_ptr->Data.Address16;
261 
262 	if (addr16.AddressLength == 0)
263 		return;
264 
265 	if (acpi_enum_debug & PARSE_RES_ADDRESS) {
266 		if (addr16.ResourceType == ACPI_MEMORY_RANGE) {
267 			cmn_err(CE_NOTE, "parse_resources() "\
268 			    "ADDRESS 16 MEMORY RANGE");
269 		} else
270 		if (addr16.ResourceType == ACPI_IO_RANGE) {
271 			cmn_err(CE_NOTE, "parse_resources() "\
272 			    "ADDRESS 16 IO RANGE");
273 		} else {
274 			cmn_err(CE_NOTE, "parse_resources() "\
275 			    "ADDRESS 16 OTHER");
276 		}
277 		cmn_err(CE_NOTE, "parse_resources() "\
278 		    "%s "\
279 		    "MinAddressFixed 0x%X, "\
280 		    "MaxAddressFixed 0x%X, "\
281 		    "Minimum 0x%X, "\
282 		    "Maximum 0x%X, "\
283 		    "length: 0x%X\n",
284 		    addr16.ProducerConsumer == ACPI_CONSUMER ?
285 		    "CONSUMER" : "PRODUCER",
286 		    addr16.MinAddressFixed,
287 		    addr16.MaxAddressFixed,
288 		    addr16.Minimum,
289 		    addr16.Maximum,
290 		    addr16.AddressLength);
291 	}
292 	if (addr16.ProducerConsumer == ACPI_PRODUCER ||
293 	    (addr16.ResourceType != ACPI_MEMORY_RANGE &&
294 	    addr16.ResourceType != ACPI_IO_RANGE)) {
295 		return;
296 	}
297 	if (addr16.AddressLength > 0) {
298 		if (addr16.ResourceType == ACPI_MEMORY_RANGE) {
299 			/* memory */
300 			io[*io_count].regspec_bustype = 0;
301 		} else {
302 			/* io */
303 			io[*io_count].regspec_bustype = 1;
304 		}
305 		io[*io_count].regspec_addr = addr16.Minimum;
306 		io[*io_count].regspec_size = addr16.AddressLength;
307 		(*io_count)++;
308 	}
309 }
310 
311 static void
312 parse_resources_addr32(ACPI_RESOURCE *resource_ptr, struct regspec *io,
313     int *io_count)
314 {
315 	ACPI_RESOURCE_ADDRESS32 addr32 =
316 	    resource_ptr->Data.Address32;
317 
318 	if (addr32.AddressLength == 0)
319 		return;
320 
321 	if (acpi_enum_debug & PARSE_RES_ADDRESS) {
322 		if (addr32.ResourceType == ACPI_MEMORY_RANGE) {
323 			cmn_err(CE_NOTE, "parse_resources() "\
324 			    "ADDRESS 32 MEMORY RANGE");
325 		} else
326 		if (addr32.ResourceType == ACPI_IO_RANGE) {
327 			cmn_err(CE_NOTE, "parse_resources() "\
328 			    "ADDRESS 32 IO RANGE");
329 		} else {
330 			cmn_err(CE_NOTE, "parse_resources() "\
331 			    "ADDRESS 32 OTHER");
332 		}
333 		cmn_err(CE_NOTE, "parse_resources() "\
334 		    "%s "\
335 		    "MinAddressFixed 0x%X, "\
336 		    "MaxAddressFixed 0x%X, "\
337 		    "Minimum 0x%X, "\
338 		    "Maximum 0x%X, "\
339 		    "length: 0x%X\n",
340 		    addr32.ProducerConsumer == ACPI_CONSUMER ?
341 		    "CONSUMER" : "PRODUCER",
342 		    addr32.MinAddressFixed,
343 		    addr32.MaxAddressFixed,
344 		    addr32.Minimum,
345 		    addr32.Maximum,
346 		    addr32.AddressLength);
347 	}
348 	if (addr32.ProducerConsumer == ACPI_PRODUCER ||
349 	    (addr32.ResourceType != ACPI_MEMORY_RANGE &&
350 	    addr32.ResourceType != ACPI_IO_RANGE)) {
351 		return;
352 	}
353 	if (addr32.AddressLength > 0) {
354 		if (addr32.ResourceType == ACPI_MEMORY_RANGE) {
355 			/* memory */
356 			io[*io_count].regspec_bustype = 0;
357 		} else {
358 			/* io */
359 			io[*io_count].regspec_bustype = 1;
360 		}
361 		io[*io_count].regspec_addr = addr32.Minimum;
362 		io[*io_count].regspec_size = addr32.AddressLength;
363 		(*io_count)++;
364 	}
365 }
366 
367 static void
368 parse_resources_addr64(ACPI_RESOURCE *resource_ptr, struct regspec *io,
369     int *io_count)
370 {
371 	ACPI_RESOURCE_ADDRESS64 addr64 =
372 	    resource_ptr->Data.Address64;
373 
374 	if (addr64.AddressLength == 0)
375 		return;
376 
377 	if (acpi_enum_debug & PARSE_RES_ADDRESS) {
378 		if (addr64.ResourceType == ACPI_MEMORY_RANGE) {
379 			cmn_err(CE_NOTE, "parse_resources() "\
380 			    "ADDRESS 64 MEMORY RANGE");
381 		} else
382 		if (addr64.ResourceType == ACPI_IO_RANGE) {
383 			cmn_err(CE_NOTE, "parse_resources() "\
384 			    "ADDRESS 64 IO RANGE");
385 		} else {
386 			cmn_err(CE_NOTE, "parse_resources() "\
387 			    "ADDRESS 64 OTHER");
388 		}
389 #ifdef _LP64
390 		cmn_err(CE_NOTE, "parse_resources() "\
391 		    "%s "\
392 		    "MinAddressFixed 0x%X, "\
393 		    "MaxAddressFixed 0x%X, "\
394 		    "Minimum 0x%lX, "\
395 		    "Maximum 0x%lX, "\
396 		    "length: 0x%lX\n",
397 		    addr64.ProducerConsumer == ACPI_CONSUMER ?
398 		    "CONSUMER" : "PRODUCER",
399 		    addr64.MinAddressFixed,
400 		    addr64.MaxAddressFixed,
401 		    addr64.Minimum,
402 		    addr64.Maximum,
403 		    addr64.AddressLength);
404 #else
405 		cmn_err(CE_NOTE, "parse_resources() "\
406 		    "%s "\
407 		    "MinAddressFixed 0x%X, "\
408 		    "MaxAddressFixed 0x%X, "\
409 		    "Minimum 0x%llX, "\
410 		    "Maximum 0x%llX, "\
411 		    "length: 0x%llX\n",
412 		    addr64.ProducerConsumer == ACPI_CONSUMER ?
413 		    "CONSUMER" : "PRODUCER",
414 		    addr64.MinAddressFixed,
415 		    addr64.MaxAddressFixed,
416 		    addr64.Minimum,
417 		    addr64.Maximum,
418 		    addr64.AddressLength);
419 #endif
420 	}
421 	if (addr64.ProducerConsumer == ACPI_PRODUCER ||
422 	    (addr64.ResourceType != ACPI_MEMORY_RANGE &&
423 	    addr64.ResourceType != ACPI_IO_RANGE)) {
424 		return;
425 	}
426 	if (addr64.AddressLength > 0) {
427 		if (addr64.ResourceType == ACPI_MEMORY_RANGE) {
428 			/* memory */
429 			io[*io_count].regspec_bustype = 0;
430 		} else {
431 			/* io */
432 			io[*io_count].regspec_bustype = 1;
433 		}
434 		io[*io_count].regspec_addr = addr64.Minimum;
435 		io[*io_count].regspec_size = addr64.AddressLength;
436 		(*io_count)++;
437 	}
438 }
439 
440 static ACPI_STATUS
441 parse_resources(ACPI_HANDLE handle, dev_info_t *xdip, char *path)
442 {
443 	ACPI_BUFFER	buf;
444 	ACPI_RESOURCE	*resource_ptr;
445 	ACPI_STATUS	status;
446 	char		*current_ptr, *last_ptr;
447 	struct		regspec *io;
448 	int		io_count = 0, interrupt_count = 0, dma_count = 0;
449 	int		i;
450 
451 	buf.Length = ACPI_ALLOCATE_BUFFER;
452 	status = AcpiGetCurrentResources(handle, &buf);
453 	switch (status) {
454 	case AE_OK:
455 		break;
456 	case AE_NOT_FOUND:
457 		/*
458 		 * Workaround for faulty DSDT tables that omit the _CRS
459 		 * method for the UAR3 device but have a valid _PRS method
460 		 * for that device.
461 		 */
462 		status = AcpiGetPossibleResources(handle, &buf);
463 		if (status != AE_OK) {
464 			return (status);
465 		}
466 		break;
467 	default:
468 		cmn_err(CE_WARN,
469 		    "!AcpiGetCurrentResources failed for %s, exception: %s",
470 		    path, AcpiFormatException(status));
471 		return (status);
472 		break;
473 	}
474 	io = (struct regspec *)kmem_zalloc(sizeof (struct regspec) *
475 	    MAX_PARSED_ACPI_RESOURCES, KM_SLEEP);
476 	current_ptr = buf.Pointer;
477 	last_ptr = (char *)buf.Pointer + buf.Length;
478 	while (current_ptr < last_ptr) {
479 		if (io_count >= MAX_PARSED_ACPI_RESOURCES) {
480 			break;
481 		}
482 		resource_ptr = (ACPI_RESOURCE *)current_ptr;
483 		current_ptr += resource_ptr->Length;
484 		switch (resource_ptr->Type) {
485 		case ACPI_RESOURCE_TYPE_END_TAG:
486 			current_ptr = last_ptr;
487 			break;
488 		case ACPI_RESOURCE_TYPE_IO:
489 			parse_resources_io(resource_ptr, io, &io_count);
490 			break;
491 		case ACPI_RESOURCE_TYPE_FIXED_IO:
492 			parse_resources_fixed_io(resource_ptr, io, &io_count);
493 			break;
494 		case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
495 			parse_resources_fixed_mem32(resource_ptr, io,
496 			    &io_count);
497 			break;
498 		case ACPI_RESOURCE_TYPE_MEMORY32:
499 			parse_resources_mem32(resource_ptr, io, &io_count);
500 			break;
501 		case ACPI_RESOURCE_TYPE_ADDRESS16:
502 			parse_resources_addr16(resource_ptr, io, &io_count);
503 			break;
504 		case ACPI_RESOURCE_TYPE_ADDRESS32:
505 			parse_resources_addr32(resource_ptr, io, &io_count);
506 			break;
507 		case ACPI_RESOURCE_TYPE_ADDRESS64:
508 			parse_resources_addr64(resource_ptr, io, &io_count);
509 			break;
510 		case ACPI_RESOURCE_TYPE_IRQ:
511 			parse_resources_irq(resource_ptr, &interrupt_count);
512 			break;
513 		case ACPI_RESOURCE_TYPE_DMA:
514 			parse_resources_dma(resource_ptr, &dma_count);
515 			break;
516 		case ACPI_RESOURCE_TYPE_START_DEPENDENT:
517 			cmn_err(CE_NOTE,
518 			    "!ACPI source type"
519 			    " ACPI_RESOURCE_TYPE_START_DEPENDENT"
520 			    " not supported");
521 			break;
522 		case ACPI_RESOURCE_TYPE_END_DEPENDENT:
523 			cmn_err(CE_NOTE,
524 			    "!ACPI source type"
525 			    " ACPI_RESOURCE_TYPE_END_DEPENDENT"
526 			    " not supported");
527 			break;
528 		case ACPI_RESOURCE_TYPE_VENDOR:
529 			cmn_err(CE_NOTE,
530 			    "!ACPI source type"
531 			    " ACPI_RESOURCE_TYPE_VENDOR"
532 			    " not supported");
533 			break;
534 		case ACPI_RESOURCE_TYPE_MEMORY24:
535 			cmn_err(CE_NOTE,
536 			    "!ACPI source type"
537 			    " ACPI_RESOURCE_TYPE_MEMORY24"
538 			    " not supported");
539 			break;
540 		case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
541 			cmn_err(CE_NOTE,
542 			    "!ACPI source type"
543 			    " ACPI_RESOURCE_TYPE_EXT_IRQ"
544 			    " not supported");
545 			break;
546 		default:
547 		/* Some types are not yet implemented (See CA 6.4) */
548 			cmn_err(CE_NOTE,
549 			    "!ACPI resource type (0X%X) not yet supported",
550 			    resource_ptr->Type);
551 			break;
552 		}
553 	}
554 
555 	if (io_count) {
556 		/*
557 		 * on LX50, you get interrupts of mouse and keyboard
558 		 * from separate PNP id...
559 		 */
560 		if (io_count == 2) {
561 			if ((io[0].regspec_addr == 0x60 &&
562 			    io[1].regspec_addr == 0x64) ||
563 			    (io[0].regspec_addr == 0x64 &&
564 			    io[1].regspec_addr == 0x60)) {
565 				interrupt[0] = 0x1;
566 				interrupt[1] = 0xc;
567 				interrupt_count = 2;
568 				used_interrupts |=
569 				    1 << interrupt[0];
570 				used_interrupts |=
571 				    1 << interrupt[1];
572 			}
573 		}
574 		add_used_io_mem(io, io_count);
575 		if (xdip != NULL) {
576 			(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip,
577 			    "reg", (int *)io, 3*io_count);
578 		}
579 	}
580 	if (interrupt_count && (xdip != NULL)) {
581 		(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip,
582 		    "interrupts", (int *)interrupt, interrupt_count);
583 	}
584 	if (dma_count && (xdip != NULL)) {
585 		(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip,
586 		    "dma-channels", (int *)dma, dma_count);
587 	}
588 	AcpiOsFree(buf.Pointer);
589 	kmem_free(io, sizeof (struct regspec) * MAX_PARSED_ACPI_RESOURCES);
590 	return (status);
591 }
592 
593 /* keyboard mouse is under i8042, everything else under isa */
594 static dev_info_t *
595 get_bus_dip(char *nodename, dev_info_t *isa_dip)
596 {
597 	static dev_info_t *i8042_dip = NULL;
598 	struct regspec i8042_regs[] = {
599 		{1, 0x60, 0x1},
600 		{1, 0x64, 0x1}
601 	};
602 	int i8042_intrs[] = {0x1, 0xc};
603 
604 	if (strcmp(nodename, keyboard_alias) != 0 &&
605 	    strcmp(nodename, mouse_alias) != 0)
606 		return (isa_dip);
607 
608 	if (i8042_dip)
609 		return (i8042_dip);
610 
611 	ndi_devi_alloc_sleep(isa_dip, "i8042", (pnode_t)DEVI_SID_NODEID,
612 	    &i8042_dip);
613 	(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, i8042_dip,
614 	    "reg", (int *)i8042_regs, 6);
615 	(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, i8042_dip,
616 	    "interrupts", (int *)i8042_intrs, 2);
617 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, i8042_dip,
618 	    "unit-address", "1,60");
619 	(void) ndi_devi_bind_driver(i8042_dip, 0);
620 	return (i8042_dip);
621 }
622 
623 /*
624  * put content of properties (if any) to dev info tree at branch xdip
625  * return non-zero if a "compatible" property was processed, zero otherwise
626  *
627  */
628 static int
629 process_properties(dev_info_t *xdip, property_t *properties)
630 {
631 	int	rv = 0;
632 
633 	while (properties != NULL) {
634 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
635 		    properties->name, properties->value);
636 		if (strcmp(properties->name, "compatible") == 0)
637 			rv = 1;
638 		properties = properties->next;
639 	}
640 
641 	return (rv);
642 }
643 
644 void
645 eisa_to_str(ACPI_INTEGER id, char *np)
646 {
647 	static const char hextab[] = "0123456789ABCDEF";
648 
649 	/*
650 	 *  Expand an EISA device name:
651 	 *
652 	 * This routine converts a 32-bit EISA device "id" to a
653 	 * 7-byte ASCII device name, which is stored at "np".
654 	 */
655 
656 	*np++ = '@' + ((id >> 2)  & 0x1F);
657 	*np++ = '@' + ((id << 3)  & 0x18) + ((id >> 13) & 0x07);
658 	*np++ = '@' + ((id >> 8)  & 0x1F);
659 	*np++ = hextab[(id >> 20) & 0x0F];
660 	*np++ = hextab[(id >> 16) & 0x0F];
661 	*np++ = hextab[(id >> 28) & 0x0F];
662 	*np++ = hextab[(id >> 24) & 0x0F];
663 	*np = 0;
664 }
665 
666 /*
667  * process_cids() -- process multiple CIDs in a package
668  */
669 static void
670 process_cids(ACPI_OBJECT *rv, device_id_t **dd)
671 {
672 	device_id_t *d;
673 	char tmp_cidstr[8];	/* 7-character EISA ID */
674 	int i;
675 
676 	if ((rv->Package.Count == 0) || rv->Package.Elements == NULL)
677 		return; /* empty package */
678 
679 	/*
680 	 * Work the package 'backwards' so the resulting list is
681 	 * in original order of preference.
682 	 */
683 	for (i = rv->Package.Count - 1; i >= 0; i--) {
684 		/* get the actual acpi_object */
685 		ACPI_OBJECT obj = rv->Package.Elements[i];
686 		switch (obj.Type) {
687 		case ACPI_TYPE_INTEGER:
688 			eisa_to_str(obj.Integer.Value, tmp_cidstr);
689 			d = mf_alloc_device_id();
690 			d->id = strdup(tmp_cidstr);
691 			d->next = *dd;
692 			*dd = d;
693 			break;
694 		case ACPI_TYPE_STRING:
695 			d = mf_alloc_device_id();
696 			d->id = strdup(obj.String.Pointer);
697 			d->next = *dd;
698 			*dd = d;
699 			break;
700 		default:
701 			if (acpi_enum_debug & PROCESS_CIDS) {
702 				cmn_err(CE_NOTE, "unexpected CID type: %d",
703 				    obj.Type);
704 			}
705 			break;
706 		}
707 	}
708 }
709 
710 /*
711  * Convert "raw" PNP and ACPI IDs to IEEE 1275-compliant form.
712  * Some liberty is taken here, treating "ACPI" as a special form
713  * of PNP vendor ID.  strsize specifies size of buffer.
714  */
715 static void
716 convert_to_pnp1275(char *pnpid, char *str, int strsize)
717 {
718 	char	vendor[5];
719 	uint_t	id;
720 
721 	if (strncmp(pnpid, "ACPI", 4) == 0) {
722 		/* Assume ACPI ID: ACPIxxxx */
723 		sscanf(pnpid, "%4s%x", vendor, &id);
724 	} else {
725 		/* Assume PNP ID: aaaxxxx */
726 		sscanf(pnpid, "%3s%x", vendor, &id);
727 	}
728 
729 	snprintf(str, strsize, "pnp%s,%x", vendor, id);
730 }
731 
732 /*
733  * Given a list of device ID elements in most-to-least-specific
734  * order, create a "compatible" property.
735  */
736 static void
737 create_compatible_property(dev_info_t *dip, device_id_t *ids)
738 {
739 	char		**strs;
740 	int		list_len, i;
741 	device_id_t	*d;
742 
743 	/* count list length */
744 	list_len = 0;
745 	d = ids;
746 	while (d != NULL) {
747 		list_len++;
748 		d = d->next;
749 	}
750 
751 	/* create string array */
752 	strs = (char **)kmem_zalloc(list_len * sizeof (char *), KM_SLEEP);
753 	i = 0;
754 	d = ids;
755 	while (d != NULL) {
756 		/* strlen("pnpXXXX,xxxx") + 1 = 13 */
757 		strs[i] = kmem_zalloc(13, KM_SLEEP);
758 		convert_to_pnp1275(d->id, strs[i++], 13);
759 		d = d->next;
760 	}
761 
762 	/* update property */
763 	(void) ndi_prop_update_string_array(DDI_DEV_T_NONE, dip,
764 	    "compatible", strs, list_len);
765 
766 
767 	/* free memory */
768 	for (i = 0; i < list_len; i++)
769 		kmem_free(strs[i], 13);
770 
771 	kmem_free(strs, list_len * sizeof (char *));
772 }
773 
774 /*
775  * isa_acpi_callback()
776  */
777 static ACPI_STATUS
778 isa_acpi_callback(ACPI_HANDLE ObjHandle, uint32_t NestingLevel, void *a,
779     void **b)
780 {
781 	_NOTE(ARGUNUSED(NestingLevel, b))
782 
783 	ACPI_BUFFER	rb;
784 	ACPI_DEVICE_INFO *info = NULL;
785 	char		*path = NULL;
786 	char		*hidstr = NULL;
787 	char		tmp_cidstr[8];	/* EISAID size */
788 	dev_info_t	*dip = (dev_info_t *)a;
789 	dev_info_t	*xdip = NULL;
790 	device_id_t	*d, *device_ids = NULL;
791 	const master_rec_t	*m;
792 	int		compatible_present = 0;
793 
794 	/*
795 	 * get full ACPI pathname for object
796 	 */
797 	rb.Length = ACPI_ALLOCATE_BUFFER;
798 	rb.Pointer = NULL;
799 	if (AcpiGetName(ObjHandle, ACPI_FULL_PATHNAME, &rb) != AE_OK) {
800 		cmn_err(CE_WARN, "!acpi_enum: could not get pathname");
801 		goto done;
802 	}
803 	path = (char *)rb.Pointer;
804 
805 	/*
806 	 * Get device info object
807 	 */
808 	if (AcpiGetObjectInfo(ObjHandle, &info) != AE_OK) {
809 		cmn_err(CE_WARN, "!acpi_enum: could not get device"
810 		    " info for %s", path);
811 		goto done;
812 	}
813 
814 	/*
815 	 * If device isn't present, we don't enumerate
816 	 * NEEDSWORK: what about docking bays and the like?
817 	 */
818 	if (info->Valid & ACPI_VALID_STA) {
819 		/*
820 		 * CA 6.3.6 _STA method
821 		 * Bit 0 -- device is present
822 		 * Bit 1 -- device is enabled
823 		 * Bit 2 -- device is shown in UI
824 		 */
825 		if (!((info->CurrentStatus & 0x7) == 7)) {
826 			if (acpi_enum_debug & DEVICES_NOT_ENUMED) {
827 				cmn_err(CE_NOTE, "parse_resources() "
828 				    "Bad status 0x%x for %s",
829 				    info->CurrentStatus, path);
830 			}
831 			goto done;
832 		}
833 	} else {
834 		cmn_err(CE_WARN, "!acpi_enum: no _STA for %s", path);
835 		goto done;
836 	}
837 
838 	/*
839 	 * Keep track of _HID value
840 	 */
841 	if (!(info->Valid & ACPI_VALID_HID)) {
842 		/* No _HID, we skip this node */
843 		if (acpi_enum_debug & DEVICES_NOT_ENUMED) {
844 			cmn_err(CE_NOTE, "parse_resources() "
845 			    "No _HID for %s", path);
846 		}
847 		goto done;
848 	}
849 	hidstr = info->HardwareId.String;
850 
851 	/*
852 	 * Attempt to get _CID value
853 	 */
854 	rb.Length = ACPI_ALLOCATE_BUFFER;
855 	rb.Pointer = NULL;
856 	if (AcpiEvaluateObject(ObjHandle, "_CID", NULL, &rb) == AE_OK &&
857 	    rb.Length != 0) {
858 		ACPI_OBJECT *rv = rb.Pointer;
859 
860 		switch (rv->Type) {
861 		case ACPI_TYPE_INTEGER:
862 			eisa_to_str(rv->Integer.Value, tmp_cidstr);
863 			d = mf_alloc_device_id();
864 			d->id = strdup(tmp_cidstr);
865 			d->next = device_ids;
866 			device_ids = d;
867 			break;
868 		case ACPI_TYPE_STRING:
869 			d = mf_alloc_device_id();
870 			d->id = strdup(rv->String.Pointer);
871 			d->next = device_ids;
872 			device_ids = d;
873 			break;
874 		case ACPI_TYPE_PACKAGE:
875 			process_cids(rv, &device_ids);
876 			break;
877 		default:
878 			break;
879 		}
880 		AcpiOsFree(rb.Pointer);
881 	}
882 
883 	/*
884 	 * Add _HID last so it's at the head of the list
885 	 */
886 	d = mf_alloc_device_id();
887 	d->id = strdup(hidstr);
888 	d->next = device_ids;
889 	device_ids = d;
890 
891 	/*
892 	 * master_file_lookup() expects _HID first in device_ids
893 	 */
894 	if ((m = master_file_lookup(device_ids)) !=  NULL) {
895 		/* PNP description found in master table */
896 		if (!(strncmp(hidstr, "ACPI", 4))) {
897 			dip = ddi_root_node();
898 		} else {
899 			dip = get_bus_dip(m->name, dip);
900 		}
901 		ndi_devi_alloc_sleep(dip, m->name,
902 		    (pnode_t)DEVI_SID_NODEID, &xdip);
903 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
904 		    "model", m->description);
905 		compatible_present = process_properties(xdip, m->properties);
906 	} else {
907 		/* for ISA devices not known to the master file */
908 		if (!(strncmp(hidstr, "PNP03", 5))) {
909 			/* a keyboard device includes PNP03xx */
910 			dip = get_bus_dip(keyboard_alias, dip);
911 			ndi_devi_alloc_sleep(dip, keyboard_alias,
912 			    (pnode_t)DEVI_SID_NODEID, &xdip);
913 			(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
914 			    "compatible", "pnpPNP,303");
915 			(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
916 			    "model", "PNP03xx keyboard");
917 		} else {
918 			if (!(strncmp(hidstr, "PNP0F", 5))) {
919 				/* a mouse device include PNP0Fxx */
920 				dip = get_bus_dip(mouse_alias, dip);
921 				ndi_devi_alloc_sleep(dip, mouse_alias,
922 				    (pnode_t)DEVI_SID_NODEID, &xdip);
923 				(void) ndi_prop_update_string(DDI_DEV_T_NONE,
924 				    xdip, "compatible", "pnpPNP,f03");
925 				(void) ndi_prop_update_string(DDI_DEV_T_NONE,
926 				    xdip, "model", "PNP0Fxx mouse");
927 			} else {
928 				(void) parse_resources(ObjHandle, xdip, path);
929 				goto done;
930 			}
931 		}
932 	}
933 
934 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, "acpi-namespace",
935 	    path);
936 
937 	(void) parse_resources(ObjHandle, xdip, path);
938 
939 	/* Special processing for mouse and keyboard devices per IEEE 1275 */
940 	/* if master entry doesn't contain "compatible" then we add default */
941 	if (strcmp(m->name, keyboard_alias) == 0) {
942 		(void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, "reg", 0);
943 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
944 		    "device-type", keyboard_alias);
945 		if (!compatible_present)
946 			(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
947 			    "compatible", "pnpPNP,303");
948 	} else if (strcmp(m->name, mouse_alias) == 0) {
949 		(void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, "reg", 1);
950 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
951 		    "device-type", mouse_alias);
952 		if (!compatible_present)
953 			(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
954 			    "compatible", "pnpPNP,f03");
955 	}
956 
957 	/*
958 	 * Create default "compatible" property if required
959 	 */
960 	if (!ddi_prop_exists(DDI_DEV_T_ANY, xdip,
961 	    DDI_PROP_DONTPASS, "compatible"))
962 		create_compatible_property(xdip, device_ids);
963 
964 	(void) ndi_devi_bind_driver(xdip, 0);
965 
966 done:
967 	/* discard _HID/_CID list */
968 	d = device_ids;
969 	while (d != NULL) {
970 		device_id_t *next;
971 
972 		next = d->next;
973 		mf_free_device_id(d);
974 		d = next;
975 	}
976 
977 	if (path != NULL)
978 		AcpiOsFree(path);
979 	if (info != NULL)
980 		AcpiOsFree(info);
981 
982 	return (AE_OK);
983 }
984 
985 static void
986 used_res_interrupts(void)
987 {
988 	int intr[ACPI_ISA_LIMIT];
989 	int count = 0;
990 	int i;
991 
992 	for (i = 0; i < ACPI_ISA_LIMIT; i++) {
993 		if ((used_interrupts >> i) & 1) {
994 			intr[count++] = i;
995 		}
996 	}
997 	(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip,
998 	    "interrupts", (int *)intr, count);
999 }
1000 
1001 static void
1002 used_res_dmas(void)
1003 {
1004 	int dma[ACPI_ISA_LIMIT];
1005 	int count = 0;
1006 	int i;
1007 
1008 	for (i = 0; i < ACPI_ISA_LIMIT; i++) {
1009 		if ((used_dmas >> i) & 1) {
1010 			dma[count++] = i;
1011 		}
1012 	}
1013 	(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip,
1014 	    "dma-channels", (int *)dma, count);
1015 }
1016 
1017 static void
1018 used_res_io_mem(char *nodename, int *count, used_io_mem_t **head)
1019 {
1020 	int *io;
1021 	used_io_mem_t *used = *head;
1022 	int i;
1023 
1024 	*count *= 2;
1025 	io = (int *)kmem_zalloc(sizeof (int)*(*count), KM_SLEEP);
1026 	for (i = 0; i < *count; i += 2) {
1027 		used_io_mem_t *prev;
1028 		if (used != NULL) {
1029 			io[i] = used->start_addr;
1030 			io[i+1] = used->length;
1031 			prev = used;
1032 			used = used->next;
1033 			kmem_free(prev, sizeof (used_io_mem_t));
1034 		}
1035 	}
1036 	(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip,
1037 	    nodename, (int *)io, *count);
1038 	kmem_free(io, sizeof (int)*(*count));
1039 	*head = NULL;
1040 }
1041 
1042 /*
1043  * acpi_isa_device_enum() -- call from isa nexus driver
1044  * returns 1 if deviced enumeration is successful
1045  *         0 if deviced enumeration fails
1046  */
1047 int
1048 acpi_isa_device_enum(dev_info_t *isa_dip)
1049 {
1050 	char *acpi_prop;
1051 
1052 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
1053 	    DDI_PROP_DONTPASS, ACPI_ENUM_DEBUG, &acpi_prop) ==
1054 	    DDI_PROP_SUCCESS) {
1055 		long data;
1056 		if (ddi_strtol(acpi_prop, NULL, 0, &data) == 0) {
1057 			acpi_enum_debug = (unsigned long)data;
1058 			e_ddi_prop_remove(DDI_DEV_T_NONE, ddi_root_node(),
1059 			    ACPI_ENUM_DEBUG);
1060 			e_ddi_prop_update_int(DDI_DEV_T_NONE,
1061 			    ddi_root_node(), ACPI_ENUM_DEBUG, data);
1062 		}
1063 		ddi_prop_free(acpi_prop);
1064 	}
1065 
1066 	if (acpi_enum_debug & ISA_DEVICE_ENUM) {
1067 		cmn_err(CE_NOTE, "acpi_isa_device_enum() called");
1068 	}
1069 
1070 	if (acpica_init() != AE_OK) {
1071 		cmn_err(CE_WARN, "!isa_enum: init failed");
1072 		/* Note, pickup by i8042 nexus */
1073 		(void) e_ddi_prop_update_string(DDI_DEV_T_NONE,
1074 		    ddi_root_node(), "acpi-enum", "off");
1075 		return (0);
1076 	}
1077 
1078 	usedrdip = ddi_find_devinfo(USED_RESOURCES, -1, 0);
1079 	if (usedrdip == NULL) {
1080 		ndi_devi_alloc_sleep(ddi_root_node(), USED_RESOURCES,
1081 		    (pnode_t)DEVI_SID_NODEID, &usedrdip);
1082 
1083 	}
1084 
1085 	process_master_file();
1086 
1087 	/*
1088 	 * Do the actual enumeration.  Avoid AcpiGetDevices because it
1089 	 * has an unnecessary internal callback that duplicates
1090 	 * determining if the device is present.
1091 	 */
1092 	(void) AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
1093 	    UINT32_MAX, isa_acpi_callback, NULL, isa_dip, NULL);
1094 
1095 	free_master_data();
1096 	used_res_interrupts();
1097 	used_res_dmas();
1098 	used_res_io_mem("device-memory", &used_mem_count, &used_mem_head);
1099 	used_res_io_mem("io-space", &used_io_count, &used_io_head);
1100 	(void) ndi_devi_bind_driver(usedrdip, 0);
1101 
1102 	return (1);
1103 }
1104