1/*- 2 * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 3 * Copyright (c) 2000, Michael Smith <msmith@freebsd.org> 4 * Copyright (c) 2000, BSDi 5 * Copyright (c) 2004, John Baldwin <jhb@FreeBSD.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice unmodified, this list of conditions, and the following 13 * disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h>
|
31__FBSDID("$FreeBSD: head/sys/i386/pci/pci_pir.c 267992 2014-06-28 03:56:17Z hselasky $");
|
31__FBSDID("$FreeBSD: head/sys/i386/pci/pci_pir.c 282274 2015-04-30 15:48:48Z jhb $"); |
32 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/bus.h> 36#include <sys/kernel.h> 37#include <sys/malloc.h> 38#include <sys/module.h> 39#include <sys/sysctl.h> 40#include <vm/vm.h> 41#include <vm/pmap.h> 42#include <vm/vm_param.h> 43#include <machine/md_var.h> 44#include <dev/pci/pcivar.h> 45#include <dev/pci/pcireg.h> 46#include <machine/pci_cfgreg.h> 47#include <machine/segments.h> 48#include <machine/pc/bios.h> 49 50#define NUM_ISA_INTERRUPTS 16 51 52/* 53 * A link device. Loosely based on the ACPI PCI link device. This doesn't 54 * try to support priorities for different ISA interrupts. 55 */ 56struct pci_link { 57 TAILQ_ENTRY(pci_link) pl_links; 58 uint8_t pl_id; 59 uint8_t pl_irq; 60 uint16_t pl_irqmask; 61 int pl_references; 62 int pl_routed; 63}; 64 65struct pci_link_lookup { 66 struct pci_link **pci_link_ptr; 67 int bus; 68 int device; 69 int pin; 70}; 71 72struct pci_dev_lookup { 73 uint8_t link; 74 int bus; 75 int device; 76 int pin; 77}; 78 79typedef void pir_entry_handler(struct PIR_entry *entry, 80 struct PIR_intpin* intpin, void *arg); 81 82static void pci_print_irqmask(u_int16_t irqs); 83static int pci_pir_biosroute(int bus, int device, int func, int pin, 84 int irq); 85static int pci_pir_choose_irq(struct pci_link *pci_link, int irqmask); 86static void pci_pir_create_links(struct PIR_entry *entry, 87 struct PIR_intpin *intpin, void *arg); 88static void pci_pir_dump_links(void); 89static struct pci_link *pci_pir_find_link(uint8_t link_id); 90static void pci_pir_find_link_handler(struct PIR_entry *entry, 91 struct PIR_intpin *intpin, void *arg); 92static void pci_pir_initial_irqs(struct PIR_entry *entry, 93 struct PIR_intpin *intpin, void *arg); 94static void pci_pir_parse(void); 95static uint8_t pci_pir_search_irq(int bus, int device, int pin); 96static int pci_pir_valid_irq(struct pci_link *pci_link, int irq); 97static void pci_pir_walk_table(pir_entry_handler *handler, void *arg); 98 99static MALLOC_DEFINE(M_PIR, "$PIR", "$PIR structures"); 100 101static struct PIR_table *pci_route_table; 102static device_t pir_device; 103static int pci_route_count, pir_bios_irqs, pir_parsed; 104static TAILQ_HEAD(, pci_link) pci_links; 105static int pir_interrupt_weight[NUM_ISA_INTERRUPTS]; 106 107/* sysctl vars */ 108SYSCTL_DECL(_hw_pci); 109 110/* XXX this likely should live in a header file */ 111#ifdef PC98 112/* IRQs 3, 5, 7, 9, 10, 11, 12, 13 */ 113#define PCI_IRQ_OVERRIDE_MASK 0x3e68 114#else 115/* IRQs 3, 4, 5, 6, 7, 9, 10, 11, 12, 14, 15 */ 116#define PCI_IRQ_OVERRIDE_MASK 0xdef8 117#endif 118 119static uint32_t pci_irq_override_mask = PCI_IRQ_OVERRIDE_MASK; 120SYSCTL_INT(_hw_pci, OID_AUTO, irq_override_mask, CTLFLAG_RDTUN, 121 &pci_irq_override_mask, PCI_IRQ_OVERRIDE_MASK, 122 "Mask of allowed irqs to try to route when it has no good clue about\n" 123 "which irqs it should use."); 124 125/* 126 * Look for the interrupt routing table. 127 * 128 * We use PCI BIOS's PIR table if it's available. $PIR is the standard way 129 * to do this. Sadly, some machines are not standards conforming and have 130 * _PIR instead. We shrug and cope by looking for both. 131 */ 132void 133pci_pir_open(void) 134{ 135 struct PIR_table *pt; 136 uint32_t sigaddr; 137 int i; 138 uint8_t ck, *cv; 139
|
140#ifdef XEN
141 return;
142#else
|
140 /* Don't try if we've already found a table. */ 141 if (pci_route_table != NULL) 142 return; 143 144 /* Look for $PIR and then _PIR. */ 145 sigaddr = bios_sigsearch(0, "$PIR", 4, 16, 0); 146 if (sigaddr == 0) 147 sigaddr = bios_sigsearch(0, "_PIR", 4, 16, 0); 148 if (sigaddr == 0) 149 return;
|
153#endif
|
150 |
151 /* If we found something, check the checksum and length. */ 152 /* XXX - Use pmap_mapdev()? */ 153 pt = (struct PIR_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr); 154 if (pt->pt_header.ph_length <= sizeof(struct PIR_header)) 155 return; 156 for (cv = (u_int8_t *)pt, ck = 0, i = 0; 157 i < (pt->pt_header.ph_length); i++) 158 ck += cv[i]; 159 if (ck != 0) 160 return; 161 162 /* Ok, we've got a valid table. */ 163 pci_route_table = pt; 164 pci_route_count = (pt->pt_header.ph_length - 165 sizeof(struct PIR_header)) / 166 sizeof(struct PIR_entry); 167} 168 169/* 170 * Find the pci_link structure for a given link ID. 171 */ 172static struct pci_link * 173pci_pir_find_link(uint8_t link_id) 174{ 175 struct pci_link *pci_link; 176 177 TAILQ_FOREACH(pci_link, &pci_links, pl_links) { 178 if (pci_link->pl_id == link_id) 179 return (pci_link); 180 } 181 return (NULL); 182} 183 184/* 185 * Find the link device associated with a PCI device in the table. 186 */ 187static void 188pci_pir_find_link_handler(struct PIR_entry *entry, struct PIR_intpin *intpin, 189 void *arg) 190{ 191 struct pci_link_lookup *lookup; 192 193 lookup = (struct pci_link_lookup *)arg; 194 if (entry->pe_bus == lookup->bus && 195 entry->pe_device == lookup->device && 196 intpin - entry->pe_intpin == lookup->pin) 197 *lookup->pci_link_ptr = pci_pir_find_link(intpin->link); 198} 199 200/* 201 * Check to see if a possible IRQ setting is valid. 202 */ 203static int 204pci_pir_valid_irq(struct pci_link *pci_link, int irq) 205{ 206 207 if (!PCI_INTERRUPT_VALID(irq)) 208 return (0); 209 return (pci_link->pl_irqmask & (1 << irq)); 210} 211 212/* 213 * Walk the $PIR executing the worker function for each valid intpin entry 214 * in the table. The handler is passed a pointer to both the entry and 215 * the intpin in the entry. 216 */ 217static void 218pci_pir_walk_table(pir_entry_handler *handler, void *arg) 219{ 220 struct PIR_entry *entry; 221 struct PIR_intpin *intpin; 222 int i, pin; 223 224 entry = &pci_route_table->pt_entry[0]; 225 for (i = 0; i < pci_route_count; i++, entry++) { 226 intpin = &entry->pe_intpin[0]; 227 for (pin = 0; pin < 4; pin++, intpin++) 228 if (intpin->link != 0) 229 handler(entry, intpin, arg); 230 } 231} 232 233static void 234pci_pir_create_links(struct PIR_entry *entry, struct PIR_intpin *intpin, 235 void *arg) 236{ 237 struct pci_link *pci_link; 238 239 pci_link = pci_pir_find_link(intpin->link); 240 if (pci_link != NULL) { 241 pci_link->pl_references++; 242 if (intpin->irqs != pci_link->pl_irqmask) { 243 if (bootverbose) 244 printf( 245 "$PIR: Entry %d.%d.INT%c has different mask for link %#x, merging\n", 246 entry->pe_bus, entry->pe_device, 247 (intpin - entry->pe_intpin) + 'A', 248 pci_link->pl_id); 249 pci_link->pl_irqmask &= intpin->irqs; 250 } 251 } else { 252 pci_link = malloc(sizeof(struct pci_link), M_PIR, M_WAITOK); 253 pci_link->pl_id = intpin->link; 254 pci_link->pl_irqmask = intpin->irqs; 255 pci_link->pl_irq = PCI_INVALID_IRQ; 256 pci_link->pl_references = 1; 257 pci_link->pl_routed = 0; 258 TAILQ_INSERT_TAIL(&pci_links, pci_link, pl_links); 259 } 260} 261 262/* 263 * Look to see if any of the function on the PCI device at bus/device have 264 * an interrupt routed to intpin 'pin' by the BIOS. 265 */ 266static uint8_t 267pci_pir_search_irq(int bus, int device, int pin) 268{ 269 uint32_t value; 270 uint8_t func, maxfunc; 271 272 /* See if we have a valid device at function 0. */ 273 value = pci_cfgregread(bus, device, 0, PCIR_HDRTYPE, 1); 274 if ((value & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) 275 return (PCI_INVALID_IRQ); 276 if (value & PCIM_MFDEV) 277 maxfunc = PCI_FUNCMAX; 278 else 279 maxfunc = 0; 280 281 /* Scan all possible functions at this device. */ 282 for (func = 0; func <= maxfunc; func++) { 283 value = pci_cfgregread(bus, device, func, PCIR_DEVVENDOR, 4); 284 if (value == 0xffffffff) 285 continue; 286 value = pci_cfgregread(bus, device, func, PCIR_INTPIN, 1); 287 288 /* 289 * See if it uses the pin in question. Note that the passed 290 * in pin uses 0 for A, .. 3 for D whereas the intpin 291 * register uses 0 for no interrupt, 1 for A, .. 4 for D. 292 */ 293 if (value != pin + 1) 294 continue; 295 value = pci_cfgregread(bus, device, func, PCIR_INTLINE, 1); 296 if (bootverbose) 297 printf( 298 "$PIR: Found matching pin for %d.%d.INT%c at func %d: %d\n", 299 bus, device, pin + 'A', func, value); 300 if (value != PCI_INVALID_IRQ) 301 return (value); 302 } 303 return (PCI_INVALID_IRQ); 304} 305 306/* 307 * Try to initialize IRQ based on this device's IRQ. 308 */ 309static void 310pci_pir_initial_irqs(struct PIR_entry *entry, struct PIR_intpin *intpin, 311 void *arg) 312{ 313 struct pci_link *pci_link; 314 uint8_t irq, pin; 315 316 pin = intpin - entry->pe_intpin; 317 pci_link = pci_pir_find_link(intpin->link); 318 irq = pci_pir_search_irq(entry->pe_bus, entry->pe_device, pin); 319 if (irq == PCI_INVALID_IRQ || irq == pci_link->pl_irq) 320 return; 321 322 /* Don't trust any BIOS IRQs greater than 15. */ 323 if (irq >= NUM_ISA_INTERRUPTS) { 324 printf( 325 "$PIR: Ignoring invalid BIOS IRQ %d from %d.%d.INT%c for link %#x\n", 326 irq, entry->pe_bus, entry->pe_device, pin + 'A', 327 pci_link->pl_id); 328 return; 329 } 330 331 /* 332 * If we don't have an IRQ for this link yet, then we trust the 333 * BIOS, even if it seems invalid from the $PIR entries. 334 */ 335 if (pci_link->pl_irq == PCI_INVALID_IRQ) { 336 if (!pci_pir_valid_irq(pci_link, irq)) 337 printf( 338 "$PIR: Using invalid BIOS IRQ %d from %d.%d.INT%c for link %#x\n", 339 irq, entry->pe_bus, entry->pe_device, pin + 'A', 340 pci_link->pl_id); 341 pci_link->pl_irq = irq; 342 pci_link->pl_routed = 1; 343 return; 344 } 345 346 /* 347 * We have an IRQ and it doesn't match the current IRQ for this 348 * link. If the new IRQ is invalid, then warn about it and ignore 349 * it. If the old IRQ is invalid and the new IRQ is valid, then 350 * prefer the new IRQ instead. If both IRQs are valid, then just 351 * use the first one. Note that if we ever get into this situation 352 * we are having to guess which setting the BIOS actually routed. 353 * Perhaps we should just give up instead. 354 */ 355 if (!pci_pir_valid_irq(pci_link, irq)) { 356 printf( 357 "$PIR: BIOS IRQ %d for %d.%d.INT%c is not valid for link %#x\n", 358 irq, entry->pe_bus, entry->pe_device, pin + 'A', 359 pci_link->pl_id); 360 } else if (!pci_pir_valid_irq(pci_link, pci_link->pl_irq)) { 361 printf( 362"$PIR: Preferring valid BIOS IRQ %d from %d.%d.INT%c for link %#x to IRQ %d\n", 363 irq, entry->pe_bus, entry->pe_device, pin + 'A', 364 pci_link->pl_id, pci_link->pl_irq); 365 pci_link->pl_irq = irq; 366 pci_link->pl_routed = 1; 367 } else 368 printf( 369 "$PIR: BIOS IRQ %d for %d.%d.INT%c does not match link %#x irq %d\n", 370 irq, entry->pe_bus, entry->pe_device, pin + 'A', 371 pci_link->pl_id, pci_link->pl_irq); 372} 373 374/* 375 * Parse $PIR to enumerate link devices and attempt to determine their 376 * initial state. This could perhaps be cleaner if we had drivers for the 377 * various interrupt routers as they could read the initial IRQ for each 378 * link. 379 */ 380static void 381pci_pir_parse(void) 382{ 383 char tunable_buffer[64]; 384 struct pci_link *pci_link; 385 int i, irq; 386 387 /* Only parse once. */ 388 if (pir_parsed) 389 return; 390 pir_parsed = 1; 391 392 /* Enumerate link devices. */ 393 TAILQ_INIT(&pci_links); 394 pci_pir_walk_table(pci_pir_create_links, NULL); 395 if (bootverbose) { 396 printf("$PIR: Links after initial probe:\n"); 397 pci_pir_dump_links(); 398 } 399 400 /* 401 * Check to see if the BIOS has already routed any of the links by 402 * checking each device connected to each link to see if it has a 403 * valid IRQ. 404 */ 405 pci_pir_walk_table(pci_pir_initial_irqs, NULL); 406 if (bootverbose) { 407 printf("$PIR: Links after initial IRQ discovery:\n"); 408 pci_pir_dump_links(); 409 } 410 411 /* 412 * Allow the user to override the IRQ for a given link device. We 413 * allow invalid IRQs to be specified but warn about them. An IRQ 414 * of 255 or 0 clears any preset IRQ. 415 */ 416 i = 0; 417 TAILQ_FOREACH(pci_link, &pci_links, pl_links) { 418 snprintf(tunable_buffer, sizeof(tunable_buffer), 419 "hw.pci.link.%#x.irq", pci_link->pl_id); 420 if (getenv_int(tunable_buffer, &irq) == 0) 421 continue; 422 if (irq == 0) 423 irq = PCI_INVALID_IRQ; 424 if (irq != PCI_INVALID_IRQ && 425 !pci_pir_valid_irq(pci_link, irq) && bootverbose) 426 printf( 427 "$PIR: Warning, IRQ %d for link %#x is not listed as valid\n", 428 irq, pci_link->pl_id); 429 pci_link->pl_routed = 0; 430 pci_link->pl_irq = irq; 431 i = 1; 432 } 433 if (bootverbose && i) { 434 printf("$PIR: Links after tunable overrides:\n"); 435 pci_pir_dump_links(); 436 } 437 438 /* 439 * Build initial interrupt weights as well as bitmap of "known-good" 440 * IRQs that the BIOS has already used for PCI link devices. 441 */ 442 TAILQ_FOREACH(pci_link, &pci_links, pl_links) { 443 if (!PCI_INTERRUPT_VALID(pci_link->pl_irq)) 444 continue; 445 pir_bios_irqs |= 1 << pci_link->pl_irq; 446 pir_interrupt_weight[pci_link->pl_irq] += 447 pci_link->pl_references; 448 } 449 if (bootverbose) { 450 printf("$PIR: IRQs used by BIOS: "); 451 pci_print_irqmask(pir_bios_irqs); 452 printf("\n"); 453 printf("$PIR: Interrupt Weights:\n[ "); 454 for (i = 0; i < NUM_ISA_INTERRUPTS; i++) 455 printf(" %3d", i); 456 printf(" ]\n[ "); 457 for (i = 0; i < NUM_ISA_INTERRUPTS; i++) 458 printf(" %3d", pir_interrupt_weight[i]); 459 printf(" ]\n"); 460 } 461} 462 463/* 464 * Use the PCI BIOS to route an interrupt for a given device. 465 * 466 * Input: 467 * AX = PCIBIOS_ROUTE_INTERRUPT 468 * BH = bus 469 * BL = device [7:3] / function [2:0] 470 * CH = IRQ 471 * CL = Interrupt Pin (0x0A = A, ... 0x0D = D) 472 */ 473static int 474pci_pir_biosroute(int bus, int device, int func, int pin, int irq) 475{ 476 struct bios_regs args; 477 478 args.eax = PCIBIOS_ROUTE_INTERRUPT; 479 args.ebx = (bus << 8) | (device << 3) | func; 480 args.ecx = (irq << 8) | (0xa + pin);
|
484#ifdef XEN
485 return (0);
486#else
|
481 return (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)));
|
488#endif
|
482} 483 484 485/* 486 * Route a PCI interrupt using a link device from the $PIR. 487 */ 488int 489pci_pir_route_interrupt(int bus, int device, int func, int pin) 490{ 491 struct pci_link_lookup lookup; 492 struct pci_link *pci_link; 493 int error, irq; 494 495 if (pci_route_table == NULL) 496 return (PCI_INVALID_IRQ); 497 498 /* Lookup link device for this PCI device/pin. */ 499 pci_link = NULL; 500 lookup.bus = bus; 501 lookup.device = device; 502 lookup.pin = pin - 1; 503 lookup.pci_link_ptr = &pci_link; 504 pci_pir_walk_table(pci_pir_find_link_handler, &lookup); 505 if (pci_link == NULL) { 506 printf("$PIR: No matching entry for %d.%d.INT%c\n", bus, 507 device, pin - 1 + 'A'); 508 return (PCI_INVALID_IRQ); 509 } 510 511 /* 512 * Pick a new interrupt if we don't have one already. We look 513 * for an interrupt from several different sets. First, if 514 * this link only has one valid IRQ, use that. Second, we 515 * check the set of PCI only interrupts from the $PIR. Third, 516 * we check the set of known-good interrupts that the BIOS has 517 * already used. Lastly, we check the "all possible valid 518 * IRQs" set. 519 */ 520 if (!PCI_INTERRUPT_VALID(pci_link->pl_irq)) { 521 if (pci_link->pl_irqmask != 0 && powerof2(pci_link->pl_irqmask)) 522 irq = ffs(pci_link->pl_irqmask) - 1; 523 else 524 irq = pci_pir_choose_irq(pci_link, 525 pci_route_table->pt_header.ph_pci_irqs); 526 if (!PCI_INTERRUPT_VALID(irq)) 527 irq = pci_pir_choose_irq(pci_link, pir_bios_irqs); 528 if (!PCI_INTERRUPT_VALID(irq)) 529 irq = pci_pir_choose_irq(pci_link, 530 pci_irq_override_mask); 531 if (!PCI_INTERRUPT_VALID(irq)) { 532 if (bootverbose) 533 printf( 534 "$PIR: Failed to route interrupt for %d:%d INT%c\n", 535 bus, device, pin - 1 + 'A'); 536 return (PCI_INVALID_IRQ); 537 } 538 pci_link->pl_irq = irq; 539 } 540 541 /* Ask the BIOS to route this IRQ if we haven't done so already. */ 542 if (!pci_link->pl_routed) { 543 error = pci_pir_biosroute(bus, device, func, pin - 1, 544 pci_link->pl_irq); 545 546 /* Ignore errors when routing a unique interrupt. */ 547 if (error && !powerof2(pci_link->pl_irqmask)) { 548 printf("$PIR: ROUTE_INTERRUPT failed.\n"); 549 return (PCI_INVALID_IRQ); 550 } 551 pci_link->pl_routed = 1; 552 553 /* Ensure the interrupt is set to level/low trigger. */ 554 KASSERT(pir_device != NULL, ("missing pir device")); 555 BUS_CONFIG_INTR(pir_device, pci_link->pl_irq, 556 INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW); 557 } 558 if (bootverbose) 559 printf("$PIR: %d:%d INT%c routed to irq %d\n", bus, device, 560 pin - 1 + 'A', pci_link->pl_irq); 561 return (pci_link->pl_irq); 562} 563 564/* 565 * Try to pick an interrupt for the specified link from the interrupts 566 * set in the mask. 567 */ 568static int 569pci_pir_choose_irq(struct pci_link *pci_link, int irqmask) 570{ 571 int i, irq, realmask; 572 573 /* XXX: Need to have a #define of known bad IRQs to also mask out? */ 574 realmask = pci_link->pl_irqmask & irqmask; 575 if (realmask == 0) 576 return (PCI_INVALID_IRQ); 577 578 /* Find IRQ with lowest weight. */ 579 irq = PCI_INVALID_IRQ; 580 for (i = 0; i < NUM_ISA_INTERRUPTS; i++) { 581 if (!(realmask & 1 << i)) 582 continue; 583 if (irq == PCI_INVALID_IRQ || 584 pir_interrupt_weight[i] < pir_interrupt_weight[irq]) 585 irq = i; 586 } 587 if (bootverbose && PCI_INTERRUPT_VALID(irq)) { 588 printf("$PIR: Found IRQ %d for link %#x from ", irq, 589 pci_link->pl_id); 590 pci_print_irqmask(realmask); 591 printf("\n"); 592 } 593 return (irq); 594} 595 596static void 597pci_print_irqmask(u_int16_t irqs) 598{ 599 int i, first; 600 601 if (irqs == 0) { 602 printf("none"); 603 return; 604 } 605 first = 1; 606 for (i = 0; i < 16; i++, irqs >>= 1) 607 if (irqs & 1) { 608 if (!first) 609 printf(" "); 610 else 611 first = 0; 612 printf("%d", i); 613 } 614} 615 616/* 617 * Display link devices. 618 */ 619static void 620pci_pir_dump_links(void) 621{ 622 struct pci_link *pci_link; 623 624 printf("Link IRQ Rtd Ref IRQs\n"); 625 TAILQ_FOREACH(pci_link, &pci_links, pl_links) { 626 printf("%#4x %3d %c %3d ", pci_link->pl_id, 627 pci_link->pl_irq, pci_link->pl_routed ? 'Y' : 'N', 628 pci_link->pl_references); 629 pci_print_irqmask(pci_link->pl_irqmask); 630 printf("\n"); 631 } 632} 633 634/* 635 * See if any interrupts for a given PCI bus are routed in the PIR. Don't 636 * even bother looking if the BIOS doesn't support routing anyways. If we 637 * are probing a PCI-PCI bridge, then require_parse will be true and we should 638 * only succeed if a host-PCI bridge has already attached and parsed the PIR. 639 */ 640int 641pci_pir_probe(int bus, int require_parse) 642{ 643 int i; 644 645 if (pci_route_table == NULL || (require_parse && !pir_parsed)) 646 return (0); 647 for (i = 0; i < pci_route_count; i++) 648 if (pci_route_table->pt_entry[i].pe_bus == bus) 649 return (1); 650 return (0); 651} 652 653/* 654 * The driver for the new-bus psuedo device pir0 for the $PIR table. 655 */ 656 657static int 658pir_probe(device_t dev) 659{ 660 char buf[64]; 661 662 snprintf(buf, sizeof(buf), "PCI Interrupt Routing Table: %d Entries", 663 pci_route_count); 664 device_set_desc_copy(dev, buf); 665 return (0); 666} 667 668static int 669pir_attach(device_t dev) 670{ 671 672 pci_pir_parse(); 673 KASSERT(pir_device == NULL, ("Multiple pir devices")); 674 pir_device = dev; 675 return (0); 676} 677 678static void 679pir_resume_find_device(struct PIR_entry *entry, struct PIR_intpin *intpin, 680 void *arg) 681{ 682 struct pci_dev_lookup *pd; 683 684 pd = (struct pci_dev_lookup *)arg; 685 if (intpin->link != pd->link || pd->bus != -1) 686 return; 687 pd->bus = entry->pe_bus; 688 pd->device = entry->pe_device; 689 pd->pin = intpin - entry->pe_intpin; 690} 691 692static int 693pir_resume(device_t dev) 694{ 695 struct pci_dev_lookup pd; 696 struct pci_link *pci_link; 697 int error; 698 699 /* Ask the BIOS to re-route each link that was already routed. */ 700 TAILQ_FOREACH(pci_link, &pci_links, pl_links) { 701 if (!PCI_INTERRUPT_VALID(pci_link->pl_irq)) { 702 KASSERT(!pci_link->pl_routed, 703 ("link %#x is routed but has invalid PCI IRQ", 704 pci_link->pl_id)); 705 continue; 706 } 707 if (pci_link->pl_routed) { 708 pd.bus = -1; 709 pd.link = pci_link->pl_id; 710 pci_pir_walk_table(pir_resume_find_device, &pd); 711 KASSERT(pd.bus != -1, 712 ("did not find matching entry for link %#x in the $PIR table", 713 pci_link->pl_id)); 714 if (bootverbose) 715 device_printf(dev, 716 "Using %d.%d.INT%c to route link %#x to IRQ %d\n", 717 pd.bus, pd.device, pd.pin + 'A', 718 pci_link->pl_id, pci_link->pl_irq); 719 error = pci_pir_biosroute(pd.bus, pd.device, 0, pd.pin, 720 pci_link->pl_irq); 721 if (error) 722 device_printf(dev, 723 "ROUTE_INTERRUPT on resume for link %#x failed.\n", 724 pci_link->pl_id); 725 } 726 } 727 return (0); 728} 729 730static device_method_t pir_methods[] = { 731 /* Device interface */ 732 DEVMETHOD(device_probe, pir_probe), 733 DEVMETHOD(device_attach, pir_attach), 734 DEVMETHOD(device_resume, pir_resume), 735 736 { 0, 0 } 737}; 738 739static driver_t pir_driver = { 740 "pir", 741 pir_methods, 742 1, 743}; 744 745static devclass_t pir_devclass; 746 747DRIVER_MODULE(pir, legacy, pir_driver, pir_devclass, 0, 0);
|