diff options
author | jhb <jhb@FreeBSD.org> | 2004-05-04 21:17:52 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2004-05-04 21:17:52 +0000 |
commit | e7ea4d6595c69e6d30df8b526a0047fdffe66908 (patch) | |
tree | 6cdccb56a7152e273017aceacad7015746d2f71d | |
parent | a11e3152f3bc324212fd54cab137aa464b64951d (diff) | |
download | FreeBSD-src-e7ea4d6595c69e6d30df8b526a0047fdffe66908.zip FreeBSD-src-e7ea4d6595c69e6d30df8b526a0047fdffe66908.tar.gz |
- Create a pir0 psuedo device as a child of legacy0 if we attach a legacy
host-PCI bridge device and find a valid $PIR.
- Make pci_pir_parse() private to pci_pir.c and have pir0's attach routine
call it instead of having legacy_pcib_attach() call it.
- Implement suspend/resume support for the $PIR by giving pir0 a resume
method that calls the BIOS to reroute each link that was already routed
before the machine was suspended.
- Dump the state of the routed flag in the links display code.
- If a link's IRQ is set by a tunable, then force that link to be re-routed
the first time it is used.
- Move the 'Found $PIR' message under bootverbose as the pir0 description
line lists the number of entries already. The pir0 line also only shows
up if we are actually using the $PIR which is a bonus.
- Use BUS_CONFIG_INTR() to ensure that any IRQs used by a PCI link are
set to level/low trigger/polarity.
-rw-r--r-- | sys/i386/include/pci_cfgreg.h | 1 | ||||
-rw-r--r-- | sys/i386/pci/pci_bus.c | 8 | ||||
-rw-r--r-- | sys/i386/pci/pci_pir.c | 125 |
3 files changed, 125 insertions, 9 deletions
diff --git a/sys/i386/include/pci_cfgreg.h b/sys/i386/include/pci_cfgreg.h index 721df1c..4bbfe72 100644 --- a/sys/i386/include/pci_cfgreg.h +++ b/sys/i386/include/pci_cfgreg.h @@ -51,6 +51,5 @@ int pci_cfgregopen(void); u_int32_t pci_cfgregread(int bus, int slot, int func, int reg, int bytes); void pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes); void pci_pir_open(void); -void pci_pir_parse(void); int pci_pir_probe(int bus, int require_parse); int pci_pir_route_interrupt(int bus, int device, int func, int pin); diff --git a/sys/i386/pci/pci_bus.c b/sys/i386/pci/pci_bus.c index 6f2fc0c..2460159 100644 --- a/sys/i386/pci/pci_bus.c +++ b/sys/i386/pci/pci_bus.c @@ -424,6 +424,7 @@ legacy_pcib_probe(device_t dev) static int legacy_pcib_attach(device_t dev) { + device_t pir; int bus; /* @@ -431,8 +432,11 @@ legacy_pcib_attach(device_t dev) * our method of routing interrupts if we have one. */ bus = pcib_get_bus(dev); - if (pci_pir_probe(bus, 0)) - pci_pir_parse(); + if (pci_pir_probe(bus, 0)) { + pir = BUS_ADD_CHILD(device_get_parent(dev), 0, "pir", 0); + KASSERT(pir != NULL, ("could not add pir0 device")); + device_probe_and_attach(pir); + } device_add_child(dev, "pci", bus); return bus_generic_attach(dev); } diff --git a/sys/i386/pci/pci_pir.c b/sys/i386/pci/pci_pir.c index f1f80cd..4cf28cc 100644 --- a/sys/i386/pci/pci_pir.c +++ b/sys/i386/pci/pci_pir.c @@ -67,6 +67,13 @@ struct pci_link_lookup { int pin; }; +struct pci_dev_lookup { + uint8_t link; + int bus; + int device; + int pin; +}; + typedef void pir_entry_handler(struct PIR_entry *entry, struct PIR_intpin* intpin, void *arg); @@ -82,6 +89,7 @@ static void pci_pir_find_link_handler(struct PIR_entry *entry, struct PIR_intpin *intpin, void *arg); static void pci_pir_initial_irqs(struct PIR_entry *entry, struct PIR_intpin *intpin, void *arg); +static void pci_pir_parse(void); static void pci_pir_print_intpin(struct PIR_entry *entry, struct PIR_intpin *intpin, void *arg); static void pci_pir_print_table(void); @@ -92,6 +100,7 @@ static void pci_pir_walk_table(pir_entry_handler *handler, void *arg); MALLOC_DEFINE(M_PIR, "$PIR", "$PIR structures"); static struct PIR_table *pci_route_table; +static device_t pir_device; static int pci_route_count, pir_bios_irqs, pir_parsed; static TAILQ_HEAD(, pci_link) pci_links; static int pir_interrupt_weight[NUM_ISA_INTERRUPTS]; @@ -156,10 +165,11 @@ pci_pir_open(void) pci_route_count = (pt->pt_header.ph_length - sizeof(struct PIR_header)) / sizeof(struct PIR_entry); - printf("Found $PIR table, %d entries at %p\n", - pci_route_count, pci_route_table); - if (bootverbose) + if (bootverbose) { + printf("Found $PIR table, %d entries at %p\n", + pci_route_count, pci_route_table); pci_pir_print_table(); + } } /* @@ -336,7 +346,7 @@ pci_pir_initial_irqs(struct PIR_entry *entry, struct PIR_intpin *intpin, * various interrupt routers as they could read the initial IRQ for each * link. */ -void +static void pci_pir_parse(void) { char tunable_buffer[64]; @@ -388,6 +398,7 @@ pci_pir_parse(void) irq = PCI_INVALID_IRQ; if (irq == PCI_INVALID_IRQ || pci_pir_valid_irq(pci_link, irq)) { + pci_link->pl_routed = 0; pci_link->pl_irq = irq; i = 1; } @@ -506,6 +517,11 @@ pci_pir_route_interrupt(int bus, int device, int func, int pin) return (PCI_INVALID_IRQ); } pci_link->pl_routed = 1; + + /* Ensure the interrupt is set to level/low trigger. */ + KASSERT(pir_device != NULL, ("missing pir device")); + BUS_CONFIG_INTR(pir_device, pci_link->pl_irq, + INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW); } printf("$PIR: %d:%d INT%c routed to irq %d\n", bus, device, pin - 1 + 'A', pci_link->pl_irq); @@ -603,9 +619,10 @@ pci_pir_dump_links(void) { struct pci_link *pci_link; - printf("Link IRQ Ref IRQs\n"); + printf("Link IRQ Rtd Ref IRQs\n"); TAILQ_FOREACH(pci_link, &pci_links, pl_links) { - printf("%#4x %3d %3d ", pci_link->pl_id, pci_link->pl_irq, + printf("%#4x %3d %c %3d ", pci_link->pl_id, + pci_link->pl_irq, pci_link->pl_routed ? 'Y' : 'N', pci_link->pl_references); pci_print_irqmask(pci_link->pl_irqmask); printf("\n"); @@ -630,3 +647,99 @@ pci_pir_probe(int bus, int require_parse) return (1); return (0); } + +/* + * The driver for the new-bus psuedo device pir0 for the $PIR table. + */ + +static int +pir_probe(device_t dev) +{ + char buf[64]; + + snprintf(buf, sizeof(buf), "PCI Interrupt Routing Table: %d Entries", + pci_route_count); + device_set_desc_copy(dev, buf); + return (0); +} + +static int +pir_attach(device_t dev) +{ + + pci_pir_parse(); + KASSERT(pir_device == NULL, ("Multiple pir devices")); + pir_device = dev; + return (0); +} + +static void +pir_resume_find_device(struct PIR_entry *entry, struct PIR_intpin *intpin, + void *arg) +{ + struct pci_dev_lookup *pd; + + pd = (struct pci_dev_lookup *)arg; + if (intpin->link != pd->link || pd->bus != -1) + return; + pd->bus = entry->pe_bus; + pd->device = entry->pe_device; + pd->pin = intpin - entry->pe_intpin; +} + +static int +pir_resume(device_t dev) +{ + struct pci_dev_lookup pd; + struct pci_link *pci_link; + int error; + + /* Ask the BIOS to re-route each link that was already routed. */ + TAILQ_FOREACH(pci_link, &pci_links, pl_links) { + if (!PCI_INTERRUPT_VALID(pci_link->pl_irq)) { + KASSERT(!pci_link->pl_routed, + ("link %#x is routed but has invalid PCI IRQ", + pci_link->pl_id)); + continue; + } + if (pci_link->pl_routed) { + pd.bus = -1; + pd.link = pci_link->pl_id; + pci_pir_walk_table(pir_resume_find_device, &pd); + KASSERT(pd.bus != -1, + ("did not find matching entry for link %#x in the $PIR table", + pci_link->pl_id)); + if (bootverbose) + device_printf(dev, + "Using %d.%d.INT%c to route link %#x to IRQ %d\n", + pd.bus, pd.device, pd.pin + 'A', + pci_link->pl_id, pci_link->pl_irq); + error = pci_pir_biosroute(pd.bus, pd.device, 0, pd.pin, + pci_link->pl_irq); + if (error) + device_printf(dev, + "ROUTE_INTERRUPT on resume for link %#x failed.\n", + pci_link->pl_id); + } + } + return (0); +} + +static device_method_t pir_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pir_probe), + DEVMETHOD(device_attach, pir_attach), + DEVMETHOD(device_resume, pir_resume), + + { 0, 0 } +}; + +static driver_t pir_driver = { + "pir", + pir_methods, + 1, +}; + +static devclass_t pir_devclass; + +DRIVER_MODULE(pir, legacy, pir_driver, pir_devclass, 0, 0); |