1 /*- 2 * Copyright (c) 2002 Mitsuru IWASAKI <iwasaki@jp.freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include "opt_acpi.h" 31 #include <sys/param.h> 32 #include <sys/kernel.h> 33 #include <sys/bus.h> 34 35 #include "acpi.h" 36 #include <dev/acpica/acpivar.h> 37 #include <dev/acpica/acpi_pcibvar.h> 38 39 #include <dev/pci/pcivar.h> 40 #include "pcib_if.h" 41 42 /* Hooks for the ACPI CA debugging infrastructure. */ 43 #define _COMPONENT ACPI_BUS 44 ACPI_MODULE_NAME("PCI_LINK") 45 46 TAILQ_HEAD(acpi_pci_link_entries, acpi_pci_link_entry); 47 static struct acpi_pci_link_entries acpi_pci_link_entries; 48 ACPI_SERIAL_DECL(pci_link, "ACPI PCI link"); 49 50 TAILQ_HEAD(acpi_prt_entries, acpi_prt_entry); 51 static struct acpi_prt_entries acpi_prt_entries; 52 53 static int irq_penalty[MAX_ACPI_INTERRUPTS]; 54 55 static int acpi_pci_link_is_valid_irq(struct acpi_pci_link_entry *link, 56 UINT8 irq); 57 static void acpi_pci_link_update_irq_penalty(device_t dev, int busno); 58 static void acpi_pci_link_set_bootdisabled_priority(void); 59 static void acpi_pci_link_fixup_bootdisabled_link(void); 60 61 /* 62 * PCI link object management 63 */ 64 65 static void 66 acpi_pci_link_dump_polarity(UINT32 ActiveHighLow) 67 { 68 69 switch (ActiveHighLow) { 70 case ACPI_ACTIVE_HIGH: 71 printf("high,"); 72 break; 73 case ACPI_ACTIVE_LOW: 74 printf("low,"); 75 break; 76 default: 77 printf("unknown,"); 78 break; 79 } 80 } 81 82 static void 83 acpi_pci_link_dump_trigger(UINT32 EdgeLevel) 84 { 85 86 switch (EdgeLevel) { 87 case ACPI_EDGE_SENSITIVE: 88 printf("edge,"); 89 break; 90 case ACPI_LEVEL_SENSITIVE: 91 printf("level,"); 92 break; 93 default: 94 printf("unknown,"); 95 break; 96 } 97 } 98 99 static void 100 acpi_pci_link_dump_sharemode(UINT32 SharedExclusive) 101 { 102 103 switch (SharedExclusive) { 104 case ACPI_EXCLUSIVE: 105 printf("exclusive"); 106 break; 107 case ACPI_SHARED: 108 printf("sharable"); 109 break; 110 default: 111 printf("unknown"); 112 break; 113 } 114 } 115 116 static void 117 acpi_pci_link_entry_dump(struct acpi_prt_entry *entry) 118 { 119 UINT8 i; 120 ACPI_RESOURCE_IRQ *Irq; 121 ACPI_RESOURCE_EXT_IRQ *ExtIrq; 122 struct acpi_pci_link_entry *link; 123 124 if (entry == NULL || entry->pci_link == NULL) 125 return; 126 link = entry->pci_link; 127 128 printf("%s irq%c%2d: ", acpi_name(link->handle), 129 (link->flags & ACPI_LINK_ROUTED) ? '*' : ' ', link->current_irq); 130 131 printf("["); 132 if (link->number_of_interrupts) 133 printf("%2d", link->interrupts[0]); 134 for (i = 1; i < link->number_of_interrupts; i++) 135 printf("%3d", link->interrupts[i]); 136 printf("] %2d+ ", link->initial_irq); 137 138 switch (link->possible_resources.Id) { 139 case ACPI_RSTYPE_IRQ: 140 Irq = &link->possible_resources.Data.Irq; 141 acpi_pci_link_dump_polarity(Irq->ActiveHighLow); 142 acpi_pci_link_dump_trigger(Irq->EdgeLevel); 143 acpi_pci_link_dump_sharemode(Irq->SharedExclusive); 144 break; 145 case ACPI_RSTYPE_EXT_IRQ: 146 ExtIrq = &link->possible_resources.Data.ExtendedIrq; 147 acpi_pci_link_dump_polarity(ExtIrq->ActiveHighLow); 148 acpi_pci_link_dump_trigger(ExtIrq->EdgeLevel); 149 acpi_pci_link_dump_sharemode(ExtIrq->SharedExclusive); 150 break; 151 } 152 153 printf(" %d.%d.%d\n", entry->busno, 154 (int)((entry->prt.Address & 0xffff0000) >> 16), 155 (int)entry->prt.Pin); 156 } 157 158 static ACPI_STATUS 159 acpi_pci_link_get_object_status(ACPI_HANDLE handle, UINT32 *sta) 160 { 161 ACPI_DEVICE_INFO *devinfo; 162 ACPI_BUFFER buf; 163 ACPI_STATUS error; 164 165 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 166 167 if (handle == NULL || sta == NULL) { 168 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "invalid argument\n")); 169 return_ACPI_STATUS (AE_BAD_PARAMETER); 170 } 171 172 buf.Pointer = NULL; 173 buf.Length = ACPI_ALLOCATE_BUFFER; 174 error = AcpiGetObjectInfo(handle, &buf); 175 if (ACPI_FAILURE(error)) { 176 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 177 "couldn't get object info %s - %s\n", 178 acpi_name(handle), AcpiFormatException(error))); 179 return_ACPI_STATUS (error); 180 } 181 182 devinfo = (ACPI_DEVICE_INFO *)buf.Pointer; 183 if ((devinfo->Valid & ACPI_VALID_HID) == 0 || 184 strcmp(devinfo->HardwareId.Value, "PNP0C0F") != 0) { 185 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "invalid hardware ID - %s\n", 186 acpi_name(handle))); 187 AcpiOsFree(buf.Pointer); 188 return_ACPI_STATUS (AE_TYPE); 189 } 190 191 if ((devinfo->Valid & ACPI_VALID_STA) != 0) { 192 *sta = devinfo->CurrentStatus; 193 } else { 194 ACPI_DEBUG_PRINT((ACPI_DB_WARN, "invalid status - %s\n", 195 acpi_name(handle))); 196 *sta = 0; 197 } 198 199 AcpiOsFree(buf.Pointer); 200 return_ACPI_STATUS (AE_OK); 201 } 202 203 static ACPI_STATUS 204 acpi_pci_link_get_irq_resources(ACPI_RESOURCE *resources, 205 UINT8 *number_of_interrupts, UINT8 interrupts[]) 206 { 207 UINT8 count; 208 UINT8 i; 209 UINT32 NumberOfInterrupts; 210 UINT32 *Interrupts; 211 212 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 213 214 if (resources == NULL || number_of_interrupts == NULL) { 215 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "invalid argument\n")); 216 return_ACPI_STATUS (AE_BAD_PARAMETER); 217 } 218 219 *number_of_interrupts = 0; 220 NumberOfInterrupts = 0; 221 Interrupts = NULL; 222 223 if (resources->Id == ACPI_RSTYPE_START_DPF) 224 resources = ACPI_NEXT_RESOURCE(resources); 225 226 if (resources->Id != ACPI_RSTYPE_IRQ && 227 resources->Id != ACPI_RSTYPE_EXT_IRQ) { 228 printf("acpi link get: resource %d is not an IRQ\n", 229 resources->Id); 230 return_ACPI_STATUS (AE_TYPE); 231 } 232 233 switch (resources->Id) { 234 case ACPI_RSTYPE_IRQ: 235 NumberOfInterrupts = resources->Data.Irq.NumberOfInterrupts; 236 Interrupts = resources->Data.Irq.Interrupts; 237 break; 238 case ACPI_RSTYPE_EXT_IRQ: 239 NumberOfInterrupts = 240 resources->Data.ExtendedIrq.NumberOfInterrupts; 241 Interrupts = resources->Data.ExtendedIrq.Interrupts; 242 break; 243 } 244 245 if (NumberOfInterrupts == 0) 246 return_ACPI_STATUS (AE_NULL_ENTRY); 247 248 count = 0; 249 for (i = 0; i < NumberOfInterrupts; i++) { 250 if (i >= MAX_POSSIBLE_INTERRUPTS) { 251 ACPI_DEBUG_PRINT((ACPI_DB_WARN, "too many IRQs (%d)\n", 252 i)); 253 break; 254 } 255 if (Interrupts[i] == 0) { 256 ACPI_DEBUG_PRINT((ACPI_DB_WARN, "invalid IRQ %d\n", 257 Interrupts[i])); 258 continue; 259 } 260 interrupts[count] = Interrupts[i]; 261 count++; 262 } 263 *number_of_interrupts = count; 264 265 return_ACPI_STATUS (AE_OK); 266 } 267 268 static ACPI_STATUS 269 acpi_pci_link_get_current_irq(struct acpi_pci_link_entry *link, UINT8 *irq) 270 { 271 ACPI_STATUS error; 272 ACPI_BUFFER buf; 273 ACPI_RESOURCE *resources; 274 UINT8 number_of_interrupts; 275 UINT8 interrupts[MAX_POSSIBLE_INTERRUPTS];; 276 277 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 278 279 if (link == NULL || irq == NULL) { 280 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "invalid argument\n")); 281 return_ACPI_STATUS (AE_BAD_PARAMETER); 282 } 283 284 *irq = 0; 285 buf.Pointer = NULL; 286 buf.Length = ACPI_ALLOCATE_BUFFER; 287 error = AcpiGetCurrentResources(link->handle, &buf); 288 if (ACPI_FAILURE(error)) { 289 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 290 "couldn't get PCI interrupt link device _CRS %s - %s\n", 291 acpi_name(link->handle), AcpiFormatException(error))); 292 return_ACPI_STATUS (error); 293 } 294 if (buf.Pointer == NULL) { 295 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 296 "couldn't allocate memory - %s\n", 297 acpi_name(link->handle))); 298 return_ACPI_STATUS (AE_NO_MEMORY); 299 } 300 301 resources = (ACPI_RESOURCE *) buf.Pointer; 302 number_of_interrupts = 0; 303 bzero(interrupts, sizeof(interrupts)); 304 error = acpi_pci_link_get_irq_resources(resources, 305 &number_of_interrupts, interrupts); 306 AcpiOsFree(buf.Pointer); 307 308 if (ACPI_FAILURE(error)) { 309 ACPI_DEBUG_PRINT((ACPI_DB_WARN, 310 "couldn't get current IRQ from interrupt link %s - %s\n", 311 acpi_name(link->handle), AcpiFormatException(error))); 312 return_ACPI_STATUS (error); 313 } 314 315 if (number_of_interrupts == 0) { 316 ACPI_DEBUG_PRINT((ACPI_DB_WARN, 317 "PCI interrupt link device _CRS data is corrupted - %s\n", 318 acpi_name(link->handle))); 319 return_ACPI_STATUS (AE_NULL_ENTRY); 320 } 321 322 *irq = interrupts[0]; 323 324 return_ACPI_STATUS (AE_OK); 325 } 326 327 static ACPI_STATUS 328 acpi_pci_link_add_link(ACPI_HANDLE handle, struct acpi_prt_entry *entry) 329 { 330 ACPI_STATUS error; 331 ACPI_BUFFER buf; 332 ACPI_RESOURCE *resources; 333 struct acpi_pci_link_entry *link; 334 335 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 336 ACPI_SERIAL_ASSERT(pci_link); 337 338 entry->pci_link = NULL; 339 TAILQ_FOREACH(link, &acpi_pci_link_entries, links) { 340 if (link->handle == handle) { 341 entry->pci_link = link; 342 link->references++; 343 return_ACPI_STATUS (AE_OK); 344 } 345 } 346 347 link = AcpiOsAllocate(sizeof(struct acpi_pci_link_entry)); 348 if (link == NULL) { 349 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 350 "couldn't allocate memory - %s\n", acpi_name(handle))); 351 return_ACPI_STATUS (AE_NO_MEMORY); 352 } 353 354 buf.Pointer = NULL; 355 buf.Length = ACPI_ALLOCATE_BUFFER; 356 357 bzero(link, sizeof(struct acpi_pci_link_entry)); 358 link->handle = handle; 359 360 /* 361 * Get the IRQ configured at boot-time. If successful, set this 362 * as the initial IRQ. 363 */ 364 error = acpi_pci_link_get_current_irq(link, &link->current_irq); 365 if (ACPI_SUCCESS(error)) { 366 link->initial_irq = link->current_irq; 367 } else { 368 ACPI_DEBUG_PRINT((ACPI_DB_WARN, 369 "couldn't get current IRQ from interrupt link %s - %s\n", 370 acpi_name(handle), AcpiFormatException(error))); 371 link->initial_irq = 0; 372 } 373 374 error = AcpiGetPossibleResources(handle, &buf); 375 if (ACPI_FAILURE(error)) { 376 ACPI_DEBUG_PRINT((ACPI_DB_WARN, 377 "couldn't get interrupt link device _PRS data %s - %s\n", 378 acpi_name(handle), AcpiFormatException(error))); 379 goto out; 380 } 381 if (buf.Pointer == NULL) { 382 ACPI_DEBUG_PRINT((ACPI_DB_WARN, 383 "_PRS buffer is empty - %s\n", acpi_name(handle))); 384 error = AE_NO_MEMORY; 385 goto out; 386 } 387 388 /* Skip any DPF descriptors. XXX We should centralize this code. */ 389 resources = (ACPI_RESOURCE *) buf.Pointer; 390 if (resources->Id == ACPI_RSTYPE_START_DPF) 391 resources = ACPI_NEXT_RESOURCE(resources); 392 393 /* XXX This only handles one resource, ignoring SourceIndex. */ 394 bcopy(resources, &link->possible_resources, 395 sizeof(link->possible_resources)); 396 397 error = acpi_pci_link_get_irq_resources(resources, 398 &link->number_of_interrupts, link->interrupts); 399 if (ACPI_FAILURE(error)) { 400 ACPI_DEBUG_PRINT((ACPI_DB_WARN, 401 "couldn't get possible IRQs from interrupt link %s - %s\n", 402 acpi_name(handle), AcpiFormatException(error))); 403 goto out; 404 } 405 406 if (link->number_of_interrupts == 0) { 407 ACPI_DEBUG_PRINT((ACPI_DB_WARN, 408 "interrupt link device _PRS data is corrupted - %s\n", 409 acpi_name(handle))); 410 error = AE_NULL_ENTRY; 411 goto out; 412 } 413 414 /* 415 * Try to disable this link. If successful, set the current IRQ to 416 * zero and flags to indicate this link is not routed. If we can't 417 * run _DIS (i.e., the method doesn't exist), assume the initial 418 * IRQ was routed by the BIOS. 419 */ 420 if (ACPI_SUCCESS(AcpiEvaluateObject(handle, "_DIS", NULL, NULL))) { 421 link->current_irq = 0; 422 link->flags = ACPI_LINK_NONE; 423 } else 424 link->flags = ACPI_LINK_ROUTED; 425 426 /* 427 * If the initial IRQ is invalid (not in _PRS), set it to 0 and 428 * mark this link as not routed. We won't use it as the preferred 429 * interrupt later when we route. 430 */ 431 if (!acpi_pci_link_is_valid_irq(link, link->initial_irq) && 432 link->initial_irq != 0) { 433 printf("ACPI link %s has invalid initial irq %d, ignoring\n", 434 acpi_name(handle), link->initial_irq); 435 link->initial_irq = 0; 436 link->flags = ACPI_LINK_NONE; 437 } 438 439 link->references++; 440 441 TAILQ_INSERT_TAIL(&acpi_pci_link_entries, link, links); 442 entry->pci_link = link; 443 444 error = AE_OK; 445 out: 446 if (buf.Pointer != NULL) 447 AcpiOsFree(buf.Pointer); 448 if (error != AE_OK && link != NULL) 449 AcpiOsFree(link); 450 451 return_ACPI_STATUS (error); 452 } 453 454 static ACPI_STATUS 455 acpi_pci_link_add_prt(device_t pcidev, ACPI_PCI_ROUTING_TABLE *prt, int busno) 456 { 457 ACPI_HANDLE handle; 458 ACPI_STATUS error; 459 UINT32 sta; 460 struct acpi_prt_entry *entry; 461 462 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 463 ACPI_SERIAL_ASSERT(pci_link); 464 465 if (prt == NULL) { 466 device_printf(pcidev, "NULL PRT entry\n"); 467 return_ACPI_STATUS (AE_BAD_PARAMETER); 468 } 469 470 /* Bail out if attempting to add a duplicate PRT entry. */ 471 TAILQ_FOREACH(entry, &acpi_prt_entries, links) { 472 if (entry->busno == busno && 473 entry->prt.Address == prt->Address && 474 entry->prt.Pin == prt->Pin) { 475 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 476 "PRT entry already exists\n")); 477 return_ACPI_STATUS (AE_ALREADY_EXISTS); 478 } 479 } 480 481 /* Allocate and initialize our new PRT entry. */ 482 entry = AcpiOsAllocate(sizeof(struct acpi_prt_entry)); 483 if (entry == NULL) { 484 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "can't allocate memory\n")); 485 return_ACPI_STATUS (AE_NO_MEMORY); 486 } 487 bzero(entry, sizeof(struct acpi_prt_entry)); 488 489 /* 490 * If the source link is NULL, then this IRQ is hardwired so skip 491 * initializing the link but still add it to the list. 492 */ 493 if (prt->Source[0] != '\0') { 494 /* Get a handle for the link source. */ 495 error = AcpiGetHandle(acpi_get_handle(pcidev), prt->Source, 496 &handle); 497 if (ACPI_FAILURE(error)) { 498 device_printf(pcidev, "get handle for %s - %s\n", 499 prt->Source, AcpiFormatException(error)); 500 goto out; 501 } 502 503 error = acpi_pci_link_get_object_status(handle, &sta); 504 if (ACPI_FAILURE(error)) { 505 device_printf(pcidev, "can't get status for %s - %s\n", 506 acpi_name(handle), AcpiFormatException(error)); 507 goto out; 508 } 509 510 /* Probe/initialize the link. */ 511 error = acpi_pci_link_add_link(handle, entry); 512 if (ACPI_FAILURE(error)) { 513 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 514 "couldn't add _PRT entry to link %s - %s\n", 515 acpi_name(handle), AcpiFormatException(error))); 516 goto out; 517 } 518 } 519 520 entry->pcidev = pcidev; 521 entry->busno = busno; 522 bcopy(prt, &entry->prt, sizeof(entry->prt)); 523 524 /* 525 * Make sure the Source value is null-terminated. It is really a 526 * variable-length string (with a fixed size in the struct) so when 527 * we copy the entire struct, we truncate the string. Instead of 528 * trying to make a variable-sized PRT object to handle the string, 529 * we store its handle in prt_source. Callers should use that to 530 * look up the link object. 531 */ 532 entry->prt.Source[sizeof(prt->Source) - 1] = '\0'; 533 entry->prt_source = handle; 534 535 TAILQ_INSERT_TAIL(&acpi_prt_entries, entry, links); 536 error = AE_OK; 537 538 out: 539 if (error != AE_OK && entry != NULL) 540 AcpiOsFree(entry); 541 542 return_ACPI_STATUS (error); 543 } 544 545 /* 546 * Look up the given interrupt in the list of possible settings for 547 * this link. We don't special-case the initial link setting. Some 548 * systems return current settings that are outside the list of valid 549 * settings so only allow choices explicitly specified in _PRS. 550 */ 551 static int 552 acpi_pci_link_is_valid_irq(struct acpi_pci_link_entry *link, UINT8 irq) 553 { 554 UINT8 i; 555 556 if (irq == 0) 557 return (FALSE); 558 559 for (i = 0; i < link->number_of_interrupts; i++) { 560 if (link->interrupts[i] == irq) 561 return (TRUE); 562 } 563 564 return (FALSE); 565 } 566 567 static ACPI_STATUS 568 acpi_pci_link_set_irq(struct acpi_pci_link_entry *link, UINT8 irq) 569 { 570 ACPI_STATUS error; 571 ACPI_RESOURCE resbuf; 572 ACPI_BUFFER crsbuf; 573 574 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 575 ACPI_SERIAL_ASSERT(pci_link); 576 577 /* Make sure the new IRQ is valid before routing. */ 578 if (!acpi_pci_link_is_valid_irq(link, irq)) { 579 printf("acpi link set: invalid IRQ %d on %s\n", 580 irq, acpi_name(link->handle)); 581 return_ACPI_STATUS (AE_BAD_PARAMETER); 582 } 583 584 /* If this this link has already been routed, just return. */ 585 if (link->flags & ACPI_LINK_ROUTED) { 586 printf("acpi link set: %s already routed to %d\n", 587 acpi_name(link->handle), link->current_irq); 588 return_ACPI_STATUS (AE_OK); 589 } 590 591 /* Set up the IRQ resource for _SRS. */ 592 bzero(&resbuf, sizeof(resbuf)); 593 crsbuf.Pointer = NULL; 594 595 switch (link->possible_resources.Id) { 596 case ACPI_RSTYPE_IRQ: 597 resbuf.Id = ACPI_RSTYPE_IRQ; 598 resbuf.Length = ACPI_SIZEOF_RESOURCE(ACPI_RESOURCE_IRQ); 599 600 /* structure copy other fields */ 601 resbuf.Data.Irq = link->possible_resources.Data.Irq; 602 resbuf.Data.Irq.NumberOfInterrupts = 1; 603 resbuf.Data.Irq.Interrupts[0] = irq; 604 break; 605 case ACPI_RSTYPE_EXT_IRQ: 606 resbuf.Id = ACPI_RSTYPE_EXT_IRQ; 607 resbuf.Length = ACPI_SIZEOF_RESOURCE(ACPI_RESOURCE_EXT_IRQ); 608 609 /* structure copy other fields */ 610 resbuf.Data.ExtendedIrq = 611 link->possible_resources.Data.ExtendedIrq; 612 resbuf.Data.ExtendedIrq.NumberOfInterrupts = 1; 613 resbuf.Data.ExtendedIrq.Interrupts[0] = irq; 614 break; 615 default: 616 printf("acpi link set: %s resource is not an IRQ (%d)\n", 617 acpi_name(link->handle), link->possible_resources.Id); 618 return_ACPI_STATUS (AE_TYPE); 619 } 620 621 error = acpi_AppendBufferResource(&crsbuf, &resbuf); 622 if (ACPI_FAILURE(error)) { 623 printf("acpi link set: AppendBuffer failed for %s\n", 624 acpi_name(link->handle)); 625 return_ACPI_STATUS (error); 626 } 627 if (crsbuf.Pointer == NULL) { 628 printf("acpi link set: AppendBuffer returned empty for %s\n", 629 acpi_name(link->handle)); 630 return_ACPI_STATUS (AE_NO_MEMORY); 631 } 632 633 /* Make the new IRQ active via the link's _SRS method. */ 634 error = AcpiSetCurrentResources(link->handle, &crsbuf); 635 if (ACPI_FAILURE(error)) { 636 printf("acpi link set: _SRS failed for link %s - %s\n", 637 acpi_name(link->handle), AcpiFormatException(error)); 638 goto out; 639 } 640 link->flags |= ACPI_LINK_ROUTED; 641 link->current_irq = 0; 642 643 /* 644 * Many systems always return invalid values for current settings 645 * (_CRS). Since we can't trust the value returned, we have to 646 * assume we were successful. 647 */ 648 error = acpi_pci_link_get_current_irq(link, &link->current_irq); 649 if (ACPI_FAILURE(error)) { 650 if (bootverbose) 651 printf("acpi link set: _CRS failed for link %s - %s\n", 652 acpi_name(link->handle), 653 AcpiFormatException(error)); 654 error = AE_OK; 655 } 656 if (link->current_irq != irq) { 657 if (bootverbose) 658 printf("acpi link set: curr irq %d != %d for %s\n", 659 link->current_irq, irq, acpi_name(link->handle)); 660 link->current_irq = irq; 661 } 662 663 out: 664 if (crsbuf.Pointer) 665 AcpiOsFree(crsbuf.Pointer); 666 return_ACPI_STATUS (error); 667 } 668 669 /* 670 * Auto arbitration for boot-disabled devices 671 */ 672 673 static void 674 acpi_pci_link_bootdisabled_dump(void) 675 { 676 int i; 677 int irq; 678 struct acpi_pci_link_entry *link; 679 680 ACPI_SERIAL_ASSERT(pci_link); 681 TAILQ_FOREACH(link, &acpi_pci_link_entries, links) { 682 /* boot-disabled link only. */ 683 if (link->current_irq != 0) 684 continue; 685 686 printf("%s (references %d, priority %d):\n", 687 acpi_name(link->handle), link->references, link->priority); 688 printf("\tinterrupts:\t"); 689 for (i = 0; i < link->number_of_interrupts; i++) { 690 irq = link->sorted_irq[i]; 691 printf("%6d", irq); 692 } 693 printf("\n"); 694 printf("\tpenalty:\t"); 695 for (i = 0; i < link->number_of_interrupts; i++) { 696 irq = link->sorted_irq[i]; 697 printf("%6d", irq_penalty[irq]); 698 } 699 printf("\n"); 700 } 701 } 702 703 /* 704 * Heuristics for choosing IRQs. We start with some static penalties, 705 * update them based on what IRQs are currently in use, then sort the 706 * result. This works ok but is not perfect. 707 * 708 * The PCI BIOS $PIR table offers "preferred PCI interrupts", but ACPI 709 * doesn't seem to offer a similar mechanism, so picking a good 710 * interrupt here is a difficult task. 711 */ 712 static void 713 acpi_pci_link_init_irq_penalty(void) 714 { 715 716 bzero(irq_penalty, sizeof(irq_penalty)); 717 718 /* 0, 1, 2, 8: timer, keyboard, cascade, RTC */ 719 irq_penalty[0] = 100000; 720 irq_penalty[1] = 100000; 721 irq_penalty[2] = 100000; 722 irq_penalty[8] = 100000; 723 724 /* 13, 14, 15: npx, ATA controllers */ 725 irq_penalty[13] = 50000; 726 irq_penalty[14] = 50000; 727 irq_penalty[15] = 50000; 728 729 /* 3, 4, 6, 7, 12: typically used by legacy hardware */ 730 irq_penalty[3] = 5000; 731 irq_penalty[4] = 5000; 732 irq_penalty[6] = 5000; 733 irq_penalty[7] = 5000; 734 irq_penalty[12] = 5000; 735 736 /* 5: sometimes legacy sound cards */ 737 irq_penalty[5] = 50; 738 } 739 740 static int 741 link_exclusive(ACPI_RESOURCE *res) 742 { 743 744 if (res == NULL || 745 (res->Id != ACPI_RSTYPE_IRQ && 746 res->Id != ACPI_RSTYPE_EXT_IRQ)) 747 return (FALSE); 748 749 if ((res->Id == ACPI_RSTYPE_IRQ && 750 res->Data.Irq.SharedExclusive == ACPI_EXCLUSIVE) || 751 (res->Id == ACPI_RSTYPE_EXT_IRQ && 752 res->Data.ExtendedIrq.SharedExclusive == ACPI_EXCLUSIVE)) 753 return (TRUE); 754 755 return (FALSE); 756 } 757 758 static void 759 acpi_pci_link_update_irq_penalty(device_t dev, int busno) 760 { 761 int i; 762 int irq; 763 int rid; 764 struct resource *res; 765 struct acpi_prt_entry *entry; 766 struct acpi_pci_link_entry *link; 767 768 ACPI_SERIAL_ASSERT(pci_link); 769 TAILQ_FOREACH(entry, &acpi_prt_entries, links) { 770 if (entry->busno != busno) 771 continue; 772 773 /* Impossible? */ 774 link = entry->pci_link; 775 if (link == NULL) 776 continue; 777 778 /* Update penalties for all possible settings of this link. */ 779 for (i = 0; i < link->number_of_interrupts; i++) { 780 /* give 10 for each possible IRQs. */ 781 irq = link->interrupts[i]; 782 irq_penalty[irq] += 10; 783 784 /* higher penalty if exclusive. */ 785 if (link_exclusive(&link->possible_resources)) 786 irq_penalty[irq] += 100; 787 788 /* XXX try to get this IRQ in non-sharable mode. */ 789 rid = 0; 790 res = bus_alloc_resource(dev, SYS_RES_IRQ, 791 &rid, irq, irq, 1, 0); 792 if (res != NULL) { 793 bus_release_resource(dev, SYS_RES_IRQ, 794 rid, res); 795 } else { 796 /* this is in use, give 10. */ 797 irq_penalty[irq] += 10; 798 } 799 } 800 801 /* initialize `sorted' possible IRQs. */ 802 bcopy(link->interrupts, link->sorted_irq, 803 sizeof(link->sorted_irq)); 804 } 805 } 806 807 static void 808 acpi_pci_link_set_bootdisabled_priority(void) 809 { 810 int sum_penalty; 811 int i; 812 int irq; 813 struct acpi_pci_link_entry *link, *link_pri; 814 TAILQ_HEAD(, acpi_pci_link_entry) sorted_list; 815 816 ACPI_SERIAL_ASSERT(pci_link); 817 818 /* reset priority for all links. */ 819 TAILQ_FOREACH(link, &acpi_pci_link_entries, links) 820 link->priority = 0; 821 822 TAILQ_FOREACH(link, &acpi_pci_link_entries, links) { 823 /* If already routed, don't include in arbitration. */ 824 if (link->flags & ACPI_LINK_ROUTED) { 825 link->priority = 0; 826 continue; 827 } 828 829 /* 830 * Calculate the priority for each boot-disabled links. 831 * o IRQ penalty indicates difficulty to use. 832 * o #references for devices indicates importance of the link. 833 * o #interrupts indicates flexibility of the link. 834 */ 835 sum_penalty = 0; 836 for (i = 0; i < link->number_of_interrupts; i++) { 837 irq = link->interrupts[i]; 838 sum_penalty += irq_penalty[irq]; 839 } 840 841 link->priority = (sum_penalty * link->references) / 842 link->number_of_interrupts; 843 } 844 845 /* 846 * Sort PCI links based on the priority. 847 * XXX Any other better ways rather than using work list? 848 */ 849 TAILQ_INIT(&sorted_list); 850 while (!TAILQ_EMPTY(&acpi_pci_link_entries)) { 851 link = TAILQ_FIRST(&acpi_pci_link_entries); 852 /* find an entry which has the highest priority. */ 853 TAILQ_FOREACH(link_pri, &acpi_pci_link_entries, links) 854 if (link->priority < link_pri->priority) 855 link = link_pri; 856 857 /* move to work list. */ 858 TAILQ_REMOVE(&acpi_pci_link_entries, link, links); 859 TAILQ_INSERT_TAIL(&sorted_list, link, links); 860 } 861 862 while (!TAILQ_EMPTY(&sorted_list)) { 863 /* move them back to the list, one by one... */ 864 link = TAILQ_FIRST(&sorted_list); 865 TAILQ_REMOVE(&sorted_list, link, links); 866 TAILQ_INSERT_TAIL(&acpi_pci_link_entries, link, links); 867 } 868 } 869 870 static void 871 acpi_pci_link_fixup_bootdisabled_link(void) 872 { 873 int i, j; 874 int irq1, irq2; 875 struct acpi_pci_link_entry *link; 876 877 ACPI_SERIAL_ASSERT(pci_link); 878 879 TAILQ_FOREACH(link, &acpi_pci_link_entries, links) { 880 /* Ignore links that have been routed already. */ 881 if (link->flags & ACPI_LINK_ROUTED) 882 continue; 883 884 /* sort IRQs based on their penalty descending. */ 885 for (i = 0; i < link->number_of_interrupts; i++) { 886 irq1 = link->sorted_irq[i]; 887 for (j = i + 1; j < link->number_of_interrupts; j++) { 888 irq2 = link->sorted_irq[j]; 889 if (irq_penalty[irq1] < irq_penalty[irq2]) { 890 continue; 891 } 892 link->sorted_irq[i] = irq2; 893 link->sorted_irq[j] = irq1; 894 irq1 = irq2; 895 } 896 } 897 } 898 899 if (bootverbose) { 900 printf("ACPI PCI link arbitrated settings:\n"); 901 acpi_pci_link_bootdisabled_dump(); 902 } 903 } 904 905 /* 906 * Public interface 907 */ 908 909 int 910 acpi_pci_link_config(device_t dev, ACPI_BUFFER *prtbuf, int busno) 911 { 912 struct acpi_prt_entry *entry; 913 ACPI_PCI_ROUTING_TABLE *prt; 914 u_int8_t *prtp; 915 ACPI_STATUS error; 916 int ret; 917 static int first_time = 1; 918 919 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 920 921 if (acpi_disabled("pci_link")) 922 return (0); 923 924 ret = -1; 925 ACPI_SERIAL_BEGIN(pci_link); 926 if (first_time) { 927 TAILQ_INIT(&acpi_prt_entries); 928 TAILQ_INIT(&acpi_pci_link_entries); 929 acpi_pci_link_init_irq_penalty(); 930 first_time = 0; 931 } 932 933 if (prtbuf == NULL) 934 goto out; 935 936 prtp = prtbuf->Pointer; 937 if (prtp == NULL) /* didn't get routing table */ 938 goto out; 939 940 /* scan the PCI Routing Table */ 941 for (;;) { 942 prt = (ACPI_PCI_ROUTING_TABLE *)prtp; 943 944 if (prt->Length == 0) /* end of table */ 945 break; 946 947 error = acpi_pci_link_add_prt(dev, prt, busno); 948 if (ACPI_FAILURE(error)) { 949 ACPI_DEBUG_PRINT((ACPI_DB_WARN, 950 "couldn't add PCI interrupt link entry - %s\n", 951 AcpiFormatException(error))); 952 } 953 954 /* skip to next entry */ 955 prtp += prt->Length; 956 } 957 958 if (bootverbose) { 959 printf("ACPI PCI link initial configuration:\n"); 960 TAILQ_FOREACH(entry, &acpi_prt_entries, links) { 961 if (entry->busno != busno) 962 continue; 963 acpi_pci_link_entry_dump(entry); 964 } 965 } 966 967 /* manual configuration. */ 968 TAILQ_FOREACH(entry, &acpi_prt_entries, links) { 969 int irq; 970 char prthint[32]; 971 972 if (entry->busno != busno) 973 continue; 974 975 snprintf(prthint, sizeof(prthint), 976 "hw.acpi.pci.link.%d.%d.%d.irq", entry->busno, 977 (int)((entry->prt.Address & 0xffff0000) >> 16), 978 (int)entry->prt.Pin); 979 980 if (getenv_int(prthint, &irq) == 0) 981 continue; 982 983 if (acpi_pci_link_is_valid_irq(entry->pci_link, irq)) { 984 error = acpi_pci_link_set_irq(entry->pci_link, irq); 985 if (ACPI_FAILURE(error)) { 986 ACPI_DEBUG_PRINT((ACPI_DB_WARN, 987 "couldn't set IRQ to link entry %s - %s\n", 988 acpi_name(entry->pci_link->handle), 989 AcpiFormatException(error))); 990 } 991 continue; 992 } 993 994 /* 995 * Do auto arbitration for this device's PCI link 996 * if hint value 0 is specified. 997 */ 998 if (irq == 0) 999 entry->pci_link->current_irq = 0; 1000 } 1001 ret = 0; 1002 1003 out: 1004 ACPI_SERIAL_END(pci_link); 1005 return (ret); 1006 } 1007 1008 int 1009 acpi_pci_link_resume(device_t dev) 1010 { 1011 struct acpi_prt_entry *entry; 1012 struct acpi_pci_link_entry *link; 1013 ACPI_STATUS error; 1014 1015 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1016 1017 if (acpi_disabled("pci_link")) 1018 return (0); 1019 1020 /* Walk through all PRT entries for this PCI bridge. */ 1021 ACPI_SERIAL_BEGIN(pci_link); 1022 TAILQ_FOREACH(entry, &acpi_prt_entries, links) { 1023 if (entry->pcidev != dev || entry->pci_link == NULL) 1024 continue; 1025 link = entry->pci_link; 1026 1027 /* If it's not routed, skip re-programming. */ 1028 if ((link->flags & ACPI_LINK_ROUTED) == 0) 1029 continue; 1030 link->flags &= ~ACPI_LINK_ROUTED; 1031 1032 /* Program it to the same setting as before suspend. */ 1033 error = acpi_pci_link_set_irq(link, link->current_irq); 1034 if (ACPI_FAILURE(error)) { 1035 ACPI_DEBUG_PRINT((ACPI_DB_WARN, 1036 "couldn't set IRQ to link entry %s - %s\n", 1037 acpi_name(link->handle), 1038 AcpiFormatException(error))); 1039 } 1040 } 1041 ACPI_SERIAL_END(pci_link); 1042 1043 return (0); 1044 } 1045 1046 /* 1047 * Look up a PRT entry for the given device. We match based on the slot 1048 * number (high word of Address) and pin number (note that ACPI uses 0 1049 * for INTA). 1050 * 1051 * Note that the low word of the Address field (function number) is 1052 * required by the specification to be 0xffff. We don't risk checking 1053 * it here. 1054 */ 1055 struct acpi_prt_entry * 1056 acpi_pci_find_prt(device_t pcibdev, device_t dev, int pin) 1057 { 1058 struct acpi_prt_entry *entry; 1059 ACPI_PCI_ROUTING_TABLE *prt; 1060 1061 ACPI_SERIAL_BEGIN(pci_link); 1062 TAILQ_FOREACH(entry, &acpi_prt_entries, links) { 1063 prt = &entry->prt; 1064 if (entry->busno == pci_get_bus(dev) && 1065 (prt->Address & 0xffff0000) >> 16 == pci_get_slot(dev) && 1066 prt->Pin == pin) 1067 break; 1068 } 1069 ACPI_SERIAL_END(pci_link); 1070 return (entry); 1071 } 1072 1073 /* 1074 * Perform the actual programming for this link. We attempt to route an 1075 * IRQ, first the one set by the BIOS, and then a priority-sorted list. 1076 * Only do the programming once per link. 1077 */ 1078 int 1079 acpi_pci_link_route(device_t dev, struct acpi_prt_entry *prt) 1080 { 1081 struct acpi_pci_link_entry *link; 1082 int busno, i, irq; 1083 ACPI_RESOURCE crsres; 1084 ACPI_STATUS status; 1085 1086 busno = pci_get_bus(dev); 1087 link = prt->pci_link; 1088 irq = PCI_INVALID_IRQ; 1089 ACPI_SERIAL_BEGIN(pci_link); 1090 if (link == NULL || link->number_of_interrupts == 0) 1091 goto out; 1092 1093 /* If already routed, just return the current setting. */ 1094 if (link->flags & ACPI_LINK_ROUTED) { 1095 irq = link->current_irq; 1096 goto out; 1097 } 1098 1099 /* Update all IRQ weights to determine our priority list. */ 1100 acpi_pci_link_update_irq_penalty(prt->pcidev, busno); 1101 acpi_pci_link_set_bootdisabled_priority(); 1102 acpi_pci_link_fixup_bootdisabled_link(); 1103 1104 /* 1105 * First, attempt to route the initial IRQ, if valid, since it was 1106 * the one set up by the BIOS. If this fails, route according to 1107 * our priority-sorted list of IRQs. 1108 */ 1109 status = AE_NOT_FOUND; 1110 irq = link->initial_irq; 1111 if (irq) 1112 status = acpi_pci_link_set_irq(link, irq); 1113 for (i = 0; ACPI_FAILURE(status) && i < link->number_of_interrupts; 1114 i++) { 1115 irq = link->sorted_irq[i]; 1116 status = acpi_pci_link_set_irq(link, irq); 1117 if (ACPI_FAILURE(status)) { 1118 device_printf(dev, "_SRS failed, irq %d via %s\n", 1119 irq, acpi_name(link->handle)); 1120 } 1121 } 1122 if (ACPI_FAILURE(status)) { 1123 irq = PCI_INVALID_IRQ; 1124 goto out; 1125 } 1126 1127 /* Update the penalty now that there's another user for this IRQ. */ 1128 irq_penalty[irq] += 10 * link->references; 1129 1130 /* Configure trigger/polarity for the new IRQ. */ 1131 bcopy(&link->possible_resources, &crsres, sizeof(crsres)); 1132 if (crsres.Id == ACPI_RSTYPE_IRQ) { 1133 crsres.Data.Irq.NumberOfInterrupts = 1; 1134 crsres.Data.Irq.Interrupts[0] = irq; 1135 } else { 1136 crsres.Data.ExtendedIrq.NumberOfInterrupts = 1; 1137 crsres.Data.ExtendedIrq.Interrupts[0] = irq; 1138 } 1139 acpi_config_intr(dev, &crsres); 1140 1141 out: 1142 ACPI_SERIAL_END(pci_link); 1143 return (irq); 1144 } 1145