summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2007-05-21 22:10:46 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2007-05-21 22:10:46 +0000
commit39b73c48db0b5f8169f56b2d9f66f105f7330089 (patch)
treec0517bc9c60ca58c893ac6ec7b5b233782c7d1e5
parent9d903cf258c7152aeb91ccb923233df9c9017c86 (diff)
Implement deep interrupt swizzling by mapping all four PCI interrupt pins
for PCI-PCI bridges and passing the mapping to the attached bus device. MD code can use these when mapping PCI device interrupts. This diff adds such code for amd64 and i386. This fixes interrupt mapping for devices that sit behind two PCI-PCI bridges where the firmware only provides a mapping for the first PCI-PCI bridge. tested by sturm@, krw@, and a few others, ok deraadt@
-rw-r--r--sys/arch/amd64/pci/pci_machdep.c34
-rw-r--r--sys/arch/i386/pci/pci_machdep.c48
-rw-r--r--sys/dev/pci/pci.c4
-rw-r--r--sys/dev/pci/pcivar.h5
-rw-r--r--sys/dev/pci/ppb.c11
5 files changed, 54 insertions, 48 deletions
diff --git a/sys/arch/amd64/pci/pci_machdep.c b/sys/arch/amd64/pci/pci_machdep.c
index 5d8d9c322bb..bf49a9952e2 100644
--- a/sys/arch/amd64/pci/pci_machdep.c
+++ b/sys/arch/amd64/pci/pci_machdep.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pci_machdep.c,v 1.11 2007/01/15 23:19:05 jsg Exp $ */
+/* $OpenBSD: pci_machdep.c,v 1.12 2007/05/21 22:10:45 kettenis Exp $ */
/* $NetBSD: pci_machdep.c,v 1.3 2003/05/07 21:33:58 fvdl Exp $ */
/*-
@@ -101,6 +101,7 @@
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcidevs.h>
+#include <dev/pci/ppbreg.h>
#include "ioapic.h"
@@ -430,12 +431,9 @@ pci_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
return 0;
}
if (pa->pa_bridgetag) {
- int bridgebus, bridgedev;
-
- pci_decompose_tag(pc, *pa->pa_bridgetag,
- &bridgebus, &bridgedev, NULL);
- mppin = (bridgedev << 2)|((rawpin + dev - 1) & 0x3);
- if (intr_find_mpmapping(bridgebus, mppin, ihp) == 0) {
+ int pin = PPB_INTERRUPT_SWIZZLE(rawpin, dev);
+ if (pa->pa_bridgeih[pin - 1] != -1) {
+ *ihp = pa->pa_bridgeih[pin - 1];
*ihp |= line;
return 0;
}
@@ -461,20 +459,18 @@ pci_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
* that the BIOS did its job, we also recognize that as meaning that
* the BIOS has not configured the device.
*/
- if (line == 0 || line == X86_PCI_INTERRUPT_LINE_NO_CONNECTION) {
- printf("pci_intr_map: no mapping for pin %c (line=%02x)\n",
- '@' + pin, line);
+ if (line == 0 || line == X86_PCI_INTERRUPT_LINE_NO_CONNECTION)
goto bad;
- } else {
- if (line >= NUM_LEGACY_IRQS) {
- printf("pci_intr_map: bad interrupt line %d\n", line);
- goto bad;
- }
- if (line == 2) {
- printf("pci_intr_map: changed line 2 to line 9\n");
- line = 9;
- }
+
+ if (line >= NUM_LEGACY_IRQS) {
+ printf("pci_intr_map: bad interrupt line %d\n", line);
+ goto bad;
+ }
+ if (line == 2) {
+ printf("pci_intr_map: changed line 2 to line 9\n");
+ line = 9;
}
+
#if NIOAPIC > 0
if (mp_busses != NULL) {
if (intr_find_mpmapping(mp_isa_bus->mb_idx, line, ihp) == 0) {
diff --git a/sys/arch/i386/pci/pci_machdep.c b/sys/arch/i386/pci/pci_machdep.c
index 6353b135729..4627e6bb880 100644
--- a/sys/arch/i386/pci/pci_machdep.c
+++ b/sys/arch/i386/pci/pci_machdep.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pci_machdep.c,v 1.38 2007/02/20 21:15:01 tom Exp $ */
+/* $OpenBSD: pci_machdep.c,v 1.39 2007/05/21 22:10:45 kettenis Exp $ */
/* $NetBSD: pci_machdep.c,v 1.28 1997/06/06 23:29:17 thorpej Exp $ */
/*-
@@ -104,6 +104,7 @@ extern bios_pciinfo_t *bios_pciinfo;
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcidevs.h>
+#include <dev/pci/ppbreg.h>
#include "ioapic.h"
@@ -409,7 +410,10 @@ not2:
int
pci_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
{
+ int pin = pa->pa_intrpin;
+ int line = pa->pa_intrline;
#if NIOAPIC > 0
+ int rawpin = pa->pa_rawintrpin;
struct mp_intr_map *mip;
int bus, dev, func;
#endif
@@ -418,15 +422,13 @@ pci_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
pci_chipset_tag_t pc = pa->pa_pc;
pcitag_t intrtag = pa->pa_intrtag;
#endif
- int pin = pa->pa_intrpin;
- int line = pa->pa_intrline;
if (pin == 0) {
/* No IRQ used. */
goto bad;
}
- if (pin > 4) {
+ if (pin > PCI_INTERRUPT_PIN_MAX) {
printf("pci_intr_map: bad interrupt pin %d\n", pin);
goto bad;
}
@@ -451,18 +453,13 @@ pci_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
return 0;
}
}
- if (mip == NULL && pa->pa_bridgetag) {
- int bridgebus, bridgedev;
- pci_decompose_tag(pc, *pa->pa_bridgetag,
- &bridgebus, &bridgedev, NULL);
- mpspec_pin = (bridgedev << 2)|((pin + dev - 1) & 0x3);
- for (mip = mp_busses[bridgebus].mb_intrs; mip != NULL;
- mip = mip->next) {
- if (mip->bus_pin == mpspec_pin) {
- ihp->line = mip->ioapic_ih | line;
- return 0;
- }
+ if (pa->pa_bridgetag) {
+ int pin = PPB_INTERRUPT_SWIZZLE(rawpin, dev);
+ if (pa->pa_bridgeih[pin - 1].line != -1) {
+ ihp->line = pa->pa_bridgeih[pin - 1].line;
+ ihp->line |= line;
+ return 0;
}
}
/*
@@ -491,19 +488,18 @@ pci_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
* that the BIOS did its job, we also recognize that as meaning that
* the BIOS has not configured the device.
*/
- if (line == 0 || line == 255) {
- printf("pci_intr_map: no mapping for pin %c\n", '@' + pin);
+ if (line == 0 || line == I386_PCI_INTERRUPT_LINE_NO_CONNECTION)
goto bad;
- } else {
- if (line >= ICU_LEN) {
- printf("pci_intr_map: bad interrupt line %d\n", line);
- goto bad;
- }
- if (line == 2) {
- printf("pci_intr_map: changed line 2 to line 9\n");
- line = 9;
- }
+
+ if (line >= ICU_LEN) {
+ printf("pci_intr_map: bad interrupt line %d\n", line);
+ goto bad;
+ }
+ if (line == 2) {
+ printf("pci_intr_map: changed line 2 to line 9\n");
+ line = 9;
}
+
#if NIOAPIC > 0
if (!(ihp->line & PCI_INT_VIA_ISA) && mp_busses != NULL) {
if (mip == NULL && mp_isa_bus) {
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index f9f62a68882..fa79de1ffcc 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pci.c,v 1.49 2006/12/14 17:36:12 kettenis Exp $ */
+/* $OpenBSD: pci.c,v 1.50 2007/05/21 22:10:45 kettenis Exp $ */
/* $NetBSD: pci.c,v 1.31 1997/06/06 23:48:04 thorpej Exp $ */
/*
@@ -155,6 +155,7 @@ pciattach(struct device *parent, struct device *self, void *aux)
sc->sc_domain = pba->pba_domain;
sc->sc_bus = pba->pba_bus;
sc->sc_bridgetag = pba->pba_bridgetag;
+ sc->sc_bridgeih = pba->pba_bridgeih;
sc->sc_maxndevs = pci_bus_maxdevs(pba->pba_pc, pba->pba_bus);
sc->sc_intrswiz = pba->pba_intrswiz;
sc->sc_intrtag = pba->pba_intrtag;
@@ -276,6 +277,7 @@ pci_probe_device(struct pci_softc *sc, pcitag_t tag,
pa.pa_id = id;
pa.pa_class = class;
pa.pa_bridgetag = sc->sc_bridgetag;
+ pa.pa_bridgeih = sc->sc_bridgeih;
/* This is a simplification of the NetBSD code.
We don't support turning off I/O or memory
diff --git a/sys/dev/pci/pcivar.h b/sys/dev/pci/pcivar.h
index 13c1884cd52..667d73859d7 100644
--- a/sys/dev/pci/pcivar.h
+++ b/sys/dev/pci/pcivar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: pcivar.h,v 1.52 2007/02/23 21:34:32 deraadt Exp $ */
+/* $OpenBSD: pcivar.h,v 1.53 2007/05/21 22:10:45 kettenis Exp $ */
/* $NetBSD: pcivar.h,v 1.23 1997/06/06 23:48:05 thorpej Exp $ */
/*
@@ -99,6 +99,7 @@ struct pcibus_attach_args {
* parent bridge, then we assume we are a root bus.
*/
pcitag_t *pba_bridgetag;
+ pci_intr_handle_t *pba_bridgeih;
/*
* Interrupt swizzling information. These fields
@@ -126,6 +127,7 @@ struct pci_attach_args {
pcireg_t pa_id, pa_class;
pcitag_t *pa_bridgetag;
+ pci_intr_handle_t *pa_bridgeih;
/*
* Interrupt information.
@@ -174,6 +176,7 @@ struct pci_softc {
LIST_HEAD(, pci_dev) sc_devs;
int sc_domain, sc_bus, sc_maxndevs;
pcitag_t *sc_bridgetag;
+ pci_intr_handle_t *sc_bridgeih;
u_int sc_intrswiz;
pcitag_t sc_intrtag;
};
diff --git a/sys/dev/pci/ppb.c b/sys/dev/pci/ppb.c
index df391182b74..2a9361785dc 100644
--- a/sys/dev/pci/ppb.c
+++ b/sys/dev/pci/ppb.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ppb.c,v 1.18 2007/02/13 18:35:32 tom Exp $ */
+/* $OpenBSD: ppb.c,v 1.19 2007/05/21 22:10:45 kettenis Exp $ */
/* $NetBSD: ppb.c,v 1.16 1997/06/06 23:48:05 thorpej Exp $ */
/*
@@ -45,6 +45,7 @@ struct ppb_softc {
struct device sc_dev; /* generic device glue */
pci_chipset_tag_t sc_pc; /* our PCI chipset... */
pcitag_t sc_tag; /* ...and tag. */
+ pci_intr_handle_t sc_ih[4];
};
int ppbmatch(struct device *, void *, void *);
@@ -91,6 +92,7 @@ ppbattach(struct device *parent, struct device *self, void *aux)
pci_chipset_tag_t pc = pa->pa_pc;
struct pcibus_attach_args pba;
pcireg_t busdata;
+ int pin;
printf("\n");
@@ -105,6 +107,12 @@ ppbattach(struct device *parent, struct device *self, void *aux)
return;
}
+ for (pin = PCI_INTERRUPT_PIN_A; pin <= PCI_INTERRUPT_PIN_D; pin++) {
+ pa->pa_intrpin = pa->pa_rawintrpin = pin;
+ pa->pa_intrline = 0;
+ pci_intr_map(pa, &sc->sc_ih[pin - PCI_INTERRUPT_PIN_A]);
+ }
+
#if 0
/*
* XXX can't do this, because we're not given our bus number
@@ -133,6 +141,7 @@ ppbattach(struct device *parent, struct device *self, void *aux)
#endif
pba.pba_domain = pa->pa_domain;
pba.pba_bus = PPB_BUSINFO_SECONDARY(busdata);
+ pba.pba_bridgeih = sc->sc_ih;
pba.pba_bridgetag = &sc->sc_tag;
pba.pba_intrswiz = pa->pa_intrswiz;
pba.pba_intrtag = pa->pa_intrtag;