summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2005-09-29 15:04:41 +0000
committerjhb <jhb@FreeBSD.org>2005-09-29 15:04:41 +0000
commit6893ea82358f5b6ed25bbd8409aace57c7dbb520 (patch)
tree4066d05ea0a2e2ca592584355f14341354e4da57 /sys/dev
parent340c88b8b4cd9fd45849f803f215c044a0e8c294 (diff)
downloadFreeBSD-src-6893ea82358f5b6ed25bbd8409aace57c7dbb520.zip
FreeBSD-src-6893ea82358f5b6ed25bbd8409aace57c7dbb520.tar.gz
- Consolidate duplicated code for assigning interrupts to PCI devices via
routing, etc. in a static pci_assign_interrupt() function. - Add a sledgehammer that allows the user to override the interrupt assignment of any PCI device via a tunable (e.g. "hw.pci0.7.INTB=5" would force any functions on the pci device in slot 7 of bus 0 that use B# to use IRQ 5). This should be used with great caution! Generally, if the interrupt routing in use provides specific tunables (such as hard-wiring the IRQ for a given $PIR or ACPI PCI link device), then those should be used instead. One instance where this tunable might be useful is if a box has an MPTable with duplicate entries for the same PCI device with different IRQs. MFC after: 1 week
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/pci/pci.c72
1 files changed, 55 insertions, 17 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index af40ca8..f8122ba 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -76,6 +76,8 @@ static void pci_fixancient(pcicfgregs *cfg);
static int pci_porten(device_t pcib, int b, int s, int f);
static int pci_memen(device_t pcib, int b, int s, int f);
+static void pci_assign_interrupt(device_t bus, device_t dev,
+ int force_route);
static int pci_add_map(device_t pcib, device_t bus, device_t dev,
int b, int s, int f, int reg,
struct resource_list *rl);
@@ -937,13 +939,60 @@ pci_ata_maps(device_t pcib, device_t bus, device_t dev, int b,
}
static void
+pci_assign_interrupt(device_t bus, device_t dev, int force_route)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ pcicfgregs *cfg = &dinfo->cfg;
+ char tunable_name[64];
+ int irq;
+
+ /* Has to have an intpin to have an interrupt. */
+ if (cfg->intpin == 0)
+ return;
+
+ /* Let the user override the IRQ with a tunable. */
+ irq = PCI_INVALID_IRQ;
+ snprintf(tunable_name, sizeof(tunable_name), "hw.pci%d.%d.INT%c.irq",
+ cfg->bus, cfg->slot, cfg->intpin + 'A' - 1);
+ if (TUNABLE_INT_FETCH(tunable_name, &irq) && (irq >= 255 || irq <= 0))
+ irq = PCI_INVALID_IRQ;
+
+ /*
+ * If we didn't get an IRQ via the tunable, then we either use the
+ * IRQ value in the intline register or we ask the bus to route an
+ * interrupt for us. If force_route is true, then we only use the
+ * value in the intline register if the bus was unable to assign an
+ * IRQ.
+ */
+ if (!PCI_INTERRUPT_VALID(irq)) {
+ if (!PCI_INTERRUPT_VALID(cfg->intline) || force_route)
+ irq = PCI_ASSIGN_INTERRUPT(bus, dev);
+ if (!PCI_INTERRUPT_VALID(irq))
+ irq = cfg->intline;
+ }
+
+ /* If after all that we don't have an IRQ, just bail. */
+ if (!PCI_INTERRUPT_VALID(irq))
+ return;
+
+ /* Update the config register if it changed. */
+ if (irq != cfg->intline) {
+ cfg->intline = irq;
+ pci_write_config(dev, PCIR_INTLINE, irq, 1);
+ }
+
+ /* Add this IRQ as rid 0 interrupt resource. */
+ resource_list_add(&dinfo->resources, SYS_RES_IRQ, 0, irq, irq, 1);
+}
+
+static void
pci_add_resources(device_t pcib, device_t bus, device_t dev)
{
struct pci_devinfo *dinfo = device_get_ivars(dev);
pcicfgregs *cfg = &dinfo->cfg;
struct resource_list *rl = &dinfo->resources;
struct pci_quirk *q;
- int b, i, irq, f, s;
+ int b, i, f, s;
b = cfg->bus;
s = cfg->slot;
@@ -974,14 +1023,10 @@ pci_add_resources(device_t pcib, device_t bus, device_t dev)
* If the re-route fails, then just stick with what we
* have.
*/
- irq = PCI_ASSIGN_INTERRUPT(bus, dev);
- if (PCI_INTERRUPT_VALID(irq)) {
- pci_write_config(dev, PCIR_INTLINE, irq, 1);
- cfg->intline = irq;
- } else
+ pci_assign_interrupt(bus, dev, 1);
+#else
+ pci_assign_interrupt(bus, dev, 0);
#endif
- irq = cfg->intline;
- resource_list_add(rl, SYS_RES_IRQ, 0, irq, irq, 1);
}
}
@@ -1737,15 +1782,8 @@ pci_alloc_resource(device_t dev, device_t child, int type, int *rid,
* interrupt, try to assign it one.
*/
if (!PCI_INTERRUPT_VALID(cfg->intline) &&
- (cfg->intpin != 0)) {
- cfg->intline = PCI_ASSIGN_INTERRUPT(dev, child);
- if (PCI_INTERRUPT_VALID(cfg->intline)) {
- pci_write_config(child, PCIR_INTLINE,
- cfg->intline, 1);
- resource_list_add(rl, SYS_RES_IRQ, 0,
- cfg->intline, cfg->intline, 1);
- }
- }
+ (cfg->intpin != 0))
+ pci_assign_interrupt(dev, child, 0);
break;
case SYS_RES_IOPORT:
case SYS_RES_MEMORY:
OpenPOWER on IntegriCloud