summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;
}
/*