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 }