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