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$"); 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 uint8_t pci_pir_search_irq(int bus, int device, int pin); 96125980Sjhbstatic int pci_pir_valid_irq(struct pci_link *pci_link, int irq); 97125980Sjhbstatic void pci_pir_walk_table(pir_entry_handler *handler, void *arg); 9859294Smsmith 99141616Sphkstatic MALLOC_DEFINE(M_PIR, "$PIR", "$PIR structures"); 100125980Sjhb 101103017Sjhbstatic struct PIR_table *pci_route_table; 102128933Sjhbstatic device_t pir_device; 103125980Sjhbstatic int pci_route_count, pir_bios_irqs, pir_parsed; 104125980Sjhbstatic TAILQ_HEAD(, pci_link) pci_links; 105125980Sjhbstatic int pir_interrupt_weight[NUM_ISA_INTERRUPTS]; 10667185Simp 107118325Simp/* sysctl vars */ 108118325SimpSYSCTL_DECL(_hw_pci); 109118325Simp 110149889Simp/* XXX this likely should live in a header file */ 111118338Snyan#ifdef PC98 112125980Sjhb/* IRQs 3, 5, 7, 9, 10, 11, 12, 13 */ 113118338Snyan#define PCI_IRQ_OVERRIDE_MASK 0x3e68 114118338Snyan#else 115125980Sjhb/* IRQs 3, 4, 5, 6, 7, 9, 10, 11, 12, 14, 15 */ 116125980Sjhb#define PCI_IRQ_OVERRIDE_MASK 0xdef8 117118338Snyan#endif 118118338Snyan 119118338Snyanstatic uint32_t pci_irq_override_mask = PCI_IRQ_OVERRIDE_MASK; 120118325SimpTUNABLE_INT("hw.pci.irq_override_mask", &pci_irq_override_mask); 121121307SsilbySYSCTL_INT(_hw_pci, OID_AUTO, irq_override_mask, CTLFLAG_RDTUN, 122118338Snyan &pci_irq_override_mask, PCI_IRQ_OVERRIDE_MASK, 123118325Simp "Mask of allowed irqs to try to route when it has no good clue about\n" 124118325Simp "which irqs it should use."); 125118325Simp 12697694Simp/* 127125980Sjhb * Look for the interrupt routing table. 128125980Sjhb * 129125980Sjhb * We use PCI BIOS's PIR table if it's available. $PIR is the standard way 130125980Sjhb * to do this. Sadly, some machines are not standards conforming and have 131125980Sjhb * _PIR instead. We shrug and cope by looking for both. 13297694Simp */ 133125980Sjhbvoid 134125980Sjhbpci_pir_open(void) 13597694Simp{ 136125980Sjhb struct PIR_table *pt; 137125980Sjhb uint32_t sigaddr; 138125980Sjhb int i; 139125980Sjhb uint8_t ck, *cv; 140125980Sjhb 141181775Skmacy#ifdef XEN 142181775Skmacy return; 143181775Skmacy#else 144125980Sjhb /* Don't try if we've already found a table. */ 145125980Sjhb if (pci_route_table != NULL) 146125980Sjhb return; 147125980Sjhb 148125980Sjhb /* Look for $PIR and then _PIR. */ 149125980Sjhb sigaddr = bios_sigsearch(0, "$PIR", 4, 16, 0); 150125980Sjhb if (sigaddr == 0) 151125980Sjhb sigaddr = bios_sigsearch(0, "_PIR", 4, 16, 0); 152125980Sjhb if (sigaddr == 0) 153125980Sjhb return; 154181775Skmacy#endif 155125980Sjhb /* If we found something, check the checksum and length. */ 156125980Sjhb /* XXX - Use pmap_mapdev()? */ 157125980Sjhb pt = (struct PIR_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr); 158125980Sjhb if (pt->pt_header.ph_length <= sizeof(struct PIR_header)) 159125980Sjhb return; 160125980Sjhb for (cv = (u_int8_t *)pt, ck = 0, i = 0; 161125980Sjhb i < (pt->pt_header.ph_length); i++) 162125980Sjhb ck += cv[i]; 163125980Sjhb if (ck != 0) 164125980Sjhb return; 165125980Sjhb 166125980Sjhb /* Ok, we've got a valid table. */ 167125980Sjhb pci_route_table = pt; 168125980Sjhb pci_route_count = (pt->pt_header.ph_length - 169125980Sjhb sizeof(struct PIR_header)) / 170125980Sjhb sizeof(struct PIR_entry); 17197694Simp} 17297694Simp 173125980Sjhb/* 174125980Sjhb * Find the pci_link structure for a given link ID. 175125980Sjhb */ 176125980Sjhbstatic struct pci_link * 177125980Sjhbpci_pir_find_link(uint8_t link_id) 17882441Simp{ 179125980Sjhb struct pci_link *pci_link; 18082441Simp 181125980Sjhb TAILQ_FOREACH(pci_link, &pci_links, pl_links) { 182125980Sjhb if (pci_link->pl_id == link_id) 183125980Sjhb return (pci_link); 184100435Simp } 185125980Sjhb return (NULL); 18682441Simp} 18782441Simp 188125980Sjhb/* 189125980Sjhb * Find the link device associated with a PCI device in the table. 19066529Smsmith */ 191125980Sjhbstatic void 192125980Sjhbpci_pir_find_link_handler(struct PIR_entry *entry, struct PIR_intpin *intpin, 193125980Sjhb void *arg) 19459294Smsmith{ 195125980Sjhb struct pci_link_lookup *lookup; 19665176Sdfr 197125980Sjhb lookup = (struct pci_link_lookup *)arg; 198125980Sjhb if (entry->pe_bus == lookup->bus && 199125980Sjhb entry->pe_device == lookup->device && 200125980Sjhb intpin - entry->pe_intpin == lookup->pin) 201125980Sjhb *lookup->pci_link_ptr = pci_pir_find_link(intpin->link); 202125980Sjhb} 20366529Smsmith 204125980Sjhb/* 205125980Sjhb * Check to see if a possible IRQ setting is valid. 206125980Sjhb */ 207125980Sjhbstatic int 208125980Sjhbpci_pir_valid_irq(struct pci_link *pci_link, int irq) 209125980Sjhb{ 21067185Simp 211125980Sjhb if (!PCI_INTERRUPT_VALID(irq)) 212125980Sjhb return (0); 213125980Sjhb return (pci_link->pl_irqmask & (1 << irq)); 21459294Smsmith} 21559294Smsmith 216125980Sjhb/* 217125980Sjhb * Walk the $PIR executing the worker function for each valid intpin entry 218125980Sjhb * in the table. The handler is passed a pointer to both the entry and 219125980Sjhb * the intpin in the entry. 22066529Smsmith */ 221125980Sjhbstatic void 222125980Sjhbpci_pir_walk_table(pir_entry_handler *handler, void *arg) 22369783Smsmith{ 224125980Sjhb struct PIR_entry *entry; 225125980Sjhb struct PIR_intpin *intpin; 226125980Sjhb int i, pin; 22769783Smsmith 228125980Sjhb entry = &pci_route_table->pt_entry[0]; 229125980Sjhb for (i = 0; i < pci_route_count; i++, entry++) { 230125980Sjhb intpin = &entry->pe_intpin[0]; 231125980Sjhb for (pin = 0; pin < 4; pin++, intpin++) 232125980Sjhb if (intpin->link != 0) 233125980Sjhb handler(entry, intpin, arg); 234100435Simp } 23569783Smsmith} 23669783Smsmith 237125980Sjhbstatic void 238125980Sjhbpci_pir_create_links(struct PIR_entry *entry, struct PIR_intpin *intpin, 239125980Sjhb void *arg) 24059294Smsmith{ 241125980Sjhb struct pci_link *pci_link; 242111068Speter 243125980Sjhb pci_link = pci_pir_find_link(intpin->link); 244125980Sjhb if (pci_link != NULL) { 245125980Sjhb pci_link->pl_references++; 246125980Sjhb if (intpin->irqs != pci_link->pl_irqmask) { 247125980Sjhb if (bootverbose) 248125980Sjhb printf( 249125980Sjhb "$PIR: Entry %d.%d.INT%c has different mask for link %#x, merging\n", 250125980Sjhb entry->pe_bus, entry->pe_device, 251125980Sjhb (intpin - entry->pe_intpin) + 'A', 252125980Sjhb pci_link->pl_id); 253125980Sjhb pci_link->pl_irqmask &= intpin->irqs; 254125980Sjhb } 255125980Sjhb } else { 256125980Sjhb pci_link = malloc(sizeof(struct pci_link), M_PIR, M_WAITOK); 257125980Sjhb pci_link->pl_id = intpin->link; 258125980Sjhb pci_link->pl_irqmask = intpin->irqs; 259125980Sjhb pci_link->pl_irq = PCI_INVALID_IRQ; 260125980Sjhb pci_link->pl_references = 1; 261125980Sjhb pci_link->pl_routed = 0; 262125980Sjhb TAILQ_INSERT_TAIL(&pci_links, pci_link, pl_links); 263125980Sjhb } 26459294Smsmith} 26559294Smsmith 26666529Smsmith/* 267125980Sjhb * Look to see if any of the function on the PCI device at bus/device have 268125980Sjhb * an interrupt routed to intpin 'pin' by the BIOS. 26967185Simp */ 270125980Sjhbstatic uint8_t 271125980Sjhbpci_pir_search_irq(int bus, int device, int pin) 27267185Simp{ 273125980Sjhb uint32_t value; 274125980Sjhb uint8_t func, maxfunc; 275125980Sjhb 276125980Sjhb /* See if we have a valid device at function 0. */ 277125980Sjhb value = pci_cfgregread(bus, device, 0, PCIR_HDRTYPE, 1); 278125980Sjhb if ((value & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) 279100435Simp return (PCI_INVALID_IRQ); 280125980Sjhb if (value & PCIM_MFDEV) 281125980Sjhb maxfunc = PCI_FUNCMAX; 282125980Sjhb else 283125980Sjhb maxfunc = 0; 28467185Simp 285125980Sjhb /* Scan all possible functions at this device. */ 286125980Sjhb for (func = 0; func <= maxfunc; func++) { 287125980Sjhb value = pci_cfgregread(bus, device, func, PCIR_DEVVENDOR, 4); 288125980Sjhb if (value == 0xffffffff) 289100435Simp continue; 290125980Sjhb value = pci_cfgregread(bus, device, func, PCIR_INTPIN, 1); 291125980Sjhb 292103025Sjhb /* 293125980Sjhb * See if it uses the pin in question. Note that the passed 294125980Sjhb * in pin uses 0 for A, .. 3 for D whereas the intpin 295125980Sjhb * register uses 0 for no interrupt, 1 for A, .. 4 for D. 296103025Sjhb */ 297125980Sjhb if (value != pin + 1) 298103025Sjhb continue; 299125980Sjhb value = pci_cfgregread(bus, device, func, PCIR_INTLINE, 1); 300125980Sjhb if (bootverbose) 301125980Sjhb printf( 302125980Sjhb "$PIR: Found matching pin for %d.%d.INT%c at func %d: %d\n", 303125980Sjhb bus, device, pin + 'A', func, value); 304125980Sjhb if (value != PCI_INVALID_IRQ) 305125980Sjhb return (value); 30682441Simp } 307125980Sjhb return (PCI_INVALID_IRQ); 30867185Simp} 30967185Simp 31068218Smsmith/* 311125980Sjhb * Try to initialize IRQ based on this device's IRQ. 312103025Sjhb */ 313125980Sjhbstatic void 314125980Sjhbpci_pir_initial_irqs(struct PIR_entry *entry, struct PIR_intpin *intpin, 315125980Sjhb void *arg) 316103025Sjhb{ 317125980Sjhb struct pci_link *pci_link; 318125980Sjhb uint8_t irq, pin; 319103025Sjhb 320125980Sjhb pin = intpin - entry->pe_intpin; 321125980Sjhb pci_link = pci_pir_find_link(intpin->link); 322125980Sjhb irq = pci_pir_search_irq(entry->pe_bus, entry->pe_device, pin); 323145083Sjhb if (irq == PCI_INVALID_IRQ || irq == pci_link->pl_irq) 324125980Sjhb return; 325145083Sjhb 326147968Sjhb /* Don't trust any BIOS IRQs greater than 15. */ 327147968Sjhb if (irq >= NUM_ISA_INTERRUPTS) { 328147968Sjhb printf( 329147968Sjhb "$PIR: Ignoring invalid BIOS IRQ %d from %d.%d.INT%c for link %#x\n", 330147968Sjhb irq, entry->pe_bus, entry->pe_device, pin + 'A', 331147968Sjhb pci_link->pl_id); 332147968Sjhb return; 333147968Sjhb } 334147968Sjhb 335145083Sjhb /* 336145083Sjhb * If we don't have an IRQ for this link yet, then we trust the 337145083Sjhb * BIOS, even if it seems invalid from the $PIR entries. 338145083Sjhb */ 339145083Sjhb if (pci_link->pl_irq == PCI_INVALID_IRQ) { 340145083Sjhb if (!pci_pir_valid_irq(pci_link, irq)) 341125980Sjhb printf( 342147968Sjhb "$PIR: Using invalid BIOS IRQ %d from %d.%d.INT%c for link %#x\n", 343125980Sjhb irq, entry->pe_bus, entry->pe_device, pin + 'A', 344145083Sjhb pci_link->pl_id); 345145083Sjhb pci_link->pl_irq = irq; 346145083Sjhb pci_link->pl_routed = 1; 347145083Sjhb return; 348145083Sjhb } 349145083Sjhb 350145083Sjhb /* 351145083Sjhb * We have an IRQ and it doesn't match the current IRQ for this 352145083Sjhb * link. If the new IRQ is invalid, then warn about it and ignore 353145083Sjhb * it. If the old IRQ is invalid and the new IRQ is valid, then 354145083Sjhb * prefer the new IRQ instead. If both IRQs are valid, then just 355145083Sjhb * use the first one. Note that if we ever get into this situation 356145083Sjhb * we are having to guess which setting the BIOS actually routed. 357145083Sjhb * Perhaps we should just give up instead. 358145083Sjhb */ 359145083Sjhb if (!pci_pir_valid_irq(pci_link, irq)) { 360125980Sjhb printf( 361125980Sjhb "$PIR: BIOS IRQ %d for %d.%d.INT%c is not valid for link %#x\n", 362125980Sjhb irq, entry->pe_bus, entry->pe_device, pin + 'A', 363125980Sjhb pci_link->pl_id); 364145083Sjhb } else if (!pci_pir_valid_irq(pci_link, pci_link->pl_irq)) { 365145083Sjhb printf( 366145083Sjhb"$PIR: Preferring valid BIOS IRQ %d from %d.%d.INT%c for link %#x to IRQ %d\n", 367145083Sjhb irq, entry->pe_bus, entry->pe_device, pin + 'A', 368145083Sjhb pci_link->pl_id, pci_link->pl_irq); 369145083Sjhb pci_link->pl_irq = irq; 370145083Sjhb pci_link->pl_routed = 1; 371145083Sjhb } else 372145083Sjhb printf( 373145083Sjhb "$PIR: BIOS IRQ %d for %d.%d.INT%c does not match link %#x irq %d\n", 374145083Sjhb irq, entry->pe_bus, entry->pe_device, pin + 'A', 375145083Sjhb pci_link->pl_id, pci_link->pl_irq); 376103025Sjhb} 377103025Sjhb 378103025Sjhb/* 379125980Sjhb * Parse $PIR to enumerate link devices and attempt to determine their 380125980Sjhb * initial state. This could perhaps be cleaner if we had drivers for the 381125980Sjhb * various interrupt routers as they could read the initial IRQ for each 382125980Sjhb * link. 38368218Smsmith */ 384128933Sjhbstatic void 385125980Sjhbpci_pir_parse(void) 38668218Smsmith{ 387125980Sjhb char tunable_buffer[64]; 388125980Sjhb struct pci_link *pci_link; 389125980Sjhb int i, irq; 390125980Sjhb 391125980Sjhb /* Only parse once. */ 392125980Sjhb if (pir_parsed) 393125980Sjhb return; 394125980Sjhb pir_parsed = 1; 395125980Sjhb 396125980Sjhb /* Enumerate link devices. */ 397125980Sjhb TAILQ_INIT(&pci_links); 398125980Sjhb pci_pir_walk_table(pci_pir_create_links, NULL); 399125980Sjhb if (bootverbose) { 400125980Sjhb printf("$PIR: Links after initial probe:\n"); 401125980Sjhb pci_pir_dump_links(); 402100435Simp } 40367185Simp 404100435Simp /* 405125980Sjhb * Check to see if the BIOS has already routed any of the links by 406125980Sjhb * checking each device connected to each link to see if it has a 407125980Sjhb * valid IRQ. 408100435Simp */ 409125980Sjhb pci_pir_walk_table(pci_pir_initial_irqs, NULL); 410125980Sjhb if (bootverbose) { 411125980Sjhb printf("$PIR: Links after initial IRQ discovery:\n"); 412125980Sjhb pci_pir_dump_links(); 413125980Sjhb } 41468218Smsmith 415125980Sjhb /* 416145083Sjhb * Allow the user to override the IRQ for a given link device. We 417145083Sjhb * allow invalid IRQs to be specified but warn about them. An IRQ 418145083Sjhb * of 255 or 0 clears any preset IRQ. 419125980Sjhb */ 420125980Sjhb i = 0; 421125980Sjhb TAILQ_FOREACH(pci_link, &pci_links, pl_links) { 422125980Sjhb snprintf(tunable_buffer, sizeof(tunable_buffer), 423125980Sjhb "hw.pci.link.%#x.irq", pci_link->pl_id); 424125980Sjhb if (getenv_int(tunable_buffer, &irq) == 0) 425125980Sjhb continue; 426125980Sjhb if (irq == 0) 427125980Sjhb irq = PCI_INVALID_IRQ; 428145083Sjhb if (irq != PCI_INVALID_IRQ && 429145083Sjhb !pci_pir_valid_irq(pci_link, irq) && bootverbose) 430145083Sjhb printf( 431145083Sjhb "$PIR: Warning, IRQ %d for link %#x is not listed as valid\n", 432145083Sjhb irq, pci_link->pl_id); 433145083Sjhb pci_link->pl_routed = 0; 434145083Sjhb pci_link->pl_irq = irq; 435145083Sjhb i = 1; 43668218Smsmith } 437125980Sjhb if (bootverbose && i) { 438125980Sjhb printf("$PIR: Links after tunable overrides:\n"); 439125980Sjhb pci_pir_dump_links(); 440125980Sjhb } 441125980Sjhb 442125980Sjhb /* 443125980Sjhb * Build initial interrupt weights as well as bitmap of "known-good" 444125980Sjhb * IRQs that the BIOS has already used for PCI link devices. 445125980Sjhb */ 446125980Sjhb TAILQ_FOREACH(pci_link, &pci_links, pl_links) { 447125980Sjhb if (!PCI_INTERRUPT_VALID(pci_link->pl_irq)) 448125980Sjhb continue; 449125980Sjhb pir_bios_irqs |= 1 << pci_link->pl_irq; 450125980Sjhb pir_interrupt_weight[pci_link->pl_irq] += 451125980Sjhb pci_link->pl_references; 452125980Sjhb } 453125980Sjhb if (bootverbose) { 454125980Sjhb printf("$PIR: IRQs used by BIOS: "); 455125980Sjhb pci_print_irqmask(pir_bios_irqs); 456125980Sjhb printf("\n"); 457125980Sjhb printf("$PIR: Interrupt Weights:\n[ "); 458125980Sjhb for (i = 0; i < NUM_ISA_INTERRUPTS; i++) 459125980Sjhb printf(" %3d", i); 460125980Sjhb printf(" ]\n[ "); 461125980Sjhb for (i = 0; i < NUM_ISA_INTERRUPTS; i++) 462125980Sjhb printf(" %3d", pir_interrupt_weight[i]); 463125980Sjhb printf(" ]\n"); 464125980Sjhb } 46568218Smsmith} 46668218Smsmith 46768218Smsmith/* 468125980Sjhb * Use the PCI BIOS to route an interrupt for a given device. 469125980Sjhb * 470125980Sjhb * Input: 471125980Sjhb * AX = PCIBIOS_ROUTE_INTERRUPT 472125980Sjhb * BH = bus 473125980Sjhb * BL = device [7:3] / function [2:0] 474125980Sjhb * CH = IRQ 475125980Sjhb * CL = Interrupt Pin (0x0A = A, ... 0x0D = D) 47668218Smsmith */ 47768218Smsmithstatic int 478125980Sjhbpci_pir_biosroute(int bus, int device, int func, int pin, int irq) 47968218Smsmith{ 480125980Sjhb struct bios_regs args; 48168218Smsmith 482125980Sjhb args.eax = PCIBIOS_ROUTE_INTERRUPT; 483125980Sjhb args.ebx = (bus << 8) | (device << 3) | func; 484125980Sjhb args.ecx = (irq << 8) | (0xa + pin); 485181775Skmacy#ifdef XEN 486181775Skmacy return (0); 487181775Skmacy#else 488125980Sjhb return (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))); 489181775Skmacy#endif 490125980Sjhb} 49168218Smsmith 492125980Sjhb 493125980Sjhb/* 494125980Sjhb * Route a PCI interrupt using a link device from the $PIR. 495125980Sjhb */ 496125980Sjhbint 497125980Sjhbpci_pir_route_interrupt(int bus, int device, int func, int pin) 498125980Sjhb{ 499125980Sjhb struct pci_link_lookup lookup; 500125980Sjhb struct pci_link *pci_link; 501125980Sjhb int error, irq; 502125980Sjhb 503125980Sjhb if (pci_route_table == NULL) 504125980Sjhb return (PCI_INVALID_IRQ); 505125980Sjhb 506125980Sjhb /* Lookup link device for this PCI device/pin. */ 507125980Sjhb pci_link = NULL; 508125980Sjhb lookup.bus = bus; 509125980Sjhb lookup.device = device; 510125980Sjhb lookup.pin = pin - 1; 511125980Sjhb lookup.pci_link_ptr = &pci_link; 512125980Sjhb pci_pir_walk_table(pci_pir_find_link_handler, &lookup); 513125980Sjhb if (pci_link == NULL) { 514125980Sjhb printf("$PIR: No matching entry for %d.%d.INT%c\n", bus, 515125980Sjhb device, pin - 1 + 'A'); 516125980Sjhb return (PCI_INVALID_IRQ); 517125980Sjhb } 518125980Sjhb 519100435Simp /* 520174840Sjhb * Pick a new interrupt if we don't have one already. We look 521174840Sjhb * for an interrupt from several different sets. First, if 522174840Sjhb * this link only has one valid IRQ, use that. Second, we 523174840Sjhb * check the set of PCI only interrupts from the $PIR. Third, 524174840Sjhb * we check the set of known-good interrupts that the BIOS has 525174840Sjhb * already used. Lastly, we check the "all possible valid 526174840Sjhb * IRQs" set. 527100435Simp */ 528125980Sjhb if (!PCI_INTERRUPT_VALID(pci_link->pl_irq)) { 529174840Sjhb if (pci_link->pl_irqmask != 0 && powerof2(pci_link->pl_irqmask)) 530174840Sjhb irq = ffs(pci_link->pl_irqmask) - 1; 531174840Sjhb else 532174840Sjhb irq = pci_pir_choose_irq(pci_link, 533174840Sjhb pci_route_table->pt_header.ph_pci_irqs); 534125980Sjhb if (!PCI_INTERRUPT_VALID(irq)) 535125980Sjhb irq = pci_pir_choose_irq(pci_link, pir_bios_irqs); 536125980Sjhb if (!PCI_INTERRUPT_VALID(irq)) 537125980Sjhb irq = pci_pir_choose_irq(pci_link, 538125980Sjhb pci_irq_override_mask); 539125980Sjhb if (!PCI_INTERRUPT_VALID(irq)) { 540125980Sjhb if (bootverbose) 541125980Sjhb printf( 542125980Sjhb "$PIR: Failed to route interrupt for %d:%d INT%c\n", 543125980Sjhb bus, device, pin - 1 + 'A'); 544125980Sjhb return (PCI_INVALID_IRQ); 545100435Simp } 546125980Sjhb pci_link->pl_irq = irq; 54768218Smsmith } 548125980Sjhb 549125980Sjhb /* Ask the BIOS to route this IRQ if we haven't done so already. */ 550125980Sjhb if (!pci_link->pl_routed) { 551125980Sjhb error = pci_pir_biosroute(bus, device, func, pin - 1, 552125980Sjhb pci_link->pl_irq); 553125980Sjhb 554125980Sjhb /* Ignore errors when routing a unique interrupt. */ 555125980Sjhb if (error && !powerof2(pci_link->pl_irqmask)) { 556125980Sjhb printf("$PIR: ROUTE_INTERRUPT failed.\n"); 557125980Sjhb return (PCI_INVALID_IRQ); 558125980Sjhb } 559125980Sjhb pci_link->pl_routed = 1; 560128933Sjhb 561128933Sjhb /* Ensure the interrupt is set to level/low trigger. */ 562128933Sjhb KASSERT(pir_device != NULL, ("missing pir device")); 563128933Sjhb BUS_CONFIG_INTR(pir_device, pci_link->pl_irq, 564128933Sjhb INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW); 565125980Sjhb } 566131398Sjhb if (bootverbose) 567131398Sjhb printf("$PIR: %d:%d INT%c routed to irq %d\n", bus, device, 568131398Sjhb pin - 1 + 'A', pci_link->pl_irq); 569125980Sjhb return (pci_link->pl_irq); 57068218Smsmith} 57168218Smsmith 57268218Smsmith/* 573125980Sjhb * Try to pick an interrupt for the specified link from the interrupts 574125980Sjhb * set in the mask. 57568218Smsmith */ 57668218Smsmithstatic int 577125980Sjhbpci_pir_choose_irq(struct pci_link *pci_link, int irqmask) 57868218Smsmith{ 579125980Sjhb int i, irq, realmask; 58068218Smsmith 581125980Sjhb /* XXX: Need to have a #define of known bad IRQs to also mask out? */ 582125980Sjhb realmask = pci_link->pl_irqmask & irqmask; 583125980Sjhb if (realmask == 0) 584125980Sjhb return (PCI_INVALID_IRQ); 585125980Sjhb 586125980Sjhb /* Find IRQ with lowest weight. */ 587125980Sjhb irq = PCI_INVALID_IRQ; 588125980Sjhb for (i = 0; i < NUM_ISA_INTERRUPTS; i++) { 589125980Sjhb if (!(realmask & 1 << i)) 590118325Simp continue; 591125980Sjhb if (irq == PCI_INVALID_IRQ || 592125980Sjhb pir_interrupt_weight[i] < pir_interrupt_weight[irq]) 593125980Sjhb irq = i; 59468218Smsmith } 595125980Sjhb if (bootverbose && PCI_INTERRUPT_VALID(irq)) { 596125980Sjhb printf("$PIR: Found IRQ %d for link %#x from ", irq, 597125980Sjhb pci_link->pl_id); 598125980Sjhb pci_print_irqmask(realmask); 599125980Sjhb printf("\n"); 600125980Sjhb } 601125980Sjhb return (irq); 60268218Smsmith} 60368218Smsmith 604103017Sjhbstatic void 605103017Sjhbpci_print_irqmask(u_int16_t irqs) 606103017Sjhb{ 607103017Sjhb int i, first; 60868218Smsmith 609103017Sjhb if (irqs == 0) { 610103017Sjhb printf("none"); 611103017Sjhb return; 612103017Sjhb } 613103017Sjhb first = 1; 614103017Sjhb for (i = 0; i < 16; i++, irqs >>= 1) 615103017Sjhb if (irqs & 1) { 616103017Sjhb if (!first) 617103017Sjhb printf(" "); 618103017Sjhb else 619103017Sjhb first = 0; 620103017Sjhb printf("%d", i); 621103017Sjhb } 622103017Sjhb} 623103017Sjhb 62468218Smsmith/* 625125980Sjhb * Display link devices. 626125980Sjhb */ 627125980Sjhbstatic void 628125980Sjhbpci_pir_dump_links(void) 629125980Sjhb{ 630125980Sjhb struct pci_link *pci_link; 631125980Sjhb 632128933Sjhb printf("Link IRQ Rtd Ref IRQs\n"); 633125980Sjhb TAILQ_FOREACH(pci_link, &pci_links, pl_links) { 634128933Sjhb printf("%#4x %3d %c %3d ", pci_link->pl_id, 635128933Sjhb pci_link->pl_irq, pci_link->pl_routed ? 'Y' : 'N', 636125980Sjhb pci_link->pl_references); 637125980Sjhb pci_print_irqmask(pci_link->pl_irqmask); 638125980Sjhb printf("\n"); 639103017Sjhb } 640103017Sjhb} 641103017Sjhb 642103017Sjhb/* 643103043Sjhb * See if any interrupts for a given PCI bus are routed in the PIR. Don't 644125980Sjhb * even bother looking if the BIOS doesn't support routing anyways. If we 645125980Sjhb * are probing a PCI-PCI bridge, then require_parse will be true and we should 646125980Sjhb * only succeed if a host-PCI bridge has already attached and parsed the PIR. 647103043Sjhb */ 648103043Sjhbint 649125980Sjhbpci_pir_probe(int bus, int require_parse) 650103043Sjhb{ 651103043Sjhb int i; 652103043Sjhb 653125980Sjhb if (pci_route_table == NULL || (require_parse && !pir_parsed)) 654103043Sjhb return (0); 655103043Sjhb for (i = 0; i < pci_route_count; i++) 656103043Sjhb if (pci_route_table->pt_entry[i].pe_bus == bus) 657103043Sjhb return (1); 658103043Sjhb return (0); 659103043Sjhb} 660128933Sjhb 661128933Sjhb/* 662128933Sjhb * The driver for the new-bus psuedo device pir0 for the $PIR table. 663128933Sjhb */ 664128933Sjhb 665128933Sjhbstatic int 666128933Sjhbpir_probe(device_t dev) 667128933Sjhb{ 668128933Sjhb char buf[64]; 669128933Sjhb 670128933Sjhb snprintf(buf, sizeof(buf), "PCI Interrupt Routing Table: %d Entries", 671128933Sjhb pci_route_count); 672128933Sjhb device_set_desc_copy(dev, buf); 673128933Sjhb return (0); 674128933Sjhb} 675128933Sjhb 676128933Sjhbstatic int 677128933Sjhbpir_attach(device_t dev) 678128933Sjhb{ 679128933Sjhb 680128933Sjhb pci_pir_parse(); 681128933Sjhb KASSERT(pir_device == NULL, ("Multiple pir devices")); 682128933Sjhb pir_device = dev; 683128933Sjhb return (0); 684128933Sjhb} 685128933Sjhb 686128933Sjhbstatic void 687128933Sjhbpir_resume_find_device(struct PIR_entry *entry, struct PIR_intpin *intpin, 688128933Sjhb void *arg) 689128933Sjhb{ 690128933Sjhb struct pci_dev_lookup *pd; 691128933Sjhb 692128933Sjhb pd = (struct pci_dev_lookup *)arg; 693128933Sjhb if (intpin->link != pd->link || pd->bus != -1) 694128933Sjhb return; 695128933Sjhb pd->bus = entry->pe_bus; 696128933Sjhb pd->device = entry->pe_device; 697128933Sjhb pd->pin = intpin - entry->pe_intpin; 698128933Sjhb} 699128933Sjhb 700128933Sjhbstatic int 701128933Sjhbpir_resume(device_t dev) 702128933Sjhb{ 703128933Sjhb struct pci_dev_lookup pd; 704128933Sjhb struct pci_link *pci_link; 705128933Sjhb int error; 706128933Sjhb 707128933Sjhb /* Ask the BIOS to re-route each link that was already routed. */ 708128933Sjhb TAILQ_FOREACH(pci_link, &pci_links, pl_links) { 709128933Sjhb if (!PCI_INTERRUPT_VALID(pci_link->pl_irq)) { 710128933Sjhb KASSERT(!pci_link->pl_routed, 711128933Sjhb ("link %#x is routed but has invalid PCI IRQ", 712128933Sjhb pci_link->pl_id)); 713128933Sjhb continue; 714128933Sjhb } 715128933Sjhb if (pci_link->pl_routed) { 716128933Sjhb pd.bus = -1; 717128933Sjhb pd.link = pci_link->pl_id; 718128933Sjhb pci_pir_walk_table(pir_resume_find_device, &pd); 719128933Sjhb KASSERT(pd.bus != -1, 720128933Sjhb ("did not find matching entry for link %#x in the $PIR table", 721128933Sjhb pci_link->pl_id)); 722128933Sjhb if (bootverbose) 723128933Sjhb device_printf(dev, 724128933Sjhb "Using %d.%d.INT%c to route link %#x to IRQ %d\n", 725128933Sjhb pd.bus, pd.device, pd.pin + 'A', 726128933Sjhb pci_link->pl_id, pci_link->pl_irq); 727128933Sjhb error = pci_pir_biosroute(pd.bus, pd.device, 0, pd.pin, 728128933Sjhb pci_link->pl_irq); 729128933Sjhb if (error) 730128933Sjhb device_printf(dev, 731128933Sjhb "ROUTE_INTERRUPT on resume for link %#x failed.\n", 732128933Sjhb pci_link->pl_id); 733128933Sjhb } 734128933Sjhb } 735128933Sjhb return (0); 736128933Sjhb} 737128933Sjhb 738128933Sjhbstatic device_method_t pir_methods[] = { 739128933Sjhb /* Device interface */ 740128933Sjhb DEVMETHOD(device_probe, pir_probe), 741128933Sjhb DEVMETHOD(device_attach, pir_attach), 742128933Sjhb DEVMETHOD(device_resume, pir_resume), 743128933Sjhb 744128933Sjhb { 0, 0 } 745128933Sjhb}; 746128933Sjhb 747128933Sjhbstatic driver_t pir_driver = { 748128933Sjhb "pir", 749128933Sjhb pir_methods, 750128933Sjhb 1, 751128933Sjhb}; 752128933Sjhb 753128933Sjhbstatic devclass_t pir_devclass; 754128933Sjhb 755128933SjhbDRIVER_MODULE(pir, legacy, pir_driver, pir_devclass, 0, 0); 756