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