summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiod Vallat <miod@cvs.openbsd.org>2009-11-11 15:29:32 +0000
committerMiod Vallat <miod@cvs.openbsd.org>2009-11-11 15:29:32 +0000
commit0ae0bc9735bfce4e564bd3d306772e3cbd4c26f7 (patch)
treece55d45ab5acc678d99dd25a2c54cde96c7baceb
parent36b0a223470c06b4001571b920f1051b6382b65b (diff)
It turns out PCI IOC3 card which embed both the Ethernet controller and the
superio chip interrupt on two different pins (yet do not advertize themselves as a multi-function device, of course). So, on one hand, this makes the ioc attachment code simpler, because it simply needs to map interrupt pins A and B, and another hand, this moves all the interrupt knowledge to the PCI bridge driver, since routing of pin B differs whether the device is the onboard IOC3 chip (and able to use any of the 8 bridge interrupt sources...) or on a PCI board (with pin mapping sane, since controlled by the bridge). This makes superio interrupts on CADduo boards work. Tested to cause no regressions on Origin 200, Octane and Fuel.
-rw-r--r--sys/arch/sgi/pci/ioc.c224
-rw-r--r--sys/arch/sgi/xbow/xbridge.c61
2 files changed, 139 insertions, 146 deletions
diff --git a/sys/arch/sgi/pci/ioc.c b/sys/arch/sgi/pci/ioc.c
index 8a59cf2e3cd..3f7d67cee21 100644
--- a/sys/arch/sgi/pci/ioc.c
+++ b/sys/arch/sgi/pci/ioc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ioc.c,v 1.29 2009/11/09 16:57:47 miod Exp $ */
+/* $OpenBSD: ioc.c,v 1.30 2009/11/11 15:29:29 miod Exp $ */
/*
* Copyright (c) 2008 Joel Sing.
@@ -79,8 +79,8 @@ struct ioc_softc {
bus_dma_tag_t sc_dmat;
pci_chipset_tag_t sc_pc;
- void *sc_ih1; /* Ethernet interrupt */
- void *sc_ih2; /* SuperIO interrupt */
+ void *sc_ih_enet; /* Ethernet interrupt */
+ void *sc_ih_superio; /* SuperIO interrupt */
struct ioc_intr *sc_intr[IOC_NDEVS];
struct onewire_bus sc_bus;
@@ -152,14 +152,13 @@ ioc_attach(struct device *parent, struct device *self, void *aux)
{
struct ioc_softc *sc = (struct ioc_softc *)self;
struct pci_attach_args *pa = aux;
- pci_intr_handle_t ih1, ih2;
+ pci_intr_handle_t ih_enet, ih_superio;
bus_space_tag_t memt;
bus_space_handle_t memh;
bus_size_t memsize;
- uint32_t data;
- int dev;
- int dual_irq, shared_handler;
- int device_mask;
+ pcireg_t data;
+ int has_superio, has_enet, is_obio;
+ int subdevice_mask;
bus_addr_t rtcbase;
if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_MEM, 0,
@@ -223,10 +222,9 @@ ioc_attach(struct device *parent, struct device *self, void *aux)
* Now figure out what our configuration is.
*/
- printf("%s: ", self->dv_xname);
-
- dual_irq = shared_handler = 0;
- device_mask = 0;
+ has_superio = has_enet = 0;
+ is_obio = 0;
+ subdevice_mask = 0;
if (sc->sc_owserial != NULL) {
if (strncmp(sc->sc_owserial->sc_product, "030-0873-", 9) == 0) {
/*
@@ -234,34 +232,37 @@ ioc_attach(struct device *parent, struct device *self, void *aux)
* behind an xbridge. However the fourth one lacks
* the superio chip.
*/
- device_mask = (1 << IOCDEV_EF);
- if (pa->pa_device != 3)
- device_mask = (1 << IOCDEV_SERIAL_A) |
+ subdevice_mask = (1 << IOCDEV_EF);
+ has_enet = 1;
+ if (pa->pa_device != 3) {
+ subdevice_mask = (1 << IOCDEV_SERIAL_A) |
(1 << IOCDEV_SERIAL_B);
- shared_handler = 1;
+ has_superio = 1;
+ }
} else
if (strncmp(sc->sc_owserial->sc_product, "030-0891-", 9) == 0) {
/* IP30 on-board IOC3 */
- device_mask = (1 << IOCDEV_SERIAL_A) |
+ subdevice_mask = (1 << IOCDEV_SERIAL_A) |
(1 << IOCDEV_SERIAL_B) | (1 << IOCDEV_LPT) |
(1 << IOCDEV_KBC) | (1 << IOCDEV_RTC) |
(1 << IOCDEV_EF);
rtcbase = IOC3_BYTEBUS_1;
- dual_irq = 1;
+ has_superio = has_enet = 1;
+ is_obio = 1;
} else
if (strncmp(sc->sc_owserial->sc_product, "030-1155-", 9) == 0) {
/* CADDuo board */
- device_mask = (1 << IOCDEV_KBC) | (1 << IOCDEV_EF);
- shared_handler = 1;
+ subdevice_mask = (1 << IOCDEV_KBC) | (1 << IOCDEV_EF);
+ has_superio = has_enet = 1;
} else
if (strncmp(sc->sc_owserial->sc_product, "030-1657-", 9) == 0 ||
strncmp(sc->sc_owserial->sc_product, "030-1664-", 9) == 0) {
/* PCI_SIO_UFC dual serial board */
- device_mask = (1 << IOCDEV_SERIAL_A) |
+ subdevice_mask = (1 << IOCDEV_SERIAL_A) |
(1 << IOCDEV_SERIAL_B);
- } else {
+ has_superio = 1;
+ } else
goto unknown;
- }
} else {
#ifdef TGT_ORIGIN
/*
@@ -275,7 +276,7 @@ ioc_attach(struct device *parent, struct device *self, void *aux)
!ISSET(ioc_nodemask, 1UL << currentnasid)) {
SET(ioc_nodemask, 1UL << currentnasid);
- device_mask = (1 << IOCDEV_SERIAL_A) |
+ subdevice_mask = (1 << IOCDEV_SERIAL_A) |
(1 << IOCDEV_SERIAL_B) | (1 << IOCDEV_LPT) |
(1 << IOCDEV_KBC) | (1 << IOCDEV_RTC) |
(1 << IOCDEV_EF);
@@ -288,10 +289,11 @@ ioc_attach(struct device *parent, struct device *self, void *aux)
*/
if (sys_config.system_type == SGI_IP35 &&
sys_config.system_subtype == IP35_O300)
- device_mask &= ~(1 << IOCDEV_KBC);
+ subdevice_mask &= ~(1 << IOCDEV_KBC);
rtcbase = IOC3_BYTEBUS_0;
- dual_irq = 1;
+ has_superio = has_enet = 1;
+ is_obio = 1;
} else
#endif
{
@@ -302,131 +304,99 @@ unknown:
* to figure out, but for now we'll just
* chicken out.
*/
- printf("unknown flavour\n");
+ printf("%s: unknown flavour\n", self->dv_xname);
return;
}
}
/*
+ * Acknowledge all pending interrupts, and disable them.
+ * Be careful not all registers may be wired depending on what
+ * devices are actually present.
+ */
+
+ if (has_superio) {
+ 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,
+ bus_space_read_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IR));
+ }
+ if (has_enet) {
+ bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_ENET_IER, 0);
+ }
+
+ /*
* IOC3 is not a real PCI device - it's a poor wrapper over a set
* of convenience chips. And when it is in full-blown configuration,
* 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 have to compute
- * the superio interrupt cookie ourselves, with the help of the
- * pci bridge driver.
- *
- * (What the above means is that you should wear peril-sensitive
- * sunglasses from now on).
+ * This would not be a problem if the device advertized itself
+ * as a multifunction device. But it doesn't...
*
- * To make things ever worse, some IOC3 boards (real boards, not
- * on-board components) lack the Ethernet component. We should
- * eventually handle them there, but it's not worth doing yet...
- * (and we'll need to parse the ownum serial numbers to know
- * this anyway)
+ * Fortunately, the interrupt used are simply interrupt pins A
+ * and B; so with the help of the PCI bridge driver, we can
+ * register the two interrupts and almost pretend things are
+ * as normal as they could be.
*/
- if (pci_intr_map(pa, &ih1) != 0) {
- printf("failed to map interrupt!\n");
- goto unmap;
+ if (has_enet) {
+ pa->pa_intrpin = PCI_INTERRUPT_PIN_A;
+ if (pci_intr_map(pa, &ih_enet) != 0) {
+ printf("%s: failed to map ethernet interrupt\n",
+ self->dv_xname);
+ goto unmap;
+ }
}
- /*
- * The second vector source seems to be the first unused PCI
- * slot.
- */
- if (dual_irq) {
- 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)
- 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;
- }
+ if (has_superio) {
+ if (has_enet)
+ pa->pa_intrpin =
+ is_obio ? PCI_INTERRUPT_PIN_D : PCI_INTERRUPT_PIN_B;
+ else
+ pa->pa_intrpin = PCI_INTERRUPT_PIN_A;
- goto establish;
+ if (pci_intr_map(pa, &ih_superio) != 0) {
+ printf("%s: failed to map superio interrupt\n",
+ self->dv_xname);
+ goto unmap;
}
-
- /*
- * There are no empty slots, thus we can't steal an
- * interrupt. I don't know how IOC3 behaves in this
- * situation, but it's probably safe to revert to
- * a shared, single interrupt.
- */
- shared_handler = 1;
- dual_irq = 0;
}
- if (dual_irq) {
-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 at %s\n",
- pci_intr_string(sc->sc_pc, ih2));
+ if (has_enet) {
+ sc->sc_ih_enet = pci_intr_establish(sc->sc_pc, ih_enet,
+ IPL_NET, ioc_intr_ethernet, sc, self->dv_xname);
+ if (sc->sc_ih_enet == NULL) {
+ printf("%s: failed to establish ethernet interrupt "
+ "at %s\n", self->dv_xname,
+ pci_intr_string(sc->sc_pc, ih_enet));
goto unmap;
}
-
- printf("superio %s", pci_intr_string(sc->sc_pc, ih2));
}
- /*
- * 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);
- if (sc->sc_ih1 == NULL) {
- printf("\n%s: failed to establish %sinterrupt!\n",
- self->dv_xname, dual_irq ? "ethernet " : "");
- goto unregister;
+ if (has_superio) {
+ sc->sc_ih_superio = pci_intr_establish(sc->sc_pc, ih_superio,
+ IPL_TTY, ioc_intr_superio, sc, self->dv_xname);
+ if (sc->sc_ih_superio == NULL) {
+ printf("%s: failed to establish superio interrupt "
+ "at %s\n", self->dv_xname,
+ pci_intr_string(sc->sc_pc, ih_superio));
+ goto unregister;
+ }
}
- printf("%s%s\n", dual_irq ? "; ethernet " : "",
- pci_intr_string(sc->sc_pc, ih1));
- /*
- * Acknowledge all pending interrupts, and disable them.
- * Be careful not all registers may be wired depending on what
- * devices are actually present.
- */
-
- if ((device_mask & ~(1 << IOCDEV_EF)) != 0) {
- 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,
- bus_space_read_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IR));
- }
- if ((device_mask & (1 << IOCDEV_EF)) != 0) {
- bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_ENET_IER, 0);
- }
+ if (has_enet)
+ printf("%s: ethernet %s\n",
+ self->dv_xname, pci_intr_string(sc->sc_pc, ih_enet));
+ if (has_superio)
+ printf("%s: superio %s\n",
+ self->dv_xname, pci_intr_string(sc->sc_pc, ih_superio));
/*
* Attach other sub-devices.
*/
- if (ISSET(device_mask, 1 << IOCDEV_SERIAL_A)) {
+ if (ISSET(subdevice_mask, 1 << IOCDEV_SERIAL_A)) {
/*
* Put serial ports in passthrough mode,
* to use the MI com(4) 16550 support.
@@ -442,20 +412,20 @@ establish:
ioc_attach_child(self, "com", IOC3_UARTA_BASE, IOCDEV_SERIAL_A);
ioc_attach_child(self, "com", IOC3_UARTB_BASE, IOCDEV_SERIAL_B);
}
- if (ISSET(device_mask, 1 << IOCDEV_KBC))
+ if (ISSET(subdevice_mask, 1 << IOCDEV_KBC))
ioc_attach_child(self, "iockbc", 0, IOCDEV_KBC);
- if (ISSET(device_mask, 1 << IOCDEV_EF))
+ if (ISSET(subdevice_mask, 1 << IOCDEV_EF))
ioc_attach_child(self, "iec", 0, IOCDEV_EF);
- if (ISSET(device_mask, 1 << IOCDEV_LPT))
+ if (ISSET(subdevice_mask, 1 << IOCDEV_LPT))
ioc_attach_child(self, "lpt", 0, IOCDEV_LPT);
- if (ISSET(device_mask, 1 << IOCDEV_RTC))
+ if (ISSET(subdevice_mask, 1 << IOCDEV_RTC))
ioc_attach_child(self, "dsrtc", rtcbase, IOCDEV_RTC);
return;
unregister:
- if (dual_irq)
- pci_intr_disestablish(sc->sc_pc, sc->sc_ih2);
+ if (has_enet)
+ pci_intr_disestablish(sc->sc_pc, sc->sc_ih_enet);
unmap:
bus_space_unmap(memt, memh, memsize);
}
diff --git a/sys/arch/sgi/xbow/xbridge.c b/sys/arch/sgi/xbow/xbridge.c
index ea5280915cb..e0c2cb3d662 100644
--- a/sys/arch/sgi/xbow/xbridge.c
+++ b/sys/arch/sgi/xbow/xbridge.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: xbridge.c,v 1.60 2009/11/07 18:56:55 miod Exp $ */
+/* $OpenBSD: xbridge.c,v 1.61 2009/11/11 15:29:31 miod Exp $ */
/*
* Copyright (c) 2008, 2009 Miodrag Vallat.
@@ -169,6 +169,11 @@ struct xbridge_softc {
#define DEVNAME(xb) ((xb)->xb_dev.dv_xname)
+#define PCI_ID_EMPTY PCI_ID_CODE(PCI_VENDOR_INVALID, 0xffff);
+#define SLOT_EMPTY(xb,dev) \
+ (PCI_VENDOR((xb)->xb_devices[dev].id) == PCI_VENDOR_INVALID || \
+ PCI_VENDOR((xb)->xb_devices[dev].id) == 0)
+
const struct cfattach xbridge_ca = {
sizeof(struct xbridge_softc), xbridge_match, xbridge_attach
};
@@ -781,7 +786,7 @@ xbridge_conf_write(void *cookie, pcitag_t tag, int offset, pcireg_t data)
switch (offset) {
case PCI_COMMAND_STATUS_REG:
/*
- * Some IOC models do not support having this bit
+ * Some IOC3 models do not support having this bit
* cleared (which is what pci_mapreg_probe() will
* do), so we set it unconditionnaly.
*/
@@ -870,12 +875,38 @@ xbridge_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
intr = XBRIDGE_INTR_BIT(pa->pa_bridgeih[pin - 1]);
} else {
/*
- * For IOC devices, the real information is in pa_intrline.
+ * For IOC3 devices, pin A is always the regular PCI interrupt,
+ * but wiring of interrupt pin B may vary.
+ * We rely upon ioc(4) being able to figure out whether it's
+ * an onboard chip or not, and to require interrupt pin D
+ * instead of B in the former case.
*/
+ intr = -1;
if (xb->xb_devices[device].id ==
PCI_ID_CODE(PCI_VENDOR_SGI, PCI_PRODUCT_SGI_IOC3)) {
- intr = pa->pa_intrline;
- } else {
+
+ switch (pa->pa_intrpin) {
+ case PCI_INTERRUPT_PIN_A:
+ case PCI_INTERRUPT_PIN_B:
+ break;
+ case PCI_INTERRUPT_PIN_D:
+ /*
+ * If this device is an onboard IOC3,
+ * interrupt pin B is wired as pin A of
+ * the first empty PCI slot...
+ */
+ for (intr = 0; intr < MAX_SLOTS; intr++)
+ if (SLOT_EMPTY(xb, intr))
+ break;
+ /* should not happen, but fallback anyway */
+ if (intr >= MAX_SLOTS)
+ intr = -1;
+ break;
+ default:
+ return 1;
+ }
+ }
+ if (intr < 0) {
if (pa->pa_intrpin & 1)
intr = device;
else
@@ -2099,8 +2130,7 @@ xbridge_setup(struct xbpci_softc *xb)
pa = xb->xb_regh + BRIDGE_PCI_CFG_SPACE +
(dev << 12) + PCI_ID_REG;
if (guarded_read_4(pa, &xb->xb_devices[dev].id) != 0)
- xb->xb_devices[dev].id =
- PCI_ID_CODE(PCI_VENDOR_INVALID, 0xffff);
+ xb->xb_devices[dev].id = PCI_ID_EMPTY;
}
/*
@@ -2199,8 +2229,7 @@ xbridge_rrb_setup(struct xbpci_softc *xb, int odd)
total = 0;
for (i = 0; i < nitems(rrb); i++) {
dev = (i << 1) + !!odd;
- if (dev >= xb->xb_nslots ||
- PCI_VENDOR(xb->xb_devices[dev].id) == PCI_VENDOR_INVALID)
+ if (dev >= xb->xb_nslots || SLOT_EMPTY(xb, dev))
rrb[i] = 0;
else
rrb[i] = 4; /* optimistic value */
@@ -2311,9 +2340,7 @@ xbridge_resource_setup(struct xbpci_softc *xb)
#endif
nppb = npccbb = 0;
for (dev = 0; dev < xb->xb_nslots; dev++) {
- id = xb->xb_devices[dev].id;
-
- if (PCI_VENDOR(id) == PCI_VENDOR_INVALID || PCI_VENDOR(id) == 0)
+ if (SLOT_EMPTY(xb, dev))
continue;
/*
@@ -2344,6 +2371,7 @@ xbridge_resource_setup(struct xbpci_softc *xb)
* we'll need to perform proper PCI resource allocation.
*/
+ id = xb->xb_devices[dev].id;
devio = xbridge_read_reg(xb, BRIDGE_DEVICE(dev));
if (id != PCI_ID_CODE(PCI_VENDOR_SGI, PCI_PRODUCT_SGI_IOC3) &&
id != PCI_ID_CODE(PCI_VENDOR_SGI, PCI_PRODUCT_SGI_IOC4))
@@ -2428,9 +2456,7 @@ xbridge_resource_setup(struct xbpci_softc *xb)
secondary = 1;
ppbstride = nppb == 0 ? 0 : (255 - npccbb) / nppb;
for (dev = 0; dev < xb->xb_nslots; dev++) {
- id = xb->xb_devices[dev].id;
-
- if (PCI_VENDOR(id) == PCI_VENDOR_INVALID || PCI_VENDOR(id) == 0)
+ if (SLOT_EMPTY(xb, dev))
continue;
tag = pci_make_tag(pc, 0, dev, 0);
@@ -3364,8 +3390,6 @@ xbridge_rbus_parent_mem(struct pci_attach_args *pa)
int
xbridge_allocate_devio(struct xbpci_softc *xb, int dev, int wantlarge)
{
- pcireg_t id;
-
/*
* If the preferred slot is available and matches the size requested,
* use it.
@@ -3386,8 +3410,7 @@ xbridge_allocate_devio(struct xbpci_softc *xb, int dev, int wantlarge)
if (xb->xb_devices[dev].devio != 0)
continue; /* devio in use */
- id = xb->xb_devices[dev].id;
- if (PCI_VENDOR(id) != PCI_VENDOR_INVALID && PCI_VENDOR(id) != 0)
+ if (!SLOT_EMPTY(xb, dev))
continue; /* devio to be used soon */
if (BRIDGE_DEVIO_SIZE(dev) >=