Merge illumos-gate
[unleashed/lotheac.git] / usr / src / uts / intel / io / acpica / acpi_enum.c
blob1caf3ef36550ebdd993dc5fb803436de28bad86f
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2018, Joyent, Inc.
23 * Copyright (c) 2012 Gary Mills
25 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
30 * ACPI enumerator
33 #include <sys/ddi.h>
34 #include <sys/sunddi.h>
35 #include <sys/sunndi.h>
36 #include <sys/note.h>
37 #include <sys/acpi/acpi.h>
38 #include <sys/acpica.h>
39 #include <util/sscanf.h>
42 static char keyboard_alias[] = "keyboard";
43 static char mouse_alias[] = "mouse";
44 #define ACPI_ENUM_DEBUG "acpi_enum_debug"
45 #define PARSE_RESOURCES_DEBUG 0x0001
46 #define MASTER_LOOKUP_DEBUG 0x0002
47 #define DEVICES_NOT_ENUMED 0x0004
48 #define PARSE_RES_IRQ 0x0008
49 #define PARSE_RES_DMA 0x0010
50 #define PARSE_RES_MEMORY 0x0020
51 #define PARSE_RES_IO 0x0040
52 #define PARSE_RES_ADDRESS 0x0080
53 #define ISA_DEVICE_ENUM 0x1000
54 #define PROCESS_CIDS 0x2000
55 static unsigned long acpi_enum_debug = 0x00;
57 static char USED_RESOURCES[] = "used-resources";
58 static dev_info_t *usedrdip = NULL;
59 static unsigned short used_interrupts = 0;
60 static unsigned short used_dmas = 0;
61 typedef struct used_io_mem {
62 unsigned int start_addr;
63 unsigned int length;
64 struct used_io_mem *next;
65 } used_io_mem_t;
66 static used_io_mem_t *used_io_head = NULL;
67 static used_io_mem_t *used_mem_head = NULL;
68 static int used_io_count = 0;
69 static int used_mem_count = 0;
71 #define MAX_PARSED_ACPI_RESOURCES 255
72 #define ACPI_ISA_LIMIT 16
73 static int interrupt[ACPI_ISA_LIMIT], dma[ACPI_ISA_LIMIT];
74 #define ACPI_ELEMENT_PACKAGE_LIMIT 32
75 #define EISA_ID_SIZE 7
78 * insert used io/mem in increasing order
80 static void
81 insert_used_resource(used_io_mem_t *used, int *used_count, used_io_mem_t **head)
83 used_io_mem_t *curr, *prev;
85 (*used_count)++;
86 if (*head == NULL) {
87 *head = used;
88 return;
90 curr = prev = *head;
91 /* find a place to insert */
92 while ((curr != NULL) &&
93 (curr->start_addr < used->start_addr)) {
94 prev = curr;
95 curr = curr->next;
97 if (prev == curr) {
98 /* head */
99 *head = used;
100 used->next = curr;
101 return;
102 } else {
103 prev->next = used;
105 used->next = curr;
108 static void
109 add_used_io_mem(struct regspec *io, int io_count)
111 int i;
112 used_io_mem_t *used;
114 for (i = 0; i < io_count; i++) {
115 used = (used_io_mem_t *)kmem_zalloc(sizeof (used_io_mem_t),
116 KM_SLEEP);
117 used->start_addr = io[i].regspec_addr;
118 used->length = io[i].regspec_size;
119 if (io[i].regspec_bustype == 1) {
120 insert_used_resource(used, &used_io_count,
121 &used_io_head);
122 } else {
123 insert_used_resource(used, &used_mem_count,
124 &used_mem_head);
129 static void
130 parse_resources_irq(ACPI_RESOURCE *resource_ptr, int *interrupt_count)
132 int i;
134 for (i = 0; i < resource_ptr->Data.Irq.InterruptCount; i++) {
135 interrupt[(*interrupt_count)++] =
136 resource_ptr->Data.Irq.Interrupts[i];
137 used_interrupts |= 1 << resource_ptr->Data.Irq.Interrupts[i];
138 if (acpi_enum_debug & PARSE_RES_IRQ) {
139 cmn_err(CE_NOTE, "!parse_resources() "\
140 "IRQ num %u, intr # = %u",
141 i, resource_ptr->Data.Irq.Interrupts[i]);
146 static void
147 parse_resources_dma(ACPI_RESOURCE *resource_ptr, int *dma_count)
149 int i;
151 for (i = 0; i < resource_ptr->Data.Dma.ChannelCount; i++) {
152 dma[(*dma_count)++] = resource_ptr->Data.Dma.Channels[i];
153 used_dmas |= 1 << resource_ptr->Data.Dma.Channels[i];
154 if (acpi_enum_debug & PARSE_RES_DMA) {
155 cmn_err(CE_NOTE, "!parse_resources() "\
156 "DMA num %u, channel # = %u",
157 i, resource_ptr->Data.Dma.Channels[i]);
162 static void
163 parse_resources_io(ACPI_RESOURCE *resource_ptr, struct regspec *io,
164 int *io_count)
166 ACPI_RESOURCE_IO acpi_io = resource_ptr->Data.Io;
168 if (acpi_io.AddressLength == 0)
169 return;
171 io[*io_count].regspec_bustype = 1; /* io */
172 io[*io_count].regspec_size = acpi_io.AddressLength;
173 io[*io_count].regspec_addr = acpi_io.Minimum;
174 if (acpi_enum_debug & PARSE_RES_IO) {
175 cmn_err(CE_NOTE, "!parse_resources() "\
176 "IO min 0x%X, max 0x%X, length: 0x%X",
177 acpi_io.Minimum,
178 acpi_io.Maximum,
179 acpi_io.AddressLength);
181 (*io_count)++;
184 static void
185 parse_resources_fixed_io(ACPI_RESOURCE *resource_ptr, struct regspec *io,
186 int *io_count)
188 ACPI_RESOURCE_FIXED_IO fixed_io = resource_ptr->Data.FixedIo;
190 if (fixed_io.AddressLength == 0)
191 return;
193 io[*io_count].regspec_bustype = 1; /* io */
194 io[*io_count].regspec_addr = fixed_io.Address;
195 io[*io_count].regspec_size = fixed_io.AddressLength;
196 if (acpi_enum_debug & PARSE_RES_IO) {
197 cmn_err(CE_NOTE, "!parse_resources() "\
198 "Fixed IO 0x%X, length: 0x%X",
199 fixed_io.Address, fixed_io.AddressLength);
201 (*io_count)++;
204 static void
205 parse_resources_fixed_mem32(ACPI_RESOURCE *resource_ptr, struct regspec *io,
206 int *io_count)
208 ACPI_RESOURCE_FIXED_MEMORY32 fixed_mem32 =
209 resource_ptr->Data.FixedMemory32;
211 if (fixed_mem32.AddressLength == 0)
212 return;
214 io[*io_count].regspec_bustype = 0; /* memory */
215 io[*io_count].regspec_addr = fixed_mem32.Address;
216 io[*io_count].regspec_size = fixed_mem32.AddressLength;
217 if (acpi_enum_debug & PARSE_RES_MEMORY) {
218 cmn_err(CE_NOTE, "!parse_resources() "\
219 "Fixed Mem 32 %ul, length: %ul",
220 fixed_mem32.Address, fixed_mem32.AddressLength);
222 (*io_count)++;
225 static void
226 parse_resources_mem32(ACPI_RESOURCE *resource_ptr, struct regspec *io,
227 int *io_count)
229 ACPI_RESOURCE_MEMORY32 mem32 = resource_ptr->Data.Memory32;
231 if (mem32.AddressLength == 0)
232 return;
234 if (resource_ptr->Data.Memory32.Minimum ==
235 resource_ptr->Data.Memory32.Maximum) {
236 io[*io_count].regspec_bustype = 0; /* memory */
237 io[*io_count].regspec_addr = mem32.Minimum;
238 io[*io_count].regspec_size = mem32.AddressLength;
239 (*io_count)++;
240 if (acpi_enum_debug & PARSE_RES_MEMORY) {
241 cmn_err(CE_NOTE, "!parse_resources() "\
242 "Mem 32 0x%X, length: 0x%X",
243 mem32.Minimum, mem32.AddressLength);
245 return;
247 if (acpi_enum_debug & PARSE_RES_MEMORY) {
248 cmn_err(CE_NOTE, "!parse_resources() "\
249 "MEM32 Min Max not equal!");
250 cmn_err(CE_NOTE, "!parse_resources() "\
251 "Mem 32 Minimum 0x%X, Maximum: 0x%X",
252 mem32.Minimum, mem32.Maximum);
256 static void
257 parse_resources_addr16(ACPI_RESOURCE *resource_ptr, struct regspec *io,
258 int *io_count)
260 ACPI_RESOURCE_ADDRESS16 addr16 =
261 resource_ptr->Data.Address16;
263 if (addr16.Address.AddressLength == 0)
264 return;
266 if (acpi_enum_debug & PARSE_RES_ADDRESS) {
267 if (addr16.ResourceType == ACPI_MEMORY_RANGE) {
268 cmn_err(CE_NOTE, "!parse_resources() "\
269 "ADDRESS 16 MEMORY RANGE");
270 } else
271 if (addr16.ResourceType == ACPI_IO_RANGE) {
272 cmn_err(CE_NOTE, "!parse_resources() "\
273 "ADDRESS 16 IO RANGE");
274 } else {
275 cmn_err(CE_NOTE, "!parse_resources() "\
276 "ADDRESS 16 OTHER");
278 cmn_err(CE_NOTE, "!parse_resources() "\
279 "%s "\
280 "MinAddressFixed 0x%X, "\
281 "MaxAddressFixed 0x%X, "\
282 "Minimum 0x%X, "\
283 "Maximum 0x%X, "\
284 "length: 0x%X\n",
285 addr16.ProducerConsumer == ACPI_CONSUMER ?
286 "CONSUMER" : "PRODUCER",
287 addr16.MinAddressFixed,
288 addr16.MaxAddressFixed,
289 addr16.Address.Minimum,
290 addr16.Address.Maximum,
291 addr16.Address.AddressLength);
293 if (addr16.ProducerConsumer == ACPI_PRODUCER ||
294 (addr16.ResourceType != ACPI_MEMORY_RANGE &&
295 addr16.ResourceType != ACPI_IO_RANGE)) {
296 return;
298 if (addr16.Address.AddressLength > 0) {
299 if (addr16.ResourceType == ACPI_MEMORY_RANGE) {
300 /* memory */
301 io[*io_count].regspec_bustype = 0;
302 } else {
303 /* io */
304 io[*io_count].regspec_bustype = 1;
306 io[*io_count].regspec_addr = addr16.Address.Minimum;
307 io[*io_count].regspec_size = addr16.Address.AddressLength;
308 (*io_count)++;
312 static void
313 parse_resources_addr32(ACPI_RESOURCE *resource_ptr, struct regspec *io,
314 int *io_count)
316 ACPI_RESOURCE_ADDRESS32 addr32 =
317 resource_ptr->Data.Address32;
319 if (addr32.Address.AddressLength == 0)
320 return;
322 if (acpi_enum_debug & PARSE_RES_ADDRESS) {
323 if (addr32.ResourceType == ACPI_MEMORY_RANGE) {
324 cmn_err(CE_NOTE, "!parse_resources() "\
325 "ADDRESS 32 MEMORY RANGE");
326 } else
327 if (addr32.ResourceType == ACPI_IO_RANGE) {
328 cmn_err(CE_NOTE, "!parse_resources() "\
329 "ADDRESS 32 IO RANGE");
330 } else {
331 cmn_err(CE_NOTE, "!parse_resources() "\
332 "ADDRESS 32 OTHER");
334 cmn_err(CE_NOTE, "!parse_resources() "\
335 "%s "\
336 "MinAddressFixed 0x%X, "\
337 "MaxAddressFixed 0x%X, "\
338 "Minimum 0x%X, "\
339 "Maximum 0x%X, "\
340 "length: 0x%X\n",
341 addr32.ProducerConsumer == ACPI_CONSUMER ?
342 "CONSUMER" : "PRODUCER",
343 addr32.MinAddressFixed,
344 addr32.MaxAddressFixed,
345 addr32.Address.Minimum,
346 addr32.Address.Maximum,
347 addr32.Address.AddressLength);
349 if (addr32.ProducerConsumer == ACPI_PRODUCER ||
350 (addr32.ResourceType != ACPI_MEMORY_RANGE &&
351 addr32.ResourceType != ACPI_IO_RANGE)) {
352 return;
354 if (addr32.Address.AddressLength > 0) {
355 if (addr32.ResourceType == ACPI_MEMORY_RANGE) {
356 /* memory */
357 io[*io_count].regspec_bustype = 0;
358 } else {
359 /* io */
360 io[*io_count].regspec_bustype = 1;
362 io[*io_count].regspec_addr = addr32.Address.Minimum;
363 io[*io_count].regspec_size = addr32.Address.AddressLength;
364 (*io_count)++;
368 static void
369 parse_resources_addr64(ACPI_RESOURCE *resource_ptr, struct regspec *io,
370 int *io_count)
372 ACPI_RESOURCE_ADDRESS64 addr64 =
373 resource_ptr->Data.Address64;
375 if (addr64.Address.AddressLength == 0)
376 return;
378 if (acpi_enum_debug & PARSE_RES_ADDRESS) {
379 if (addr64.ResourceType == ACPI_MEMORY_RANGE) {
380 cmn_err(CE_NOTE, "!parse_resources() "\
381 "ADDRESS 64 MEMORY RANGE");
382 } else
383 if (addr64.ResourceType == ACPI_IO_RANGE) {
384 cmn_err(CE_NOTE, "!parse_resources() "\
385 "ADDRESS 64 IO RANGE");
386 } else {
387 cmn_err(CE_NOTE, "!parse_resources() "\
388 "ADDRESS 64 OTHER");
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.Address.Minimum,
402 addr64.Address.Maximum,
403 addr64.Address.AddressLength);
405 if (addr64.ProducerConsumer == ACPI_PRODUCER ||
406 (addr64.ResourceType != ACPI_MEMORY_RANGE &&
407 addr64.ResourceType != ACPI_IO_RANGE)) {
408 return;
410 if (addr64.Address.AddressLength > 0) {
411 if (addr64.ResourceType == ACPI_MEMORY_RANGE) {
412 /* memory */
413 io[*io_count].regspec_bustype = 0;
414 } else {
415 /* io */
416 io[*io_count].regspec_bustype = 1;
418 io[*io_count].regspec_addr = addr64.Address.Minimum;
419 io[*io_count].regspec_size = addr64.Address.AddressLength;
420 (*io_count)++;
424 static ACPI_STATUS
425 parse_resources(ACPI_HANDLE handle, dev_info_t *xdip, char *path)
427 ACPI_BUFFER buf;
428 ACPI_RESOURCE *resource_ptr;
429 ACPI_STATUS status;
430 char *current_ptr, *last_ptr;
431 struct regspec *io;
432 int io_count = 0, interrupt_count = 0, dma_count = 0;
433 int i;
435 buf.Length = ACPI_ALLOCATE_BUFFER;
436 status = AcpiGetCurrentResources(handle, &buf);
437 switch (status) {
438 case AE_OK:
439 break;
440 case AE_NOT_FOUND:
442 * Workaround for faulty DSDT tables that omit the _CRS
443 * method for the UAR3 device but have a valid _PRS method
444 * for that device.
446 status = AcpiGetPossibleResources(handle, &buf);
447 if (status != AE_OK) {
448 return (status);
450 break;
451 default:
452 cmn_err(CE_WARN,
453 "!AcpiGetCurrentResources failed for %s, exception: %s",
454 path, AcpiFormatException(status));
455 return (status);
456 break;
458 io = kmem_zalloc(sizeof (struct regspec) *
459 MAX_PARSED_ACPI_RESOURCES, KM_SLEEP);
460 current_ptr = buf.Pointer;
461 last_ptr = (char *)buf.Pointer + buf.Length;
462 while (current_ptr < last_ptr) {
463 if (io_count >= MAX_PARSED_ACPI_RESOURCES) {
464 break;
466 resource_ptr = (ACPI_RESOURCE *)current_ptr;
467 current_ptr += resource_ptr->Length;
468 switch (resource_ptr->Type) {
469 case ACPI_RESOURCE_TYPE_END_TAG:
470 current_ptr = last_ptr;
471 break;
472 case ACPI_RESOURCE_TYPE_IO:
473 parse_resources_io(resource_ptr, io, &io_count);
474 break;
475 case ACPI_RESOURCE_TYPE_FIXED_IO:
476 parse_resources_fixed_io(resource_ptr, io, &io_count);
477 break;
478 case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
479 parse_resources_fixed_mem32(resource_ptr, io,
480 &io_count);
481 break;
482 case ACPI_RESOURCE_TYPE_MEMORY32:
483 parse_resources_mem32(resource_ptr, io, &io_count);
484 break;
485 case ACPI_RESOURCE_TYPE_ADDRESS16:
486 parse_resources_addr16(resource_ptr, io, &io_count);
487 break;
488 case ACPI_RESOURCE_TYPE_ADDRESS32:
489 parse_resources_addr32(resource_ptr, io, &io_count);
490 break;
491 case ACPI_RESOURCE_TYPE_ADDRESS64:
492 parse_resources_addr64(resource_ptr, io, &io_count);
493 break;
494 case ACPI_RESOURCE_TYPE_IRQ:
495 parse_resources_irq(resource_ptr, &interrupt_count);
496 break;
497 case ACPI_RESOURCE_TYPE_DMA:
498 parse_resources_dma(resource_ptr, &dma_count);
499 break;
500 case ACPI_RESOURCE_TYPE_START_DEPENDENT:
501 cmn_err(CE_NOTE,
502 "!ACPI source type"
503 " ACPI_RESOURCE_TYPE_START_DEPENDENT"
504 " not supported");
505 break;
506 case ACPI_RESOURCE_TYPE_END_DEPENDENT:
507 cmn_err(CE_NOTE,
508 "!ACPI source type"
509 " ACPI_RESOURCE_TYPE_END_DEPENDENT"
510 " not supported");
511 break;
512 case ACPI_RESOURCE_TYPE_VENDOR:
513 cmn_err(CE_NOTE,
514 "!ACPI source type"
515 " ACPI_RESOURCE_TYPE_VENDOR"
516 " not supported");
517 break;
518 case ACPI_RESOURCE_TYPE_MEMORY24:
519 cmn_err(CE_NOTE,
520 "!ACPI source type"
521 " ACPI_RESOURCE_TYPE_MEMORY24"
522 " not supported");
523 break;
524 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
525 cmn_err(CE_NOTE,
526 "!ACPI source type"
527 " ACPI_RESOURCE_TYPE_EXT_IRQ"
528 " not supported");
529 break;
530 default:
531 /* Some types are not yet implemented (See CA 6.4) */
532 cmn_err(CE_NOTE,
533 "!ACPI resource type (0X%X) not yet supported",
534 resource_ptr->Type);
535 break;
539 if (io_count) {
541 * on LX50, you get interrupts of mouse and keyboard
542 * from separate PNP id...
544 if (io_count == 2) {
545 if ((io[0].regspec_addr == 0x60 &&
546 io[1].regspec_addr == 0x64) ||
547 (io[0].regspec_addr == 0x64 &&
548 io[1].regspec_addr == 0x60)) {
549 interrupt[0] = 0x1;
550 interrupt[1] = 0xc;
551 interrupt_count = 2;
552 used_interrupts |=
553 1 << interrupt[0];
554 used_interrupts |=
555 1 << interrupt[1];
558 add_used_io_mem(io, io_count);
559 if (xdip != NULL) {
560 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip,
561 "reg", (int *)io, 3*io_count);
564 if (interrupt_count && (xdip != NULL)) {
565 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip,
566 "interrupts", (int *)interrupt, interrupt_count);
568 if (dma_count && (xdip != NULL)) {
569 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip,
570 "dma-channels", (int *)dma, dma_count);
572 AcpiOsFree(buf.Pointer);
573 kmem_free(io, sizeof (struct regspec) * MAX_PARSED_ACPI_RESOURCES);
574 return (status);
577 /* keyboard mouse is under i8042, everything else under isa */
578 static dev_info_t *
579 get_bus_dip(char *nodename, dev_info_t *isa_dip)
581 static dev_info_t *i8042_dip = NULL;
582 struct regspec i8042_regs[] = {
583 {1, 0x60, 0x1},
584 {1, 0x64, 0x1}
586 int i8042_intrs[] = {0x1, 0xc};
588 if (strcmp(nodename, keyboard_alias) != 0 &&
589 strcmp(nodename, mouse_alias) != 0)
590 return (isa_dip);
592 if (i8042_dip)
593 return (i8042_dip);
595 ndi_devi_alloc_sleep(isa_dip, "i8042", (pnode_t)DEVI_SID_NODEID,
596 &i8042_dip);
597 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, i8042_dip,
598 "reg", (int *)i8042_regs, 6);
599 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, i8042_dip,
600 "interrupts", (int *)i8042_intrs, 2);
601 (void) ndi_prop_update_string(DDI_DEV_T_NONE, i8042_dip,
602 "unit-address", "1,60");
603 (void) ndi_devi_bind_driver(i8042_dip, 0);
604 return (i8042_dip);
608 * put content of properties (if any) to dev info tree at branch xdip
609 * return non-zero if a "compatible" property was processed, zero otherwise
612 static int
613 process_properties(dev_info_t *xdip, property_t *properties)
615 int rv = 0;
617 while (properties != NULL) {
618 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
619 properties->name, properties->value);
620 if (strcmp(properties->name, "compatible") == 0)
621 rv = 1;
622 properties = properties->next;
625 return (rv);
628 void
629 eisa_to_str(ACPI_INTEGER id, char *np)
631 static const char hextab[] = "0123456789ABCDEF";
634 * Expand an EISA device name:
636 * This routine converts a 32-bit EISA device "id" to a
637 * 7-byte ASCII device name, which is stored at "np".
640 *np++ = '@' + ((id >> 2) & 0x1F);
641 *np++ = '@' + ((id << 3) & 0x18) + ((id >> 13) & 0x07);
642 *np++ = '@' + ((id >> 8) & 0x1F);
643 *np++ = hextab[(id >> 20) & 0x0F];
644 *np++ = hextab[(id >> 16) & 0x0F];
645 *np++ = hextab[(id >> 28) & 0x0F];
646 *np++ = hextab[(id >> 24) & 0x0F];
647 *np = 0;
651 * process_cids() -- process multiple CIDs in a package
653 static void
654 process_cids(ACPI_OBJECT *rv, device_id_t **dd)
656 device_id_t *d;
657 char tmp_cidstr[8]; /* 7-character EISA ID */
658 int i;
660 if ((rv->Package.Count == 0) || rv->Package.Elements == NULL)
661 return; /* empty package */
664 * Work the package 'backwards' so the resulting list is
665 * in original order of preference.
667 for (i = rv->Package.Count - 1; i >= 0; i--) {
668 /* get the actual acpi_object */
669 ACPI_OBJECT obj = rv->Package.Elements[i];
670 switch (obj.Type) {
671 case ACPI_TYPE_INTEGER:
672 eisa_to_str(obj.Integer.Value, tmp_cidstr);
673 d = mf_alloc_device_id();
674 d->id = strdup(tmp_cidstr);
675 d->next = *dd;
676 *dd = d;
677 break;
678 case ACPI_TYPE_STRING:
679 d = mf_alloc_device_id();
680 d->id = strdup(obj.String.Pointer);
681 d->next = *dd;
682 *dd = d;
683 break;
684 default:
685 if (acpi_enum_debug & PROCESS_CIDS) {
686 cmn_err(CE_NOTE, "!unexpected CID type: %d",
687 obj.Type);
689 break;
695 * Convert "raw" PNP and ACPI IDs to IEEE 1275-compliant form.
696 * Some liberty is taken here, treating "ACPI" as a special form
697 * of PNP vendor ID. strsize specifies size of buffer.
699 static void
700 convert_to_pnp1275(char *pnpid, char *str, int strsize)
702 char vendor[5];
703 uint_t id;
705 if (strncmp(pnpid, "ACPI", 4) == 0) {
706 /* Assume ACPI ID: ACPIxxxx */
707 sscanf(pnpid, "%4s%x", vendor, &id);
708 } else {
709 /* Assume PNP ID: aaaxxxx */
710 sscanf(pnpid, "%3s%x", vendor, &id);
713 snprintf(str, strsize, "pnp%s,%x", vendor, id);
717 * Given a list of device ID elements in most-to-least-specific
718 * order, create a "compatible" property.
720 static void
721 create_compatible_property(dev_info_t *dip, device_id_t *ids)
723 char **strs;
724 int list_len, i;
725 device_id_t *d;
727 /* count list length */
728 list_len = 0;
729 d = ids;
730 while (d != NULL) {
731 list_len++;
732 d = d->next;
735 /* create string array */
736 strs = (char **)kmem_zalloc(list_len * sizeof (char *), KM_SLEEP);
737 i = 0;
738 d = ids;
739 while (d != NULL) {
740 /* strlen("pnpXXXX,xxxx") + 1 = 13 */
741 strs[i] = kmem_zalloc(13, KM_SLEEP);
742 convert_to_pnp1275(d->id, strs[i++], 13);
743 d = d->next;
746 /* update property */
747 (void) ndi_prop_update_string_array(DDI_DEV_T_NONE, dip,
748 "compatible", strs, list_len);
751 /* free memory */
752 for (i = 0; i < list_len; i++)
753 kmem_free(strs[i], 13);
755 kmem_free(strs, list_len * sizeof (char *));
759 * isa_acpi_callback()
761 static ACPI_STATUS
762 isa_acpi_callback(ACPI_HANDLE ObjHandle, uint32_t NestingLevel, void *a,
763 void **b)
765 _NOTE(ARGUNUSED(NestingLevel, b))
767 ACPI_BUFFER rb;
768 ACPI_DEVICE_INFO *info = NULL;
769 char *path = NULL;
770 char *hidstr = NULL;
771 char tmp_cidstr[8]; /* EISAID size */
772 dev_info_t *dip = (dev_info_t *)a;
773 dev_info_t *xdip = NULL;
774 device_id_t *d, *device_ids = NULL;
775 const master_rec_t *m;
776 int compatible_present = 0;
777 int status;
780 * get full ACPI pathname for object
782 rb.Length = ACPI_ALLOCATE_BUFFER;
783 rb.Pointer = NULL;
784 if (AcpiGetName(ObjHandle, ACPI_FULL_PATHNAME, &rb) != AE_OK) {
785 cmn_err(CE_WARN, "!acpi_enum: could not get pathname");
786 goto done;
788 path = (char *)rb.Pointer;
791 * Get device info object
793 if (AcpiGetObjectInfo(ObjHandle, &info) != AE_OK) {
794 cmn_err(CE_WARN, "!acpi_enum: could not get device"
795 " info for %s", path);
796 goto done;
800 * If device isn't present, we don't enumerate
801 * NEEDSWORK: what about docking bays and the like?
803 if (ACPI_FAILURE(acpica_get_object_status(ObjHandle, &status))) {
804 cmn_err(CE_WARN, "!acpi_enum: no _STA for %s", path);
805 goto done;
809 * CA 6.3.6 _STA method
810 * Bit 0 -- device is present
811 * Bit 1 -- device is enabled
812 * Bit 2 -- device is shown in UI
814 if ((status & 0x7) != 0x7) {
815 if (acpi_enum_debug & DEVICES_NOT_ENUMED) {
816 cmn_err(CE_NOTE, "!parse_resources() "
817 "Bad status 0x%x for %s",
818 status, path);
820 goto done;
824 * Keep track of _HID value
826 if (!(info->Valid & ACPI_VALID_HID)) {
827 /* No _HID, we skip this node */
828 if (acpi_enum_debug & DEVICES_NOT_ENUMED) {
829 cmn_err(CE_NOTE, "!parse_resources() "
830 "No _HID for %s", path);
832 goto done;
834 hidstr = info->HardwareId.String;
837 * Attempt to get _CID value
839 rb.Length = ACPI_ALLOCATE_BUFFER;
840 rb.Pointer = NULL;
841 if (AcpiEvaluateObject(ObjHandle, "_CID", NULL, &rb) == AE_OK &&
842 rb.Length != 0) {
843 ACPI_OBJECT *rv = rb.Pointer;
845 switch (rv->Type) {
846 case ACPI_TYPE_INTEGER:
847 eisa_to_str(rv->Integer.Value, tmp_cidstr);
848 d = mf_alloc_device_id();
849 d->id = strdup(tmp_cidstr);
850 d->next = device_ids;
851 device_ids = d;
852 break;
853 case ACPI_TYPE_STRING:
854 d = mf_alloc_device_id();
855 d->id = strdup(rv->String.Pointer);
856 d->next = device_ids;
857 device_ids = d;
858 break;
859 case ACPI_TYPE_PACKAGE:
860 process_cids(rv, &device_ids);
861 break;
862 default:
863 break;
865 AcpiOsFree(rb.Pointer);
869 * Add _HID last so it's at the head of the list
871 d = mf_alloc_device_id();
872 d->id = strdup(hidstr);
873 d->next = device_ids;
874 device_ids = d;
877 * master_file_lookup() expects _HID first in device_ids
879 if ((m = master_file_lookup(device_ids)) != NULL) {
880 /* PNP description found in master table */
881 if (!(strncmp(hidstr, "ACPI", 4))) {
882 dip = ddi_root_node();
883 } else {
884 dip = get_bus_dip(m->name, dip);
886 ndi_devi_alloc_sleep(dip, m->name,
887 (pnode_t)DEVI_SID_NODEID, &xdip);
888 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
889 "model", m->description);
890 compatible_present = process_properties(xdip, m->properties);
891 } else {
892 /* for ISA devices not known to the master file */
893 if (!(strncmp(hidstr, "PNP03", 5))) {
894 /* a keyboard device includes PNP03xx */
895 dip = get_bus_dip(keyboard_alias, dip);
896 ndi_devi_alloc_sleep(dip, keyboard_alias,
897 (pnode_t)DEVI_SID_NODEID, &xdip);
898 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
899 "compatible", "pnpPNP,303");
900 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
901 "model", "PNP03xx keyboard");
902 } else {
903 if (!(strncmp(hidstr, "PNP0F", 5))) {
904 /* a mouse device include PNP0Fxx */
905 dip = get_bus_dip(mouse_alias, dip);
906 ndi_devi_alloc_sleep(dip, mouse_alias,
907 (pnode_t)DEVI_SID_NODEID, &xdip);
908 (void) ndi_prop_update_string(DDI_DEV_T_NONE,
909 xdip, "compatible", "pnpPNP,f03");
910 (void) ndi_prop_update_string(DDI_DEV_T_NONE,
911 xdip, "model", "PNP0Fxx mouse");
912 } else {
913 (void) parse_resources(ObjHandle, xdip, path);
914 goto done;
919 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, "acpi-namespace",
920 path);
922 (void) parse_resources(ObjHandle, xdip, path);
924 /* Special processing for mouse and keyboard devices per IEEE 1275 */
925 /* if master entry doesn't contain "compatible" then we add default */
926 if (strcmp(m->name, keyboard_alias) == 0) {
927 (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, "reg", 0);
928 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
929 "device-type", keyboard_alias);
930 if (!compatible_present)
931 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
932 "compatible", "pnpPNP,303");
933 } else if (strcmp(m->name, mouse_alias) == 0) {
934 (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, "reg", 1);
935 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
936 "device-type", mouse_alias);
937 if (!compatible_present)
938 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
939 "compatible", "pnpPNP,f03");
943 * Create default "compatible" property if required
945 if (!ddi_prop_exists(DDI_DEV_T_ANY, xdip,
946 DDI_PROP_DONTPASS, "compatible"))
947 create_compatible_property(xdip, device_ids);
949 (void) ndi_devi_bind_driver(xdip, 0);
951 done:
952 /* discard _HID/_CID list */
953 d = device_ids;
954 while (d != NULL) {
955 device_id_t *next;
957 next = d->next;
958 mf_free_device_id(d);
959 d = next;
962 if (path != NULL)
963 AcpiOsFree(path);
964 if (info != NULL)
965 AcpiOsFree(info);
967 return (AE_OK);
970 static void
971 used_res_interrupts(void)
973 int intr[ACPI_ISA_LIMIT];
974 int count = 0;
975 int i;
977 for (i = 0; i < ACPI_ISA_LIMIT; i++) {
978 if ((used_interrupts >> i) & 1) {
979 intr[count++] = i;
982 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip,
983 "interrupts", (int *)intr, count);
986 static void
987 used_res_dmas(void)
989 int dma[ACPI_ISA_LIMIT];
990 int count = 0;
991 int i;
993 for (i = 0; i < ACPI_ISA_LIMIT; i++) {
994 if ((used_dmas >> i) & 1) {
995 dma[count++] = i;
998 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip,
999 "dma-channels", (int *)dma, count);
1002 static void
1003 used_res_io_mem(char *nodename, int *count, used_io_mem_t **head)
1005 int *io;
1006 used_io_mem_t *used = *head;
1007 int i;
1009 *count *= 2;
1010 io = (int *)kmem_zalloc(sizeof (int)*(*count), KM_SLEEP);
1011 for (i = 0; i < *count; i += 2) {
1012 used_io_mem_t *prev;
1013 if (used != NULL) {
1014 io[i] = used->start_addr;
1015 io[i+1] = used->length;
1016 prev = used;
1017 used = used->next;
1018 kmem_free(prev, sizeof (used_io_mem_t));
1021 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip,
1022 nodename, (int *)io, *count);
1023 kmem_free(io, sizeof (int)*(*count));
1024 *head = NULL;
1028 * acpi_isa_device_enum() -- call from isa nexus driver
1029 * returns 1 if deviced enumeration is successful
1030 * 0 if deviced enumeration fails
1033 acpi_isa_device_enum(dev_info_t *isa_dip)
1035 char *acpi_prop;
1037 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
1038 DDI_PROP_DONTPASS, ACPI_ENUM_DEBUG, &acpi_prop) ==
1039 DDI_PROP_SUCCESS) {
1040 long data;
1041 if (ddi_strtol(acpi_prop, NULL, 0, &data) == 0) {
1042 acpi_enum_debug = (unsigned long)data;
1043 e_ddi_prop_remove(DDI_DEV_T_NONE, ddi_root_node(),
1044 ACPI_ENUM_DEBUG);
1045 e_ddi_prop_update_int(DDI_DEV_T_NONE,
1046 ddi_root_node(), ACPI_ENUM_DEBUG, data);
1048 ddi_prop_free(acpi_prop);
1051 if (acpi_enum_debug & ISA_DEVICE_ENUM) {
1052 cmn_err(CE_NOTE, "!acpi_isa_device_enum() called");
1055 if (acpica_init() != AE_OK) {
1056 cmn_err(CE_WARN, "!isa_enum: init failed");
1057 /* Note, pickup by i8042 nexus */
1058 (void) e_ddi_prop_update_string(DDI_DEV_T_NONE,
1059 ddi_root_node(), "acpi-enum", "off");
1060 return (0);
1063 usedrdip = ddi_find_devinfo(USED_RESOURCES, -1, 0);
1064 if (usedrdip == NULL) {
1065 ndi_devi_alloc_sleep(ddi_root_node(), USED_RESOURCES,
1066 (pnode_t)DEVI_SID_NODEID, &usedrdip);
1070 process_master_file();
1073 * Do the actual enumeration. Avoid AcpiGetDevices because it
1074 * has an unnecessary internal callback that duplicates
1075 * determining if the device is present.
1077 (void) AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
1078 UINT32_MAX, isa_acpi_callback, NULL, isa_dip, NULL);
1080 free_master_data();
1081 used_res_interrupts();
1082 used_res_dmas();
1083 used_res_io_mem("device-memory", &used_mem_count, &used_mem_head);
1084 used_res_io_mem("io-space", &used_io_count, &used_io_head);
1085 (void) ndi_devi_bind_driver(usedrdip, 0);
1087 return (1);