diff options
author | Miod Vallat <miod@cvs.openbsd.org> | 2009-11-11 15:29:32 +0000 |
---|---|---|
committer | Miod Vallat <miod@cvs.openbsd.org> | 2009-11-11 15:29:32 +0000 |
commit | 0ae0bc9735bfce4e564bd3d306772e3cbd4c26f7 (patch) | |
tree | ce55d45ab5acc678d99dd25a2c54cde96c7baceb | |
parent | 36b0a223470c06b4001571b920f1051b6382b65b (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.c | 224 | ||||
-rw-r--r-- | sys/arch/sgi/xbow/xbridge.c | 61 |
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) >= |