1 /* $NetBSD: pci_intr_fixup.c,v 1.6 2000/07/09 00:42:47 mycroft Exp $ */ 2 3 /*- 4 * Copyright (c) 1999 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 * NASA Ames Research Center. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 /* 41 * Copyright (c) 1999, by UCHIYAMA Yasushi 42 * All rights reserved. 43 * 44 * Redistribution and use in source and binary forms, with or without 45 * modification, are permitted provided that the following conditions 46 * are met: 47 * 1. Redistributions of source code must retain the above copyright 48 * notice, this list of conditions and the following disclaimer. 49 * 2. The name of the developer may NOT be used to endorse or promote products 50 * derived from this software without specific prior written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 55 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 62 * SUCH DAMAGE. 63 */ 64 65 /* 66 * PCI Interrupt Router support. 67 */ 68 69 #include "opt_pcibios.h" 70 71 #include <sys/param.h> 72 #include <sys/systm.h> 73 #include <sys/kernel.h> 74 #include <sys/malloc.h> 75 #include <sys/queue.h> 76 #include <sys/device.h> 77 78 #include <machine/bus.h> 79 #include <machine/intr.h> 80 81 #include <dev/pci/pcireg.h> 82 #include <dev/pci/pcivar.h> 83 #include <dev/pci/pcidevs.h> 84 85 #include <i386/isa/icu.h> 86 #include <i386/pci/pci_intr_fixup.h> 87 #include <i386/pci/pcibios.h> 88 89 struct pciintr_link_map { 90 int link; 91 int clink; 92 int irq; 93 u_int16_t bitmap; 94 int fixup_stage; 95 int old_irq; 96 SIMPLEQ_ENTRY(pciintr_link_map) list; 97 }; 98 99 pciintr_icu_tag_t pciintr_icu_tag; 100 pciintr_icu_handle_t pciintr_icu_handle; 101 102 struct pciintr_link_map *pciintr_link_lookup_pin 103 __P((struct pcibios_intr_routing *, int)); 104 struct pciintr_link_map *pciintr_link_lookup_link __P((int)); 105 struct pciintr_link_map *pciintr_link_alloc __P((struct pcibios_intr_routing *, 106 int)); 107 struct pcibios_intr_routing *pciintr_pir_lookup __P((int, int)); 108 int pciintr_link_init __P((void)); 109 int pciintr_link_fixup __P((void)); 110 int pciintr_link_route __P((u_int16_t *)); 111 int pciintr_irq_release __P((u_int16_t *)); 112 int pciintr_header_fixup __P((pci_chipset_tag_t)); 113 void pciintr_do_header_fixup __P((pci_chipset_tag_t, pcitag_t)); 114 115 SIMPLEQ_HEAD(, pciintr_link_map) pciintr_link_map_list; 116 117 const struct pciintr_icu_table { 118 pci_vendor_id_t piit_vendor; 119 pci_product_id_t piit_product; 120 int (*piit_init) __P((pci_chipset_tag_t, 121 bus_space_tag_t, pcitag_t, pciintr_icu_tag_t *, 122 pciintr_icu_handle_t *)); 123 } pciintr_icu_table[] = { 124 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82371MX, 125 piix_init }, 126 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82371AB_ISA, 127 piix_init }, 128 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82371FB_ISA, 129 piix_init }, 130 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82371SB_ISA, 131 piix_init }, 132 133 { PCI_VENDOR_OPTI, PCI_PRODUCT_OPTI_82C558, 134 opti82c558_init }, 135 { PCI_VENDOR_OPTI, PCI_PRODUCT_OPTI_82C700, 136 opti82c700_init }, 137 138 { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT82C586_ISA, 139 via82c586_init, }, 140 141 { PCI_VENDOR_SIS, PCI_PRODUCT_SIS_85C503, 142 sis85c503_init }, 143 144 { 0, 0, 145 NULL }, 146 }; 147 148 const struct pciintr_icu_table *pciintr_icu_lookup __P((pcireg_t)); 149 150 const struct pciintr_icu_table * 151 pciintr_icu_lookup(id) 152 pcireg_t id; 153 { 154 const struct pciintr_icu_table *piit; 155 156 for (piit = pciintr_icu_table; 157 piit->piit_init != NULL; 158 piit++) { 159 if (PCI_VENDOR(id) == piit->piit_vendor && 160 PCI_PRODUCT(id) == piit->piit_product) 161 return (piit); 162 } 163 164 return (NULL); 165 } 166 167 struct pciintr_link_map * 168 pciintr_link_lookup_pin(pir, pin) 169 struct pcibios_intr_routing *pir; 170 int pin; 171 { 172 173 return (pciintr_link_lookup_link(pir->linkmap[pin].link)); 174 } 175 176 struct pciintr_link_map * 177 pciintr_link_lookup_link(link) 178 int link; 179 { 180 struct pciintr_link_map *l; 181 182 for (l = SIMPLEQ_FIRST(&pciintr_link_map_list); l != NULL; 183 l = SIMPLEQ_NEXT(l, list)) { 184 if (l->link == link) 185 return (l); 186 } 187 188 return (NULL); 189 } 190 191 struct pciintr_link_map * 192 pciintr_link_alloc(pir, pin) 193 struct pcibios_intr_routing *pir; 194 int pin; 195 { 196 struct pciintr_link_map *l, *lstart; 197 198 l = malloc(sizeof(*l), M_DEVBUF, M_NOWAIT); 199 if (l == NULL) 200 panic("pciintr_link_alloc"); 201 202 memset(l, 0, sizeof(*l)); 203 204 l->link = pir->linkmap[pin].link; 205 l->bitmap = pir->linkmap[pin].bitmap; 206 207 lstart = SIMPLEQ_FIRST(&pciintr_link_map_list); 208 if (lstart == NULL || lstart->link < l->link) 209 SIMPLEQ_INSERT_TAIL(&pciintr_link_map_list, l, list); 210 else 211 SIMPLEQ_INSERT_HEAD(&pciintr_link_map_list, l, list); 212 213 return (l); 214 } 215 216 struct pcibios_intr_routing * 217 pciintr_pir_lookup(bus, device) 218 int bus, device; 219 { 220 struct pcibios_intr_routing *pir; 221 int entry; 222 223 if (pcibios_pir_table == NULL) 224 return (NULL); 225 226 for (entry = 0; entry < pcibios_pir_table_nentries; entry++) { 227 pir = &pcibios_pir_table[entry]; 228 if (pir->bus == bus && ((pir->device >> 3) & 0x1f) == device) 229 return (pir); 230 } 231 232 return (NULL); 233 } 234 235 int 236 pciintr_link_init() 237 { 238 int entry, pin, error, link, clink; 239 struct pcibios_intr_routing *pir; 240 struct pciintr_link_map *l; 241 242 if (pcibios_pir_table == NULL) { 243 /* No PIR table; can't do anything. */ 244 printf("pciintr_link_init: no PIR table\n"); 245 return (1); 246 } 247 248 error = 0; 249 SIMPLEQ_INIT(&pciintr_link_map_list); 250 251 for (entry = 0; entry < pcibios_pir_table_nentries; entry++) { 252 pir = &pcibios_pir_table[entry]; 253 for (pin = 0; pin < 4; pin++) { 254 link = pir->linkmap[pin].link; 255 if (link == 0) { 256 /* No connection for this pin. */ 257 continue; 258 } 259 260 /* 261 * Check the link value by asking the ICU for 262 * the canonical link value. 263 */ 264 if (pciintr_icu_getclink(pciintr_icu_tag, 265 pciintr_icu_handle, link, &clink) != 0) { 266 /* 267 * Table entry is bogus. Just ignore it. 268 */ 269 #ifdef PCIINTR_DEBUG 270 printf("pciintr_link_init: bad table entry: " 271 "bus %d device %d link 0x%02x\n", 272 pir->bus, (pir->device >> 3 & 0x1f), link); 273 #endif 274 continue; 275 } 276 277 /* 278 * Multiple devices may be wired to the same 279 * interrupt; check to see if we've seen this 280 * one already. If not, allocate a new link 281 * map entry and stuff it in the map. 282 */ 283 l = pciintr_link_lookup_pin(pir, pin); 284 if (l == NULL) 285 (void) pciintr_link_alloc(pir, pin); 286 } 287 } 288 289 return (error); 290 } 291 292 int 293 pciintr_link_fixup() 294 { 295 struct pciintr_link_map *l; 296 u_int16_t pciirq, bitmap; 297 int i, j, cnt, irq; 298 299 /* 300 * First stage: Attempt to connect PIRQs which aren't 301 * yet connected. 302 */ 303 pciirq = 0; 304 305 for (l = SIMPLEQ_FIRST(&pciintr_link_map_list); l != NULL; 306 l = SIMPLEQ_NEXT(l, list)) { 307 /* 308 * Get the canonical link value for this entry. 309 */ 310 if (pciintr_icu_getclink(pciintr_icu_tag, pciintr_icu_handle, 311 l->link, &l->clink) != 0) { 312 /* 313 * ICU doesn't understand this link value. 314 */ 315 #ifdef PCIINTR_DEBUG 316 printf("pciintr_link_fixup: link 0x%02x invalid\n", 317 l->link); 318 #endif 319 l->clink = -1; 320 continue; 321 } 322 323 /* 324 * Determine if this PIRQ is mapped to an IRQ. 325 */ 326 if (pciintr_icu_get_intr(pciintr_icu_tag, pciintr_icu_handle, 327 l->clink, &irq) != 0) { 328 /* 329 * ICU doesn't understand this PIRQ value. 330 */ 331 l->clink = -1; 332 #ifdef PCIINTR_DEBUG 333 printf("pciintr_link_fixup: PIRQ %d invalid\n", 334 l->clink); 335 #endif 336 continue; 337 } 338 339 if (irq == 0xff) { 340 /* 341 * Interrupt isn't connected. Attempt to assign 342 * it to an IRQ. 343 */ 344 #ifdef PCIINTR_DEBUG 345 printf("pciintr_link_fixup: PIRQ %d not connected", 346 l->clink); 347 #endif 348 bitmap = l->bitmap; 349 for (i = 0, j = 0xff, cnt = 0; i < 16; i++) 350 if (bitmap & (1 << i)) 351 j = i, cnt++; 352 /* 353 * Just do the easy case now; we'll defer the 354 * harder ones to Stage 2. 355 */ 356 if (cnt == 1) { 357 l->irq = j; 358 l->old_irq = irq; 359 l->fixup_stage = 1; 360 pciirq |= 1 << j; 361 #ifdef PCIINTR_DEBUG 362 printf(", assigning IRQ %d", l->irq); 363 #endif 364 } 365 #ifdef PCIINTR_DEBUG 366 printf("\n"); 367 #endif 368 } else { 369 /* 370 * Interrupt is already connected. Don't do 371 * anything to it. 372 */ 373 l->irq = irq; 374 pciirq |= 1 << irq; 375 #ifdef PCIINTR_DEBUG 376 printf("pciintr_link_fixup: PIRQ %d already connected " 377 "to IRQ %d\n", l->clink, l->irq); 378 #endif 379 } 380 } 381 382 /* 383 * Stage 2: Attempt to connect PIRQs which we didn't 384 * connect in Stage 1. 385 */ 386 for (l = SIMPLEQ_FIRST(&pciintr_link_map_list); l != NULL; 387 l = SIMPLEQ_NEXT(l, list)) { 388 if (l->irq != 0) 389 continue; 390 bitmap = l->bitmap; 391 for (i = 0; i < 16; i++) { 392 if ((pciirq & (1 << i)) != 0 && 393 (bitmap & (1 << i)) != 0) { 394 /* 395 * This IRQ is a valid PCI IRQ already 396 * connected to another PIRQ, and also an 397 * IRQ our PIRQ can use; connect it up! 398 */ 399 l->irq = i; 400 l->old_irq = 0xff; 401 l->fixup_stage = 2; 402 #ifdef PCIINTR_DEBUG 403 printf("pciintr_link_fixup: assigning " 404 "IRQ %d to PIRQ %d\n", l->irq, 405 l->clink); 406 #endif 407 break; 408 } 409 } 410 } 411 412 #ifdef PCIBIOS_IRQS_HINT 413 /* 414 * Stage 3: The worst case. I need configuration hint that 415 * user supplied a mask for the PCI irqs 416 */ 417 pciirq = PCIBIOS_IRQS_HINT; 418 for (l = SIMPLEQ_FIRST(&pciintr_link_map_list); l != NULL; 419 l = SIMPLEQ_NEXT(l, list)) { 420 if (l->irq != 0) 421 continue; 422 for (i = 0; i < 16; i++) { 423 if ((pciirq & (1 << i)) != 0 && 424 (bitmap & (1 << i)) != 0) { 425 l->irq = i; 426 l->old_irq = 0xff; 427 l->fixup_stage = 3; 428 #ifdef PCIINTR_DEBUG 429 printf("pciintr_link_fixup (stage 3): " 430 "assigning IRQ %d to PIRQ %d\n", 431 l->irq, l->clink); 432 #endif 433 break; 434 } 435 } 436 } 437 #endif /* PCIBIOS_IRQS_HINT */ 438 439 return (0); 440 } 441 442 int 443 pciintr_link_route(pciirq) 444 u_int16_t *pciirq; 445 { 446 struct pciintr_link_map *l; 447 int rv = 0; 448 449 *pciirq = 0; 450 451 for (l = SIMPLEQ_FIRST(&pciintr_link_map_list); l != NULL; 452 l = SIMPLEQ_NEXT(l, list)) { 453 if (pciintr_icu_set_intr(pciintr_icu_tag, pciintr_icu_handle, 454 l->clink, l->irq) != 0 || 455 pciintr_icu_set_trigger(pciintr_icu_tag, pciintr_icu_handle, 456 l->irq, IST_LEVEL) != 0) { 457 printf("pciintr_link_route: route of PIRQ %d -> IRQ %d" 458 " failed\n", l->clink, l->irq); 459 rv = 1; 460 } else { 461 /* 462 * Succssfully routed interrupt. Mark this as 463 * a PCI interrupt. 464 */ 465 *pciirq |= (1 << l->irq); 466 } 467 } 468 469 return (rv); 470 } 471 472 int 473 pciintr_irq_release(pciirq) 474 u_int16_t *pciirq; 475 { 476 int i; 477 478 for (i = 0; i < 16; i++) { 479 if ((*pciirq & (1 << i)) == 0) 480 (void) pciintr_icu_set_trigger(pciintr_icu_tag, 481 pciintr_icu_handle, i, IST_EDGE); 482 } 483 484 return (0); 485 } 486 487 int 488 pciintr_header_fixup(pc) 489 pci_chipset_tag_t pc; 490 { 491 #ifdef PCIBIOSVERBOSE 492 printf("--------------------------------------------\n"); 493 printf(" device vendor product pin PIRQ IRQ stage\n"); 494 printf("--------------------------------------------\n"); 495 #endif 496 pci_device_foreach(pc, pcibios_max_bus, pciintr_do_header_fixup); 497 #ifdef PCIBIOSVERBOSE 498 printf("--------------------------------------------\n"); 499 #endif 500 501 return (0); 502 } 503 504 void 505 pciintr_do_header_fixup(pc, tag) 506 pci_chipset_tag_t pc; 507 pcitag_t tag; 508 { 509 struct pcibios_intr_routing *pir; 510 struct pciintr_link_map *l; 511 int pin, irq, link; 512 int bus, device, function; 513 pcireg_t intr, id; 514 515 pci_decompose_tag(pc, tag, &bus, &device, &function); 516 id = pci_conf_read(pc, tag, PCI_ID_REG); 517 518 intr = pci_conf_read(pc, tag, PCI_INTERRUPT_REG); 519 pin = PCI_INTERRUPT_PIN(intr); 520 irq = PCI_INTERRUPT_LINE(intr); 521 522 if (pin == 0) { 523 /* 524 * No interrupt used. 525 */ 526 return; 527 } 528 529 pir = pciintr_pir_lookup(bus, device); 530 if (pir == NULL || (link = pir->linkmap[pin - 1].link) == 0) { 531 /* 532 * Interrupt not connected; no 533 * need to change. 534 */ 535 return; 536 } 537 538 l = pciintr_link_lookup_link(link); 539 if (l == NULL) { 540 /* 541 * No link map entry?! 542 */ 543 printf("pciintr_header_fixup: no entry for link 0x%02x " 544 "(%d:%d:%d:%c)\n", link, bus, device, function, 545 '@' + pin); 546 return; 547 } 548 549 /* 550 * IRQs 14 and 15 are reserved for PCI IDE interrupts; don't muck 551 * with them. 552 */ 553 if (irq == 14 || irq == 15) 554 return; 555 556 #ifdef PCIBIOSVERBOSE 557 printf("%03d:%02d:%d 0x%04x 0x%04x %c 0x%02x %02d %d\n", 558 bus, device, function, PCI_VENDOR(id), PCI_PRODUCT(id), 559 '@' + pin, l->clink, l->irq, l->fixup_stage); 560 #endif 561 562 intr &= ~(PCI_INTERRUPT_LINE_MASK << PCI_INTERRUPT_LINE_SHIFT); 563 intr |= (l->irq << PCI_INTERRUPT_LINE_SHIFT); 564 pci_conf_write(pc, tag, PCI_INTERRUPT_REG, intr); 565 } 566 567 int 568 pci_intr_fixup(pc, iot, pciirq) 569 pci_chipset_tag_t pc; 570 bus_space_tag_t iot; 571 u_int16_t *pciirq; 572 { 573 const struct pciintr_icu_table *piit = NULL; 574 pcitag_t icutag; 575 pcireg_t icuid; 576 577 /* 578 * Attempt to initialize our PCI interrupt router. If 579 * the PIR Table is present in ROM, use the location 580 * specified by the PIR Table, and use the compat ID, 581 * if present. Otherwise, we have to look for the router 582 * ourselves (the PCI-ISA bridge). 583 */ 584 if (pcibios_pir_header.signature != 0) { 585 icutag = pci_make_tag(pc, pcibios_pir_header.router_bus, 586 (pcibios_pir_header.router_devfunc >> 3) & 0x1f, 587 pcibios_pir_header.router_devfunc & 7); 588 icuid = pcibios_pir_header.compat_router; 589 if (icuid == 0 || 590 (piit = pciintr_icu_lookup(icuid)) == NULL) { 591 /* 592 * No compat ID, or don't know the compat ID? Read 593 * it from the configuration header. 594 */ 595 icuid = pci_conf_read(pc, icutag, PCI_ID_REG); 596 } 597 if (piit == NULL) 598 piit = pciintr_icu_lookup(icuid); 599 } else { 600 int device, maxdevs = pci_bus_maxdevs(pc, 0); 601 602 /* 603 * Search configuration space for a known interrupt 604 * router. 605 */ 606 for (device = 0; device < maxdevs; device++) { 607 icutag = pci_make_tag(pc, 0, device, 0); 608 icuid = pci_conf_read(pc, icutag, PCI_ID_REG); 609 610 /* Invalid vendor ID value? */ 611 if (PCI_VENDOR(icuid) == PCI_VENDOR_INVALID) 612 continue; 613 /* XXX Not invalid, but we've done this ~forever. */ 614 if (PCI_VENDOR(icuid) == 0) 615 continue; 616 617 piit = pciintr_icu_lookup(icuid); 618 if (piit != NULL) 619 break; 620 } 621 } 622 623 if (piit == NULL) { 624 printf("pci_intr_fixup: no compatible PCI ICU found\n"); 625 return (-1); /* non-fatal */ 626 } 627 628 /* 629 * Initialize the PCI ICU. 630 */ 631 if ((*piit->piit_init)(pc, iot, icutag, &pciintr_icu_tag, 632 &pciintr_icu_handle) != 0) 633 return (-1); /* non-fatal */ 634 635 /* 636 * Initialize the PCI interrupt link map. 637 */ 638 if (pciintr_link_init()) 639 return (-1); /* non-fatal */ 640 641 /* 642 * Fix up the link->IRQ mappings. 643 */ 644 if (pciintr_link_fixup() != 0) 645 return (-1); /* non-fatal */ 646 647 /* 648 * Now actually program the PCI ICU with the new 649 * routing information. 650 */ 651 if (pciintr_link_route(pciirq) != 0) 652 return (1); /* fatal */ 653 654 /* 655 * Now that we've routed all of the PIRQs, rewrite the PCI 656 * configuration headers to reflect the new mapping. 657 */ 658 if (pciintr_header_fixup(pc) != 0) 659 return (1); /* fatal */ 660 661 /* 662 * Free any unused PCI IRQs for ISA devices. 663 */ 664 if (pciintr_irq_release(pciirq) != 0) 665 return (-1); /* non-fatal */ 666 667 /* 668 * All done! 669 */ 670 return (0); /* success! */ 671 } 672