summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiod Vallat <miod@cvs.openbsd.org>2009-05-27 19:04:48 +0000
committerMiod Vallat <miod@cvs.openbsd.org>2009-05-27 19:04:48 +0000
commitfdfd6841927b99237cc814cfc3f85745ad56bffa (patch)
tree67063e5dec635d45ff52161362906d405fad9ee2
parent142f545264089ad6a493f097b3c2eefacd6e357c (diff)
Yet another attempt at a more reliable detection of the second interrupt
used by onboard IOC chips, by forcing the IOC to trigger this interrupt, and some help from the PCI bridge driver to report which interrupt has fired through a fake PCI configuration register. This works nicely on IP27 and IP35, but on IP30 the interrupt doesn't happen, for some reason; so keep the existing heuristic in case the above trick did not give us a valid interrupt number. In case we got an interrupt, this will also detect IOC configurations where there is actually one interrupt, should such configurations exist. <rant style="beck"> I probably deserve to rot in hell for this abomination, but I won't mind as long as the IOC designers who came with the bright ``let's use more than one interrupt and defecate on the pci spec'' ideas are there, too. </rant>
-rw-r--r--sys/arch/sgi/pci/ioc.c113
-rw-r--r--sys/arch/sgi/xbow/xbridge.c64
2 files changed, 135 insertions, 42 deletions
diff --git a/sys/arch/sgi/pci/ioc.c b/sys/arch/sgi/pci/ioc.c
index f3610a8ed97..f8c3574cb46 100644
--- a/sys/arch/sgi/pci/ioc.c
+++ b/sys/arch/sgi/pci/ioc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ioc.c,v 1.15 2009/05/22 21:21:19 miod Exp $ */
+/* $OpenBSD: ioc.c,v 1.16 2009/05/27 19:04:45 miod Exp $ */
/*
* Copyright (c) 2008 Joel Sing.
@@ -110,7 +110,7 @@ ioc_match(struct device *parent, void *match, void *aux)
struct pci_attach_args *pa = aux;
if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_SGI &&
- PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_SGI_IOC3)
+ PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_SGI_IOC3)
return (1);
return (0);
@@ -266,9 +266,9 @@ unknown:
* it actually needs to use two interrupts, one for the superio
* chip, and the other for the Ethernet chip.
*
- * Since our pci layer doesn't handle this, we cheat and compute
- * the superio interrupt cookie ourselves. This is ugly, and
- * depends on xbridge knowledge.
+ * Since our pci layer doesn't handle this, we have to compute
+ * the superio interrupt cookie ourselves, with the help of the
+ * pci bridge driver. This is ugly, and depends on xbridge knowledge.
*
* (What the above means is that you should wear peril-sensitive
* sunglasses from now on).
@@ -288,41 +288,77 @@ unknown:
/*
* The second vector source seems to be the first unused PCI
* slot.
- *
- * On Octane systems, the on-board IOC3 is device #2 and
- * immediately followed by the RAD1 audio, device #3, thus
- * the next empty slot is #4.
- * XXX Is this still true with the Octane PCI cardcage?
- *
- * On Origin200 systems, there is no RAD1 audio, slot #3 is
- * empty (available PCI slots are #5-#7).
- *
- * On Fuel systems, the on-board IOC3 is device #4,
- * with the USB controller being device #5, and slot #6
- * is empty (available PCI slots are on a different bridge).
*/
if (dual_irq) {
+ /*
+ * First, try to get reliable interrupt information
+ * from the pci bridge.
+ *
+ * In order to do this, we trigger the superio interrupt
+ * and read a magic register in configuration space.
+ * See the xbridge code for details.
+ */
+
+ /* enable TX empty interrupt */
+ delay(20 * 1000); /* let console output drain */
+ bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IEC, ~0x0);
+ bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IES,
+ 0x0201);
+
+ /* read pseudo interrupt register */
+ data = pci_conf_read(pa->pa_pc, pa->pa_tag,
+ PCI_INTERRUPT_REG + 4);
+
+ /* disable interrupt again */
+ bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IEC,
+ 0x0201);
+
+ if (data != 0) {
+ /*
+ * If the interrupt has been received as the
+ * regular PCI interrupt, then we probably don't
+ * have separate interrupts.
+ */
+ if (PCI_INTERRUPT_LINE(data) == pa->pa_intrline)
+ goto single;
+
+ pa->pa_intrline = PCI_INTERRUPT_LINE(data);
+ pa->pa_rawintrpin = pa->pa_intrpin =
+ PCI_INTERRUPT_PIN(data);
+
+ if (pci_intr_map(pa, &ih2) == 0)
+ goto establish;
+ }
+
+ /*
+ * If there was no bridge hint or it did not work, use the
+ * heuristic of picking the lowest free slot.
+ */
+
for (dev = 0;
dev < pci_bus_maxdevs(pa->pa_pc, pa->pa_bus); dev++) {
pcitag_t tag;
+ int line, rc;
if (dev == pa->pa_device)
continue;
tag = pci_make_tag(pa->pa_pc, pa->pa_bus, dev, 0);
- if (pci_conf_read(pa->pa_pc, tag, PCI_ID_REG) ==
- 0xffffffff) {
- pa->pa_tag = tag;
- if (pci_intr_map(pa, &ih2) != 0) {
- printf(": failed to map superio"
- " interrupt!\n");
- goto unmap;
- }
- pa->pa_tag = pci_make_tag(pa->pa_pc, pa->pa_bus,
- pa->pa_device, pa->pa_function);
-
- goto establish;
+ if (pci_conf_read(pa->pa_pc, tag, PCI_ID_REG) !=
+ 0xffffffff)
+ continue; /* slot in use... */
+
+ line = pa->pa_intrline;
+ pa->pa_intrline = dev;
+ rc = pci_intr_map(pa, &ih2);
+ pa->pa_intrline = line;
+
+ if (rc != 0) {
+ printf(": failed to map superio interrupt!\n");
+ goto unmap;
}
+
+ goto establish;
}
/*
@@ -331,6 +367,7 @@ unknown:
* situation, but it's probably safe to revert to
* a shared, single interrupt.
*/
+single:
shared_handler = 1;
dual_irq = 0;
}
@@ -340,10 +377,12 @@ establish:
/*
* Register the second (superio) interrupt.
*/
+
sc->sc_ih2 = pci_intr_establish(sc->sc_pc, ih2, IPL_TTY,
ioc_intr_superio, sc, self->dv_xname);
if (sc->sc_ih2 == NULL) {
- printf("failed to establish superio interrupt!\n");
+ printf("failed to establish superio interrupt at %s\n",
+ pci_intr_string(sc->sc_pc, ih2));
goto unmap;
}
@@ -354,6 +393,7 @@ establish:
* Register the main (Ethernet if available, superio otherwise)
* interrupt.
*/
+
sc->sc_ih1 = pci_intr_establish(sc->sc_pc, ih1, IPL_NET,
shared_handler ? ioc_intr_shared : ioc_intr_ethernet,
sc, self->dv_xname);
@@ -372,6 +412,7 @@ establish:
/*
* Acknowledge all pending interrupts, and disable them.
*/
+
bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IEC, ~0x0);
bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IES, 0x0);
bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IR,
@@ -388,7 +429,7 @@ establish:
if (has_ps2)
ioc_attach_child(self, "iockbc", 0, IOCDEV_KEYBOARD);
if (has_ethernet)
- ioc_attach_child(self, "ef", IOC3_EF_BASE, IOCDEV_EF);
+ ioc_attach_child(self, "iec", IOC3_EF_BASE, IOCDEV_EF);
/* XXX what about the parallel port? */
ioc_attach_child(self, "dsrtc", 0, IOCDEV_RTC);
@@ -443,7 +484,7 @@ ioc_search_mundane(struct device *parent, void *vcf, void *args)
if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != (int)iaa->iaa_base)
return 0;
- return (*cf->cf_attach->ca_match)(parent, cf, iaa);
+ return (*cf->cf_attach->ca_match)(parent, cf, iaa);
}
/*
@@ -479,8 +520,8 @@ ioc_search_onewire(struct device *parent, void *vcf, void *args)
oba.oba_flags = ONEWIRE_SCAN_NOW | ONEWIRE_NO_PERIODIC_SCAN;
/* In case onewire is disabled in UKC... */
- if ((*cf->cf_attach->ca_match)(parent, cf, &oba) == 0)
- return 0;
+ if ((*cf->cf_attach->ca_match)(parent, cf, &oba) == 0)
+ return 0;
owdev = config_attach(parent, cf, &oba, onewirebus_print);
@@ -626,7 +667,7 @@ void *
ioc_intr_establish(void *cookie, u_long dev, int level, int (*func)(void *),
void *arg, char *name)
{
- struct ioc_softc *sc = cookie;
+ struct ioc_softc *sc = cookie;
struct ioc_intr *ii;
if (dev < 0 || dev >= IOC_NDEVS)
@@ -707,7 +748,7 @@ ioc_intr_dispatch(struct ioc_softc *sc, int dev)
if ((ii = sc->sc_intr[dev]) != NULL && ii->ii_func != NULL) {
rc = (*ii->ii_func)(ii->ii_arg);
if (rc != 0)
- ii->ii_count.ec_count++;
+ ii->ii_count.ec_count++;
}
return rc;
diff --git a/sys/arch/sgi/xbow/xbridge.c b/sys/arch/sgi/xbow/xbridge.c
index 570d11c27cf..4d1191024e1 100644
--- a/sys/arch/sgi/xbow/xbridge.c
+++ b/sys/arch/sgi/xbow/xbridge.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: xbridge.c,v 1.22 2009/05/27 18:58:52 miod Exp $ */
+/* $OpenBSD: xbridge.c,v 1.23 2009/05/27 19:04:47 miod Exp $ */
/*
* Copyright (c) 2008, 2009 Miodrag Vallat.
@@ -80,6 +80,7 @@ struct xbridge_softc {
struct xbridge_intr *sc_intr[BRIDGE_NINTRS];
pcireg_t sc_devices[BRIDGE_NSLOTS];
+ pcireg_t sc_ier_ignore;
struct mutex sc_atemtx;
uint sc_atecnt;
@@ -411,6 +412,7 @@ xbridge_conf_read(void *cookie, pcitag_t tag, int offset)
{
struct xbridge_softc *sc = cookie;
pcireg_t data;
+ uint32_t ier;
int bus, dev, fn;
paddr_t pa;
int skip;
@@ -453,7 +455,44 @@ xbridge_conf_read(void *cookie, pcitag_t tag, int offset)
break;
case PCI_INTERRUPT_REG:
/* This register is not implemented. Fake it. */
- data = PCI_INTERRUPT_PIN_A << PCI_INTERRUPT_PIN_SHIFT;
+ data = (PCI_INTERRUPT_PIN_A <<
+ PCI_INTERRUPT_PIN_SHIFT) |
+ (dev << PCI_INTERRUPT_LINE_SHIFT);
+ skip = 1;
+ break;
+ case PCI_INTERRUPT_REG + 4:
+ /*
+ * This is a kluge to help the IOC driver figure
+ * out which second interrupt line it uses.
+ *
+ * The ioc driver will attempt to trigger that
+ * interrupt, and then read that sort-of second
+ * interrupt register. Here we check the pending
+ * interrupts, and see if a bit has changed.
+ *
+ * Unfortunately, this does not work on all
+ * platforms (e.g. IP30). The ioc driver falls
+ * back to heuristics in that case.
+ */
+
+ ier = bus_space_read_4(sc->sc_iot, sc->sc_regh,
+ BRIDGE_IER) & 0xff;
+ ier &= ~sc->sc_ier_ignore;
+ sc->sc_ier_ignore |= ier;
+
+ /* we expect only one bit to trigger */
+ if (ier != 0 && (ier & (ier - 1)) != 0)
+ ier = 0;
+
+ if (ier != 0) {
+ /* compute interrupt line */
+ for (data = 0; ier != 1; ier >>= 1, data++) ;
+
+ data = (PCI_INTERRUPT_PIN_A <<
+ PCI_INTERRUPT_PIN_SHIFT) |
+ (data << PCI_INTERRUPT_LINE_SHIFT);
+ } else
+ data = 0;
skip = 1;
break;
default:
@@ -568,6 +607,7 @@ struct xbridge_intr {
int
xbridge_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
{
+ struct xbridge_softc *sc = pa->pa_pc->pc_conf_v;
int bus, device, intr;
*ihp = -1;
@@ -585,10 +625,19 @@ xbridge_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
#endif
xbridge_decompose_tag(pa->pa_pc, pa->pa_tag, &bus, &device, NULL);
- if (pa->pa_intrpin & 1)
- intr = device;
- else
- intr = device ^ 4;
+
+ /*
+ * For IOC devices, the real information is in pa_intrline.
+ */
+ if (sc->sc_devices[device] ==
+ PCI_ID_CODE(PCI_VENDOR_SGI, PCI_PRODUCT_SGI_IOC3)) {
+ intr = pa->pa_intrline;
+ } else {
+ if (pa->pa_intrpin & 1)
+ intr = device;
+ else
+ intr = device ^ 4;
+ }
*ihp = XBRIDGE_INTR_HANDLE(device, intr);
@@ -1627,6 +1676,9 @@ xbridge_setup(struct xbridge_softc *sc)
for (i = 0; i < BRIDGE_NINTRS; i++)
sc->sc_intrbit[i] = -1;
+
+ sc->sc_ier_ignore = bus_space_read_4(sc->sc_iot, sc->sc_regh,
+ BRIDGE_IER) & 0xff;
}
/*