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 2018, Joyent, Inc. 23 * Copyright (c) 2012 Gary Mills 24 * 25 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 26 * Use is subject to license terms. 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 <sys/acpi/acpi.h> 38 #include <sys/acpica.h> 39 #include <util/sscanf.h> 40 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.Address.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.Address.Minimum, 290 addr16.Address.Maximum, 291 addr16.Address.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.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; 305 } 306 io[*io_count].regspec_addr = addr16.Address.Minimum; 307 io[*io_count].regspec_size = addr16.Address.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.Address.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.Address.Minimum, 346 addr32.Address.Maximum, 347 addr32.Address.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.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; 361 } 362 io[*io_count].regspec_addr = addr32.Address.Minimum; 363 io[*io_count].regspec_size = addr32.Address.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.Address.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.Address.Minimum, 403 addr64.Address.Maximum, 404 addr64.Address.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.Address.Minimum, 418 addr64.Address.Maximum, 419 addr64.Address.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.Address.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.Address.Minimum; 436 io[*io_count].regspec_size = addr64.Address.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 int status; 795 796 /* 797 * get full ACPI pathname for object 798 */ 799 rb.Length = ACPI_ALLOCATE_BUFFER; 800 rb.Pointer = NULL; 801 if (AcpiGetName(ObjHandle, ACPI_FULL_PATHNAME, &rb) != AE_OK) { 802 cmn_err(CE_WARN, "!acpi_enum: could not get pathname"); 803 goto done; 804 } 805 path = (char *)rb.Pointer; 806 807 /* 808 * Get device info object 809 */ 810 if (AcpiGetObjectInfo(ObjHandle, &info) != AE_OK) { 811 cmn_err(CE_WARN, "!acpi_enum: could not get device" 812 " info for %s", path); 813 goto done; 814 } 815 816 /* 817 * If device isn't present, we don't enumerate 818 * NEEDSWORK: what about docking bays and the like? 819 */ 820 if (ACPI_FAILURE(acpica_get_object_status(ObjHandle, &status))) { 821 cmn_err(CE_WARN, "!acpi_enum: no _STA for %s", path); 822 goto done; 823 } 824 825 /* 826 * CA 6.3.6 _STA method 827 * Bit 0 -- device is present 828 * Bit 1 -- device is enabled 829 * Bit 2 -- device is shown in UI 830 */ 831 if ((status & 0x7) != 0x7) { 832 if (acpi_enum_debug & DEVICES_NOT_ENUMED) { 833 cmn_err(CE_NOTE, "!parse_resources() " 834 "Bad status 0x%x for %s", 835 status, path); 836 } 837 goto done; 838 } 839 840 /* 841 * Keep track of _HID value 842 */ 843 if (!(info->Valid & ACPI_VALID_HID)) { 844 /* No _HID, we skip this node */ 845 if (acpi_enum_debug & DEVICES_NOT_ENUMED) { 846 cmn_err(CE_NOTE, "!parse_resources() " 847 "No _HID for %s", path); 848 } 849 goto done; 850 } 851 hidstr = info->HardwareId.String; 852 853 /* 854 * Attempt to get _CID value 855 */ 856 rb.Length = ACPI_ALLOCATE_BUFFER; 857 rb.Pointer = NULL; 858 if (AcpiEvaluateObject(ObjHandle, "_CID", NULL, &rb) == AE_OK && 859 rb.Length != 0) { 860 ACPI_OBJECT *rv = rb.Pointer; 861 862 switch (rv->Type) { 863 case ACPI_TYPE_INTEGER: 864 eisa_to_str(rv->Integer.Value, tmp_cidstr); 865 d = mf_alloc_device_id(); 866 d->id = strdup(tmp_cidstr); 867 d->next = device_ids; 868 device_ids = d; 869 break; 870 case ACPI_TYPE_STRING: 871 d = mf_alloc_device_id(); 872 d->id = strdup(rv->String.Pointer); 873 d->next = device_ids; 874 device_ids = d; 875 break; 876 case ACPI_TYPE_PACKAGE: 877 process_cids(rv, &device_ids); 878 break; 879 default: 880 break; 881 } 882 AcpiOsFree(rb.Pointer); 883 } 884 885 /* 886 * Add _HID last so it's at the head of the list 887 */ 888 d = mf_alloc_device_id(); 889 d->id = strdup(hidstr); 890 d->next = device_ids; 891 device_ids = d; 892 893 /* 894 * master_file_lookup() expects _HID first in device_ids 895 */ 896 if ((m = master_file_lookup(device_ids)) != NULL) { 897 /* PNP description found in master table */ 898 if (!(strncmp(hidstr, "ACPI", 4))) { 899 dip = ddi_root_node(); 900 } else { 901 dip = get_bus_dip(m->name, dip); 902 } 903 ndi_devi_alloc_sleep(dip, m->name, 904 (pnode_t)DEVI_SID_NODEID, &xdip); 905 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 906 "model", m->description); 907 compatible_present = process_properties(xdip, m->properties); 908 } else { 909 /* for ISA devices not known to the master file */ 910 if (!(strncmp(hidstr, "PNP03", 5))) { 911 /* a keyboard device includes PNP03xx */ 912 dip = get_bus_dip(keyboard_alias, dip); 913 ndi_devi_alloc_sleep(dip, keyboard_alias, 914 (pnode_t)DEVI_SID_NODEID, &xdip); 915 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 916 "compatible", "pnpPNP,303"); 917 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 918 "model", "PNP03xx keyboard"); 919 } else { 920 if (!(strncmp(hidstr, "PNP0F", 5))) { 921 /* a mouse device include PNP0Fxx */ 922 dip = get_bus_dip(mouse_alias, dip); 923 ndi_devi_alloc_sleep(dip, mouse_alias, 924 (pnode_t)DEVI_SID_NODEID, &xdip); 925 (void) ndi_prop_update_string(DDI_DEV_T_NONE, 926 xdip, "compatible", "pnpPNP,f03"); 927 (void) ndi_prop_update_string(DDI_DEV_T_NONE, 928 xdip, "model", "PNP0Fxx mouse"); 929 } else { 930 (void) parse_resources(ObjHandle, xdip, path); 931 goto done; 932 } 933 } 934 } 935 936 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, "acpi-namespace", 937 path); 938 939 (void) parse_resources(ObjHandle, xdip, path); 940 941 /* Special processing for mouse and keyboard devices per IEEE 1275 */ 942 /* if master entry doesn't contain "compatible" then we add default */ 943 if (strcmp(m->name, keyboard_alias) == 0) { 944 (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, "reg", 0); 945 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 946 "device-type", keyboard_alias); 947 if (!compatible_present) 948 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 949 "compatible", "pnpPNP,303"); 950 } else if (strcmp(m->name, mouse_alias) == 0) { 951 (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, "reg", 1); 952 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 953 "device-type", mouse_alias); 954 if (!compatible_present) 955 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 956 "compatible", "pnpPNP,f03"); 957 } 958 959 /* 960 * Create default "compatible" property if required 961 */ 962 if (!ddi_prop_exists(DDI_DEV_T_ANY, xdip, 963 DDI_PROP_DONTPASS, "compatible")) 964 create_compatible_property(xdip, device_ids); 965 966 (void) ndi_devi_bind_driver(xdip, 0); 967 968 done: 969 /* discard _HID/_CID list */ 970 d = device_ids; 971 while (d != NULL) { 972 device_id_t *next; 973 974 next = d->next; 975 mf_free_device_id(d); 976 d = next; 977 } 978 979 if (path != NULL) 980 AcpiOsFree(path); 981 if (info != NULL) 982 AcpiOsFree(info); 983 984 return (AE_OK); 985 } 986 987 static void 988 used_res_interrupts(void) 989 { 990 int intr[ACPI_ISA_LIMIT]; 991 int count = 0; 992 int i; 993 994 for (i = 0; i < ACPI_ISA_LIMIT; i++) { 995 if ((used_interrupts >> i) & 1) { 996 intr[count++] = i; 997 } 998 } 999 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip, 1000 "interrupts", (int *)intr, count); 1001 } 1002 1003 static void 1004 used_res_dmas(void) 1005 { 1006 int dma[ACPI_ISA_LIMIT]; 1007 int count = 0; 1008 int i; 1009 1010 for (i = 0; i < ACPI_ISA_LIMIT; i++) { 1011 if ((used_dmas >> i) & 1) { 1012 dma[count++] = i; 1013 } 1014 } 1015 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip, 1016 "dma-channels", (int *)dma, count); 1017 } 1018 1019 static void 1020 used_res_io_mem(char *nodename, int *count, used_io_mem_t **head) 1021 { 1022 int *io; 1023 used_io_mem_t *used = *head; 1024 int i; 1025 1026 *count *= 2; 1027 io = (int *)kmem_zalloc(sizeof (int)*(*count), KM_SLEEP); 1028 for (i = 0; i < *count; i += 2) { 1029 used_io_mem_t *prev; 1030 if (used != NULL) { 1031 io[i] = used->start_addr; 1032 io[i+1] = used->length; 1033 prev = used; 1034 used = used->next; 1035 kmem_free(prev, sizeof (used_io_mem_t)); 1036 } 1037 } 1038 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip, 1039 nodename, (int *)io, *count); 1040 kmem_free(io, sizeof (int)*(*count)); 1041 *head = NULL; 1042 } 1043 1044 /* 1045 * acpi_isa_device_enum() -- call from isa nexus driver 1046 * returns 1 if deviced enumeration is successful 1047 * 0 if deviced enumeration fails 1048 */ 1049 int 1050 acpi_isa_device_enum(dev_info_t *isa_dip) 1051 { 1052 char *acpi_prop; 1053 1054 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 1055 DDI_PROP_DONTPASS, ACPI_ENUM_DEBUG, &acpi_prop) == 1056 DDI_PROP_SUCCESS) { 1057 long data; 1058 if (ddi_strtol(acpi_prop, NULL, 0, &data) == 0) { 1059 acpi_enum_debug = (unsigned long)data; 1060 e_ddi_prop_remove(DDI_DEV_T_NONE, ddi_root_node(), 1061 ACPI_ENUM_DEBUG); 1062 e_ddi_prop_update_int(DDI_DEV_T_NONE, 1063 ddi_root_node(), ACPI_ENUM_DEBUG, data); 1064 } 1065 ddi_prop_free(acpi_prop); 1066 } 1067 1068 if (acpi_enum_debug & ISA_DEVICE_ENUM) { 1069 cmn_err(CE_NOTE, "!acpi_isa_device_enum() called"); 1070 } 1071 1072 if (acpica_init() != AE_OK) { 1073 cmn_err(CE_WARN, "!isa_enum: init failed"); 1074 /* Note, pickup by i8042 nexus */ 1075 (void) e_ddi_prop_update_string(DDI_DEV_T_NONE, 1076 ddi_root_node(), "acpi-enum", "off"); 1077 return (0); 1078 } 1079 1080 usedrdip = ddi_find_devinfo(USED_RESOURCES, -1, 0); 1081 if (usedrdip == NULL) { 1082 ndi_devi_alloc_sleep(ddi_root_node(), USED_RESOURCES, 1083 (pnode_t)DEVI_SID_NODEID, &usedrdip); 1084 1085 } 1086 1087 process_master_file(); 1088 1089 /* 1090 * Do the actual enumeration. Avoid AcpiGetDevices because it 1091 * has an unnecessary internal callback that duplicates 1092 * determining if the device is present. 1093 */ 1094 (void) AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 1095 UINT32_MAX, isa_acpi_callback, NULL, isa_dip, NULL); 1096 1097 free_master_data(); 1098 used_res_interrupts(); 1099 used_res_dmas(); 1100 used_res_io_mem("device-memory", &used_mem_count, &used_mem_head); 1101 used_res_io_mem("io-space", &used_io_count, &used_io_head); 1102 (void) ndi_devi_bind_driver(usedrdip, 0); 1103 1104 return (1); 1105 } 1106