pci_pir.c revision 145083
1139790Simp/*- 226159Sse * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 366529Smsmith * Copyright (c) 2000, Michael Smith <msmith@freebsd.org> 466529Smsmith * Copyright (c) 2000, BSDi 5125980Sjhb * Copyright (c) 2004, John Baldwin <jhb@FreeBSD.org> 626159Sse * All rights reserved. 726159Sse * 826159Sse * Redistribution and use in source and binary forms, with or without 926159Sse * modification, are permitted provided that the following conditions 1026159Sse * are met: 1126159Sse * 1. Redistributions of source code must retain the above copyright 1226159Sse * notice unmodified, this list of conditions, and the following 1326159Sse * disclaimer. 1426159Sse * 2. Redistributions in binary form must reproduce the above copyright 1526159Sse * notice, this list of conditions and the following disclaimer in the 1626159Sse * documentation and/or other materials provided with the distribution. 1726159Sse * 1826159Sse * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1926159Sse * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2026159Sse * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2126159Sse * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2226159Sse * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2326159Sse * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2426159Sse * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2526159Sse * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2626159Sse * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2726159Sse * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2826159Sse */ 296104Sse 30115706Sobrien#include <sys/cdefs.h> 31115706Sobrien__FBSDID("$FreeBSD: head/sys/i386/pci/pci_pir.c 145083 2005-04-14 18:25:09Z jhb $"); 32115706Sobrien 33125980Sjhb#include <sys/param.h> 346734Sbde#include <sys/systm.h> 3547307Speter#include <sys/bus.h> 3647307Speter#include <sys/kernel.h> 3765304Speter#include <sys/malloc.h> 38129876Sphk#include <sys/module.h> 39118325Simp#include <sys/sysctl.h> 4067185Simp#include <vm/vm.h> 4167185Simp#include <vm/pmap.h> 42130312Sjhb#include <vm/vm_param.h> 4367185Simp#include <machine/md_var.h> 44100435Simp#include <dev/pci/pcivar.h> 45100435Simp#include <dev/pci/pcireg.h> 4666529Smsmith#include <machine/pci_cfgreg.h> 4759294Smsmith#include <machine/segments.h> 4859294Smsmith#include <machine/pc/bios.h> 4959294Smsmith 50125980Sjhb#define NUM_ISA_INTERRUPTS 16 5165176Sdfr 52125980Sjhb/* 53125980Sjhb * A link device. Loosely based on the ACPI PCI link device. This doesn't 54125980Sjhb * try to support priorities for different ISA interrupts. 55125980Sjhb */ 56125980Sjhbstruct pci_link { 57125980Sjhb TAILQ_ENTRY(pci_link) pl_links; 58125980Sjhb uint8_t pl_id; 59125980Sjhb uint8_t pl_irq; 60125980Sjhb uint16_t pl_irqmask; 61125980Sjhb int pl_references; 62125980Sjhb int pl_routed; 63125980Sjhb}; 6482441Simp 65125980Sjhbstruct pci_link_lookup { 66125980Sjhb struct pci_link **pci_link_ptr; 67125980Sjhb int bus; 68125980Sjhb int device; 69125980Sjhb int pin; 70125980Sjhb}; 716104Sse 72128933Sjhbstruct pci_dev_lookup { 73128933Sjhb uint8_t link; 74128933Sjhb int bus; 75128933Sjhb int device; 76128933Sjhb int pin; 77128933Sjhb}; 78128933Sjhb 79125980Sjhbtypedef void pir_entry_handler(struct PIR_entry *entry, 80125980Sjhb struct PIR_intpin* intpin, void *arg); 8168218Smsmith 82103017Sjhbstatic void pci_print_irqmask(u_int16_t irqs); 83125980Sjhbstatic int pci_pir_biosroute(int bus, int device, int func, int pin, 84125980Sjhb int irq); 85125980Sjhbstatic int pci_pir_choose_irq(struct pci_link *pci_link, int irqmask); 86125980Sjhbstatic void pci_pir_create_links(struct PIR_entry *entry, 87125980Sjhb struct PIR_intpin *intpin, void *arg); 88125980Sjhbstatic void pci_pir_dump_links(void); 89125980Sjhbstatic struct pci_link *pci_pir_find_link(uint8_t link_id); 90125980Sjhbstatic void pci_pir_find_link_handler(struct PIR_entry *entry, 91125980Sjhb struct PIR_intpin *intpin, void *arg); 92125980Sjhbstatic void pci_pir_initial_irqs(struct PIR_entry *entry, 93125980Sjhb struct PIR_intpin *intpin, void *arg); 94128933Sjhbstatic void pci_pir_parse(void); 95125980Sjhbstatic void pci_pir_print_intpin(struct PIR_entry *entry, 96125980Sjhb struct PIR_intpin *intpin, void *arg); 97125980Sjhbstatic void pci_pir_print_table(void); 98125980Sjhbstatic uint8_t pci_pir_search_irq(int bus, int device, int pin); 99125980Sjhbstatic int pci_pir_valid_irq(struct pci_link *pci_link, int irq); 100125980Sjhbstatic void pci_pir_walk_table(pir_entry_handler *handler, void *arg); 10159294Smsmith 102141616Sphkstatic MALLOC_DEFINE(M_PIR, "$PIR", "$PIR structures"); 103125980Sjhb 104103017Sjhbstatic struct PIR_table *pci_route_table; 105128933Sjhbstatic device_t pir_device; 106125980Sjhbstatic int pci_route_count, pir_bios_irqs, pir_parsed; 107125980Sjhbstatic TAILQ_HEAD(, pci_link) pci_links; 108125980Sjhbstatic int pir_interrupt_weight[NUM_ISA_INTERRUPTS]; 10967185Simp 110118325Simp/* sysctl vars */ 111118325SimpSYSCTL_DECL(_hw_pci); 112118325Simp 113118338Snyan#ifdef PC98 114125980Sjhb/* IRQs 3, 5, 7, 9, 10, 11, 12, 13 */ 115118338Snyan#define PCI_IRQ_OVERRIDE_MASK 0x3e68 116118338Snyan#else 117125980Sjhb/* IRQs 3, 4, 5, 6, 7, 9, 10, 11, 12, 14, 15 */ 118125980Sjhb#define PCI_IRQ_OVERRIDE_MASK 0xdef8 119118338Snyan#endif 120118338Snyan 121118338Snyanstatic uint32_t pci_irq_override_mask = PCI_IRQ_OVERRIDE_MASK; 122118325SimpTUNABLE_INT("hw.pci.irq_override_mask", &pci_irq_override_mask); 123121307SsilbySYSCTL_INT(_hw_pci, OID_AUTO, irq_override_mask, CTLFLAG_RDTUN, 124118338Snyan &pci_irq_override_mask, PCI_IRQ_OVERRIDE_MASK, 125118325Simp "Mask of allowed irqs to try to route when it has no good clue about\n" 126118325Simp "which irqs it should use."); 127118325Simp 12897694Simp/* 129125980Sjhb * Look for the interrupt routing table. 130125980Sjhb * 131125980Sjhb * We use PCI BIOS's PIR table if it's available. $PIR is the standard way 132125980Sjhb * to do this. Sadly, some machines are not standards conforming and have 133125980Sjhb * _PIR instead. We shrug and cope by looking for both. 13497694Simp */ 135125980Sjhbvoid 136125980Sjhbpci_pir_open(void) 13797694Simp{ 138125980Sjhb struct PIR_table *pt; 139125980Sjhb uint32_t sigaddr; 140125980Sjhb int i; 141125980Sjhb uint8_t ck, *cv; 142125980Sjhb 143125980Sjhb /* Don't try if we've already found a table. */ 144125980Sjhb if (pci_route_table != NULL) 145125980Sjhb return; 146125980Sjhb 147125980Sjhb /* Look for $PIR and then _PIR. */ 148125980Sjhb sigaddr = bios_sigsearch(0, "$PIR", 4, 16, 0); 149125980Sjhb if (sigaddr == 0) 150125980Sjhb sigaddr = bios_sigsearch(0, "_PIR", 4, 16, 0); 151125980Sjhb if (sigaddr == 0) 152125980Sjhb return; 153125980Sjhb 154125980Sjhb /* If we found something, check the checksum and length. */ 155125980Sjhb /* XXX - Use pmap_mapdev()? */ 156125980Sjhb pt = (struct PIR_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr); 157125980Sjhb if (pt->pt_header.ph_length <= sizeof(struct PIR_header)) 158125980Sjhb return; 159125980Sjhb for (cv = (u_int8_t *)pt, ck = 0, i = 0; 160125980Sjhb i < (pt->pt_header.ph_length); i++) 161125980Sjhb ck += cv[i]; 162125980Sjhb if (ck != 0) 163125980Sjhb return; 164125980Sjhb 165125980Sjhb /* Ok, we've got a valid table. */ 166125980Sjhb pci_route_table = pt; 167125980Sjhb pci_route_count = (pt->pt_header.ph_length - 168125980Sjhb sizeof(struct PIR_header)) / 169125980Sjhb sizeof(struct PIR_entry); 170128933Sjhb if (bootverbose) { 171128933Sjhb printf("Found $PIR table, %d entries at %p\n", 172128933Sjhb pci_route_count, pci_route_table); 173125980Sjhb pci_pir_print_table(); 174128933Sjhb } 17597694Simp} 17697694Simp 177125980Sjhb/* 178125980Sjhb * Find the pci_link structure for a given link ID. 179125980Sjhb */ 180125980Sjhbstatic struct pci_link * 181125980Sjhbpci_pir_find_link(uint8_t link_id) 18282441Simp{ 183125980Sjhb struct pci_link *pci_link; 18482441Simp 185125980Sjhb TAILQ_FOREACH(pci_link, &pci_links, pl_links) { 186125980Sjhb if (pci_link->pl_id == link_id) 187125980Sjhb return (pci_link); 188100435Simp } 189125980Sjhb return (NULL); 19082441Simp} 19182441Simp 192125980Sjhb/* 193125980Sjhb * Find the link device associated with a PCI device in the table. 19466529Smsmith */ 195125980Sjhbstatic void 196125980Sjhbpci_pir_find_link_handler(struct PIR_entry *entry, struct PIR_intpin *intpin, 197125980Sjhb void *arg) 19859294Smsmith{ 199125980Sjhb struct pci_link_lookup *lookup; 20065176Sdfr 201125980Sjhb lookup = (struct pci_link_lookup *)arg; 202125980Sjhb if (entry->pe_bus == lookup->bus && 203125980Sjhb entry->pe_device == lookup->device && 204125980Sjhb intpin - entry->pe_intpin == lookup->pin) 205125980Sjhb *lookup->pci_link_ptr = pci_pir_find_link(intpin->link); 206125980Sjhb} 20766529Smsmith 208125980Sjhb/* 209125980Sjhb * Check to see if a possible IRQ setting is valid. 210125980Sjhb */ 211125980Sjhbstatic int 212125980Sjhbpci_pir_valid_irq(struct pci_link *pci_link, int irq) 213125980Sjhb{ 21467185Simp 215125980Sjhb if (!PCI_INTERRUPT_VALID(irq)) 216125980Sjhb return (0); 217125980Sjhb return (pci_link->pl_irqmask & (1 << irq)); 21859294Smsmith} 21959294Smsmith 220125980Sjhb/* 221125980Sjhb * Walk the $PIR executing the worker function for each valid intpin entry 222125980Sjhb * in the table. The handler is passed a pointer to both the entry and 223125980Sjhb * the intpin in the entry. 22466529Smsmith */ 225125980Sjhbstatic void 226125980Sjhbpci_pir_walk_table(pir_entry_handler *handler, void *arg) 22769783Smsmith{ 228125980Sjhb struct PIR_entry *entry; 229125980Sjhb struct PIR_intpin *intpin; 230125980Sjhb int i, pin; 23169783Smsmith 232125980Sjhb entry = &pci_route_table->pt_entry[0]; 233125980Sjhb for (i = 0; i < pci_route_count; i++, entry++) { 234125980Sjhb intpin = &entry->pe_intpin[0]; 235125980Sjhb for (pin = 0; pin < 4; pin++, intpin++) 236125980Sjhb if (intpin->link != 0) 237125980Sjhb handler(entry, intpin, arg); 238100435Simp } 23969783Smsmith} 24069783Smsmith 241125980Sjhbstatic void 242125980Sjhbpci_pir_create_links(struct PIR_entry *entry, struct PIR_intpin *intpin, 243125980Sjhb void *arg) 24459294Smsmith{ 245125980Sjhb struct pci_link *pci_link; 246111068Speter 247125980Sjhb pci_link = pci_pir_find_link(intpin->link); 248125980Sjhb if (pci_link != NULL) { 249125980Sjhb pci_link->pl_references++; 250125980Sjhb if (intpin->irqs != pci_link->pl_irqmask) { 251125980Sjhb if (bootverbose) 252125980Sjhb printf( 253125980Sjhb "$PIR: Entry %d.%d.INT%c has different mask for link %#x, merging\n", 254125980Sjhb entry->pe_bus, entry->pe_device, 255125980Sjhb (intpin - entry->pe_intpin) + 'A', 256125980Sjhb pci_link->pl_id); 257125980Sjhb pci_link->pl_irqmask &= intpin->irqs; 258125980Sjhb } 259125980Sjhb } else { 260125980Sjhb pci_link = malloc(sizeof(struct pci_link), M_PIR, M_WAITOK); 261125980Sjhb pci_link->pl_id = intpin->link; 262125980Sjhb pci_link->pl_irqmask = intpin->irqs; 263125980Sjhb pci_link->pl_irq = PCI_INVALID_IRQ; 264125980Sjhb pci_link->pl_references = 1; 265125980Sjhb pci_link->pl_routed = 0; 266125980Sjhb TAILQ_INSERT_TAIL(&pci_links, pci_link, pl_links); 267125980Sjhb } 26859294Smsmith} 26959294Smsmith 27066529Smsmith/* 271125980Sjhb * Look to see if any of the function on the PCI device at bus/device have 272125980Sjhb * an interrupt routed to intpin 'pin' by the BIOS. 27367185Simp */ 274125980Sjhbstatic uint8_t 275125980Sjhbpci_pir_search_irq(int bus, int device, int pin) 27667185Simp{ 277125980Sjhb uint32_t value; 278125980Sjhb uint8_t func, maxfunc; 279125980Sjhb 280125980Sjhb /* See if we have a valid device at function 0. */ 281125980Sjhb value = pci_cfgregread(bus, device, 0, PCIR_HDRTYPE, 1); 282125980Sjhb if ((value & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) 283100435Simp return (PCI_INVALID_IRQ); 284125980Sjhb if (value & PCIM_MFDEV) 285125980Sjhb maxfunc = PCI_FUNCMAX; 286125980Sjhb else 287125980Sjhb maxfunc = 0; 28867185Simp 289125980Sjhb /* Scan all possible functions at this device. */ 290125980Sjhb for (func = 0; func <= maxfunc; func++) { 291125980Sjhb value = pci_cfgregread(bus, device, func, PCIR_DEVVENDOR, 4); 292125980Sjhb if (value == 0xffffffff) 293100435Simp continue; 294125980Sjhb value = pci_cfgregread(bus, device, func, PCIR_INTPIN, 1); 295125980Sjhb 296103025Sjhb /* 297125980Sjhb * See if it uses the pin in question. Note that the passed 298125980Sjhb * in pin uses 0 for A, .. 3 for D whereas the intpin 299125980Sjhb * register uses 0 for no interrupt, 1 for A, .. 4 for D. 300103025Sjhb */ 301125980Sjhb if (value != pin + 1) 302103025Sjhb continue; 303125980Sjhb value = pci_cfgregread(bus, device, func, PCIR_INTLINE, 1); 304125980Sjhb if (bootverbose) 305125980Sjhb printf( 306125980Sjhb "$PIR: Found matching pin for %d.%d.INT%c at func %d: %d\n", 307125980Sjhb bus, device, pin + 'A', func, value); 308125980Sjhb if (value != PCI_INVALID_IRQ) 309125980Sjhb return (value); 31082441Simp } 311125980Sjhb return (PCI_INVALID_IRQ); 31267185Simp} 31367185Simp 31468218Smsmith/* 315125980Sjhb * Try to initialize IRQ based on this device's IRQ. 316103025Sjhb */ 317125980Sjhbstatic void 318125980Sjhbpci_pir_initial_irqs(struct PIR_entry *entry, struct PIR_intpin *intpin, 319125980Sjhb void *arg) 320103025Sjhb{ 321125980Sjhb struct pci_link *pci_link; 322125980Sjhb uint8_t irq, pin; 323103025Sjhb 324125980Sjhb pin = intpin - entry->pe_intpin; 325125980Sjhb pci_link = pci_pir_find_link(intpin->link); 326125980Sjhb irq = pci_pir_search_irq(entry->pe_bus, entry->pe_device, pin); 327145083Sjhb if (irq == PCI_INVALID_IRQ || irq == pci_link->pl_irq) 328125980Sjhb return; 329145083Sjhb 330145083Sjhb /* 331145083Sjhb * If we don't have an IRQ for this link yet, then we trust the 332145083Sjhb * BIOS, even if it seems invalid from the $PIR entries. 333145083Sjhb */ 334145083Sjhb if (pci_link->pl_irq == PCI_INVALID_IRQ) { 335145083Sjhb if (!pci_pir_valid_irq(pci_link, irq)) 336125980Sjhb printf( 337145083Sjhb "$PIR: Using invalid BIOS IRQ %d from %d.%d.INT%c is for link %#x\n", 338125980Sjhb irq, entry->pe_bus, entry->pe_device, pin + 'A', 339145083Sjhb pci_link->pl_id); 340145083Sjhb pci_link->pl_irq = irq; 341145083Sjhb pci_link->pl_routed = 1; 342145083Sjhb return; 343145083Sjhb } 344145083Sjhb 345145083Sjhb /* 346145083Sjhb * We have an IRQ and it doesn't match the current IRQ for this 347145083Sjhb * link. If the new IRQ is invalid, then warn about it and ignore 348145083Sjhb * it. If the old IRQ is invalid and the new IRQ is valid, then 349145083Sjhb * prefer the new IRQ instead. If both IRQs are valid, then just 350145083Sjhb * use the first one. Note that if we ever get into this situation 351145083Sjhb * we are having to guess which setting the BIOS actually routed. 352145083Sjhb * Perhaps we should just give up instead. 353145083Sjhb */ 354145083Sjhb if (!pci_pir_valid_irq(pci_link, irq)) { 355125980Sjhb printf( 356125980Sjhb "$PIR: BIOS IRQ %d for %d.%d.INT%c is not valid for link %#x\n", 357125980Sjhb irq, entry->pe_bus, entry->pe_device, pin + 'A', 358125980Sjhb pci_link->pl_id); 359145083Sjhb } else if (!pci_pir_valid_irq(pci_link, pci_link->pl_irq)) { 360145083Sjhb printf( 361145083Sjhb"$PIR: Preferring valid BIOS IRQ %d from %d.%d.INT%c for link %#x to IRQ %d\n", 362145083Sjhb irq, entry->pe_bus, entry->pe_device, pin + 'A', 363145083Sjhb pci_link->pl_id, pci_link->pl_irq); 364145083Sjhb pci_link->pl_irq = irq; 365145083Sjhb pci_link->pl_routed = 1; 366145083Sjhb } else 367145083Sjhb printf( 368145083Sjhb "$PIR: BIOS IRQ %d for %d.%d.INT%c does not match link %#x irq %d\n", 369145083Sjhb irq, entry->pe_bus, entry->pe_device, pin + 'A', 370145083Sjhb pci_link->pl_id, pci_link->pl_irq); 371103025Sjhb} 372103025Sjhb 373103025Sjhb/* 374125980Sjhb * Parse $PIR to enumerate link devices and attempt to determine their 375125980Sjhb * initial state. This could perhaps be cleaner if we had drivers for the 376125980Sjhb * various interrupt routers as they could read the initial IRQ for each 377125980Sjhb * link. 37868218Smsmith */ 379128933Sjhbstatic void 380125980Sjhbpci_pir_parse(void) 38168218Smsmith{ 382125980Sjhb char tunable_buffer[64]; 383125980Sjhb struct pci_link *pci_link; 384125980Sjhb int i, irq; 385125980Sjhb 386125980Sjhb /* Only parse once. */ 387125980Sjhb if (pir_parsed) 388125980Sjhb return; 389125980Sjhb pir_parsed = 1; 390125980Sjhb 391125980Sjhb /* Enumerate link devices. */ 392125980Sjhb TAILQ_INIT(&pci_links); 393125980Sjhb pci_pir_walk_table(pci_pir_create_links, NULL); 394125980Sjhb if (bootverbose) { 395125980Sjhb printf("$PIR: Links after initial probe:\n"); 396125980Sjhb pci_pir_dump_links(); 397100435Simp } 39867185Simp 399125980Sjhb /* Check for unique IRQ masks. */ 400125980Sjhb TAILQ_FOREACH(pci_link, &pci_links, pl_links) { 401125980Sjhb if (pci_link->pl_irqmask != 0 && powerof2(pci_link->pl_irqmask)) 402125980Sjhb pci_link->pl_irq = ffs(pci_link->pl_irqmask) - 1; 403125980Sjhb } 40468218Smsmith 405100435Simp /* 406125980Sjhb * Check to see if the BIOS has already routed any of the links by 407125980Sjhb * checking each device connected to each link to see if it has a 408125980Sjhb * valid IRQ. 409100435Simp */ 410125980Sjhb pci_pir_walk_table(pci_pir_initial_irqs, NULL); 411125980Sjhb if (bootverbose) { 412125980Sjhb printf("$PIR: Links after initial IRQ discovery:\n"); 413125980Sjhb pci_pir_dump_links(); 414125980Sjhb } 41568218Smsmith 416125980Sjhb /* 417145083Sjhb * Allow the user to override the IRQ for a given link device. We 418145083Sjhb * allow invalid IRQs to be specified but warn about them. An IRQ 419145083Sjhb * of 255 or 0 clears any preset IRQ. 420125980Sjhb */ 421125980Sjhb i = 0; 422125980Sjhb TAILQ_FOREACH(pci_link, &pci_links, pl_links) { 423125980Sjhb snprintf(tunable_buffer, sizeof(tunable_buffer), 424125980Sjhb "hw.pci.link.%#x.irq", pci_link->pl_id); 425125980Sjhb if (getenv_int(tunable_buffer, &irq) == 0) 426125980Sjhb continue; 427125980Sjhb if (irq == 0) 428125980Sjhb irq = PCI_INVALID_IRQ; 429145083Sjhb if (irq != PCI_INVALID_IRQ && 430145083Sjhb !pci_pir_valid_irq(pci_link, irq) && bootverbose) 431145083Sjhb printf( 432145083Sjhb "$PIR: Warning, IRQ %d for link %#x is not listed as valid\n", 433145083Sjhb irq, pci_link->pl_id); 434145083Sjhb pci_link->pl_routed = 0; 435145083Sjhb pci_link->pl_irq = irq; 436145083Sjhb i = 1; 43768218Smsmith } 438125980Sjhb if (bootverbose && i) { 439125980Sjhb printf("$PIR: Links after tunable overrides:\n"); 440125980Sjhb pci_pir_dump_links(); 441125980Sjhb } 442125980Sjhb 443125980Sjhb /* 444125980Sjhb * Build initial interrupt weights as well as bitmap of "known-good" 445125980Sjhb * IRQs that the BIOS has already used for PCI link devices. 446125980Sjhb */ 447125980Sjhb TAILQ_FOREACH(pci_link, &pci_links, pl_links) { 448125980Sjhb if (!PCI_INTERRUPT_VALID(pci_link->pl_irq)) 449125980Sjhb continue; 450125980Sjhb pir_bios_irqs |= 1 << pci_link->pl_irq; 451125980Sjhb pir_interrupt_weight[pci_link->pl_irq] += 452125980Sjhb pci_link->pl_references; 453125980Sjhb } 454125980Sjhb if (bootverbose) { 455125980Sjhb printf("$PIR: IRQs used by BIOS: "); 456125980Sjhb pci_print_irqmask(pir_bios_irqs); 457125980Sjhb printf("\n"); 458125980Sjhb printf("$PIR: Interrupt Weights:\n[ "); 459125980Sjhb for (i = 0; i < NUM_ISA_INTERRUPTS; i++) 460125980Sjhb printf(" %3d", i); 461125980Sjhb printf(" ]\n[ "); 462125980Sjhb for (i = 0; i < NUM_ISA_INTERRUPTS; i++) 463125980Sjhb printf(" %3d", pir_interrupt_weight[i]); 464125980Sjhb printf(" ]\n"); 465125980Sjhb } 46668218Smsmith} 46768218Smsmith 46868218Smsmith/* 469125980Sjhb * Use the PCI BIOS to route an interrupt for a given device. 470125980Sjhb * 471125980Sjhb * Input: 472125980Sjhb * AX = PCIBIOS_ROUTE_INTERRUPT 473125980Sjhb * BH = bus 474125980Sjhb * BL = device [7:3] / function [2:0] 475125980Sjhb * CH = IRQ 476125980Sjhb * CL = Interrupt Pin (0x0A = A, ... 0x0D = D) 47768218Smsmith */ 47868218Smsmithstatic int 479125980Sjhbpci_pir_biosroute(int bus, int device, int func, int pin, int irq) 48068218Smsmith{ 481125980Sjhb struct bios_regs args; 48268218Smsmith 483125980Sjhb args.eax = PCIBIOS_ROUTE_INTERRUPT; 484125980Sjhb args.ebx = (bus << 8) | (device << 3) | func; 485125980Sjhb args.ecx = (irq << 8) | (0xa + pin); 486125980Sjhb return (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))); 487125980Sjhb} 48868218Smsmith 489125980Sjhb 490125980Sjhb/* 491125980Sjhb * Route a PCI interrupt using a link device from the $PIR. 492125980Sjhb */ 493125980Sjhbint 494125980Sjhbpci_pir_route_interrupt(int bus, int device, int func, int pin) 495125980Sjhb{ 496125980Sjhb struct pci_link_lookup lookup; 497125980Sjhb struct pci_link *pci_link; 498125980Sjhb int error, irq; 499125980Sjhb 500125980Sjhb if (pci_route_table == NULL) 501125980Sjhb return (PCI_INVALID_IRQ); 502125980Sjhb 503125980Sjhb /* Lookup link device for this PCI device/pin. */ 504125980Sjhb pci_link = NULL; 505125980Sjhb lookup.bus = bus; 506125980Sjhb lookup.device = device; 507125980Sjhb lookup.pin = pin - 1; 508125980Sjhb lookup.pci_link_ptr = &pci_link; 509125980Sjhb pci_pir_walk_table(pci_pir_find_link_handler, &lookup); 510125980Sjhb if (pci_link == NULL) { 511125980Sjhb printf("$PIR: No matching entry for %d.%d.INT%c\n", bus, 512125980Sjhb device, pin - 1 + 'A'); 513125980Sjhb return (PCI_INVALID_IRQ); 514125980Sjhb } 515125980Sjhb 516100435Simp /* 517125980Sjhb * Pick a new interrupt if we don't have one already. We look for 518125980Sjhb * an interrupt from several different sets. First, we check the 519125980Sjhb * set of PCI only interrupts from the $PIR. Second, we check the 520125980Sjhb * set of known-good interrupts that the BIOS has already used. 521125980Sjhb * Lastly, we check the "all possible valid IRQs" set. 522100435Simp */ 523125980Sjhb if (!PCI_INTERRUPT_VALID(pci_link->pl_irq)) { 524125980Sjhb irq = pci_pir_choose_irq(pci_link, 525125980Sjhb pci_route_table->pt_header.ph_pci_irqs); 526125980Sjhb if (!PCI_INTERRUPT_VALID(irq)) 527125980Sjhb irq = pci_pir_choose_irq(pci_link, pir_bios_irqs); 528125980Sjhb if (!PCI_INTERRUPT_VALID(irq)) 529125980Sjhb irq = pci_pir_choose_irq(pci_link, 530125980Sjhb pci_irq_override_mask); 531125980Sjhb if (!PCI_INTERRUPT_VALID(irq)) { 532125980Sjhb if (bootverbose) 533125980Sjhb printf( 534125980Sjhb "$PIR: Failed to route interrupt for %d:%d INT%c\n", 535125980Sjhb bus, device, pin - 1 + 'A'); 536125980Sjhb return (PCI_INVALID_IRQ); 537100435Simp } 538125980Sjhb pci_link->pl_irq = irq; 53968218Smsmith } 540125980Sjhb 541125980Sjhb /* Ask the BIOS to route this IRQ if we haven't done so already. */ 542125980Sjhb if (!pci_link->pl_routed) { 543125980Sjhb error = pci_pir_biosroute(bus, device, func, pin - 1, 544125980Sjhb pci_link->pl_irq); 545125980Sjhb 546125980Sjhb /* Ignore errors when routing a unique interrupt. */ 547125980Sjhb if (error && !powerof2(pci_link->pl_irqmask)) { 548125980Sjhb printf("$PIR: ROUTE_INTERRUPT failed.\n"); 549125980Sjhb return (PCI_INVALID_IRQ); 550125980Sjhb } 551125980Sjhb pci_link->pl_routed = 1; 552128933Sjhb 553128933Sjhb /* Ensure the interrupt is set to level/low trigger. */ 554128933Sjhb KASSERT(pir_device != NULL, ("missing pir device")); 555128933Sjhb BUS_CONFIG_INTR(pir_device, pci_link->pl_irq, 556128933Sjhb INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW); 557125980Sjhb } 558131398Sjhb if (bootverbose) 559131398Sjhb printf("$PIR: %d:%d INT%c routed to irq %d\n", bus, device, 560131398Sjhb pin - 1 + 'A', pci_link->pl_irq); 561125980Sjhb return (pci_link->pl_irq); 56268218Smsmith} 56368218Smsmith 56468218Smsmith/* 565125980Sjhb * Try to pick an interrupt for the specified link from the interrupts 566125980Sjhb * set in the mask. 56768218Smsmith */ 56868218Smsmithstatic int 569125980Sjhbpci_pir_choose_irq(struct pci_link *pci_link, int irqmask) 57068218Smsmith{ 571125980Sjhb int i, irq, realmask; 57268218Smsmith 573125980Sjhb /* XXX: Need to have a #define of known bad IRQs to also mask out? */ 574125980Sjhb realmask = pci_link->pl_irqmask & irqmask; 575125980Sjhb if (realmask == 0) 576125980Sjhb return (PCI_INVALID_IRQ); 577125980Sjhb 578125980Sjhb /* Find IRQ with lowest weight. */ 579125980Sjhb irq = PCI_INVALID_IRQ; 580125980Sjhb for (i = 0; i < NUM_ISA_INTERRUPTS; i++) { 581125980Sjhb if (!(realmask & 1 << i)) 582118325Simp continue; 583125980Sjhb if (irq == PCI_INVALID_IRQ || 584125980Sjhb pir_interrupt_weight[i] < pir_interrupt_weight[irq]) 585125980Sjhb irq = i; 58668218Smsmith } 587125980Sjhb if (bootverbose && PCI_INTERRUPT_VALID(irq)) { 588125980Sjhb printf("$PIR: Found IRQ %d for link %#x from ", irq, 589125980Sjhb pci_link->pl_id); 590125980Sjhb pci_print_irqmask(realmask); 591125980Sjhb printf("\n"); 592125980Sjhb } 593125980Sjhb return (irq); 59468218Smsmith} 59568218Smsmith 596103017Sjhbstatic void 597103017Sjhbpci_print_irqmask(u_int16_t irqs) 598103017Sjhb{ 599103017Sjhb int i, first; 60068218Smsmith 601103017Sjhb if (irqs == 0) { 602103017Sjhb printf("none"); 603103017Sjhb return; 604103017Sjhb } 605103017Sjhb first = 1; 606103017Sjhb for (i = 0; i < 16; i++, irqs >>= 1) 607103017Sjhb if (irqs & 1) { 608103017Sjhb if (!first) 609103017Sjhb printf(" "); 610103017Sjhb else 611103017Sjhb first = 0; 612103017Sjhb printf("%d", i); 613103017Sjhb } 614103017Sjhb} 615103017Sjhb 61668218Smsmith/* 617125980Sjhb * Dump the contents of a single intpin entry to the console. 618125980Sjhb */ 619125980Sjhbstatic void 620125980Sjhbpci_pir_print_intpin(struct PIR_entry *entry, struct PIR_intpin *intpin, 621125980Sjhb void *arg) 622125980Sjhb{ 623125980Sjhb 624125980Sjhb if (entry->pe_slot == 0) 625125980Sjhb printf("embedded "); 626125980Sjhb else 627125980Sjhb printf("slot %-3d ", entry->pe_slot); 628125980Sjhb printf(" %3d %3d %c 0x%02x ", entry->pe_bus, entry->pe_device, 629125980Sjhb intpin - entry->pe_intpin + 'A', intpin->link); 630125980Sjhb pci_print_irqmask(intpin->irqs); 631125980Sjhb printf("\n"); 632125980Sjhb} 633125980Sjhb 634125980Sjhb/* 635103017Sjhb * Dump the contents of a PCI BIOS Interrupt Routing Table to the console. 636103017Sjhb */ 637103017Sjhbstatic void 638125980Sjhbpci_pir_print_table(void) 639103017Sjhb{ 640103017Sjhb 641103017Sjhb printf("PCI-Only Interrupts: "); 642125980Sjhb pci_print_irqmask(pci_route_table->pt_header.ph_pci_irqs); 643103017Sjhb printf("\nLocation Bus Device Pin Link IRQs\n"); 644125980Sjhb pci_pir_walk_table(pci_pir_print_intpin, NULL); 645125980Sjhb} 646125980Sjhb 647125980Sjhb/* 648125980Sjhb * Display link devices. 649125980Sjhb */ 650125980Sjhbstatic void 651125980Sjhbpci_pir_dump_links(void) 652125980Sjhb{ 653125980Sjhb struct pci_link *pci_link; 654125980Sjhb 655128933Sjhb printf("Link IRQ Rtd Ref IRQs\n"); 656125980Sjhb TAILQ_FOREACH(pci_link, &pci_links, pl_links) { 657128933Sjhb printf("%#4x %3d %c %3d ", pci_link->pl_id, 658128933Sjhb pci_link->pl_irq, pci_link->pl_routed ? 'Y' : 'N', 659125980Sjhb pci_link->pl_references); 660125980Sjhb pci_print_irqmask(pci_link->pl_irqmask); 661125980Sjhb printf("\n"); 662103017Sjhb } 663103017Sjhb} 664103017Sjhb 665103017Sjhb/* 666103043Sjhb * See if any interrupts for a given PCI bus are routed in the PIR. Don't 667125980Sjhb * even bother looking if the BIOS doesn't support routing anyways. If we 668125980Sjhb * are probing a PCI-PCI bridge, then require_parse will be true and we should 669125980Sjhb * only succeed if a host-PCI bridge has already attached and parsed the PIR. 670103043Sjhb */ 671103043Sjhbint 672125980Sjhbpci_pir_probe(int bus, int require_parse) 673103043Sjhb{ 674103043Sjhb int i; 675103043Sjhb 676125980Sjhb if (pci_route_table == NULL || (require_parse && !pir_parsed)) 677103043Sjhb return (0); 678103043Sjhb for (i = 0; i < pci_route_count; i++) 679103043Sjhb if (pci_route_table->pt_entry[i].pe_bus == bus) 680103043Sjhb return (1); 681103043Sjhb return (0); 682103043Sjhb} 683128933Sjhb 684128933Sjhb/* 685128933Sjhb * The driver for the new-bus psuedo device pir0 for the $PIR table. 686128933Sjhb */ 687128933Sjhb 688128933Sjhbstatic int 689128933Sjhbpir_probe(device_t dev) 690128933Sjhb{ 691128933Sjhb char buf[64]; 692128933Sjhb 693128933Sjhb snprintf(buf, sizeof(buf), "PCI Interrupt Routing Table: %d Entries", 694128933Sjhb pci_route_count); 695128933Sjhb device_set_desc_copy(dev, buf); 696128933Sjhb return (0); 697128933Sjhb} 698128933Sjhb 699128933Sjhbstatic int 700128933Sjhbpir_attach(device_t dev) 701128933Sjhb{ 702128933Sjhb 703128933Sjhb pci_pir_parse(); 704128933Sjhb KASSERT(pir_device == NULL, ("Multiple pir devices")); 705128933Sjhb pir_device = dev; 706128933Sjhb return (0); 707128933Sjhb} 708128933Sjhb 709128933Sjhbstatic void 710128933Sjhbpir_resume_find_device(struct PIR_entry *entry, struct PIR_intpin *intpin, 711128933Sjhb void *arg) 712128933Sjhb{ 713128933Sjhb struct pci_dev_lookup *pd; 714128933Sjhb 715128933Sjhb pd = (struct pci_dev_lookup *)arg; 716128933Sjhb if (intpin->link != pd->link || pd->bus != -1) 717128933Sjhb return; 718128933Sjhb pd->bus = entry->pe_bus; 719128933Sjhb pd->device = entry->pe_device; 720128933Sjhb pd->pin = intpin - entry->pe_intpin; 721128933Sjhb} 722128933Sjhb 723128933Sjhbstatic int 724128933Sjhbpir_resume(device_t dev) 725128933Sjhb{ 726128933Sjhb struct pci_dev_lookup pd; 727128933Sjhb struct pci_link *pci_link; 728128933Sjhb int error; 729128933Sjhb 730128933Sjhb /* Ask the BIOS to re-route each link that was already routed. */ 731128933Sjhb TAILQ_FOREACH(pci_link, &pci_links, pl_links) { 732128933Sjhb if (!PCI_INTERRUPT_VALID(pci_link->pl_irq)) { 733128933Sjhb KASSERT(!pci_link->pl_routed, 734128933Sjhb ("link %#x is routed but has invalid PCI IRQ", 735128933Sjhb pci_link->pl_id)); 736128933Sjhb continue; 737128933Sjhb } 738128933Sjhb if (pci_link->pl_routed) { 739128933Sjhb pd.bus = -1; 740128933Sjhb pd.link = pci_link->pl_id; 741128933Sjhb pci_pir_walk_table(pir_resume_find_device, &pd); 742128933Sjhb KASSERT(pd.bus != -1, 743128933Sjhb ("did not find matching entry for link %#x in the $PIR table", 744128933Sjhb pci_link->pl_id)); 745128933Sjhb if (bootverbose) 746128933Sjhb device_printf(dev, 747128933Sjhb "Using %d.%d.INT%c to route link %#x to IRQ %d\n", 748128933Sjhb pd.bus, pd.device, pd.pin + 'A', 749128933Sjhb pci_link->pl_id, pci_link->pl_irq); 750128933Sjhb error = pci_pir_biosroute(pd.bus, pd.device, 0, pd.pin, 751128933Sjhb pci_link->pl_irq); 752128933Sjhb if (error) 753128933Sjhb device_printf(dev, 754128933Sjhb "ROUTE_INTERRUPT on resume for link %#x failed.\n", 755128933Sjhb pci_link->pl_id); 756128933Sjhb } 757128933Sjhb } 758128933Sjhb return (0); 759128933Sjhb} 760128933Sjhb 761128933Sjhbstatic device_method_t pir_methods[] = { 762128933Sjhb /* Device interface */ 763128933Sjhb DEVMETHOD(device_probe, pir_probe), 764128933Sjhb DEVMETHOD(device_attach, pir_attach), 765128933Sjhb DEVMETHOD(device_resume, pir_resume), 766128933Sjhb 767128933Sjhb { 0, 0 } 768128933Sjhb}; 769128933Sjhb 770128933Sjhbstatic driver_t pir_driver = { 771128933Sjhb "pir", 772128933Sjhb pir_methods, 773128933Sjhb 1, 774128933Sjhb}; 775128933Sjhb 776128933Sjhbstatic devclass_t pir_devclass; 777128933Sjhb 778128933SjhbDRIVER_MODULE(pir, legacy, pir_driver, pir_devclass, 0, 0); 779