diff options
author | Miod Vallat <miod@cvs.openbsd.org> | 2009-07-13 21:19:29 +0000 |
---|---|---|
committer | Miod Vallat <miod@cvs.openbsd.org> | 2009-07-13 21:19:29 +0000 |
commit | 2ca5f2a4a05686d3b6e646d0e112f77b56e018d5 (patch) | |
tree | 82227fa2dd489d3be437f49a68cf30a815c07572 /sys | |
parent | b7a39aa88744aef81c21d68a32fd0b11fa29847c (diff) |
Extend xbridge to support shared interrupt handlers, and perform PCI-PCI
bridge initialization if necessary; enable ppb on IP27 and IP30 kernels.
With feedback from kettenis@; macepcibr to gain the same functionality soon.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/sgi/conf/GENERIC-IP27 | 5 | ||||
-rw-r--r-- | sys/arch/sgi/conf/GENERIC-IP30 | 5 | ||||
-rw-r--r-- | sys/arch/sgi/conf/RAMDISK-IP27 | 5 | ||||
-rw-r--r-- | sys/arch/sgi/conf/RAMDISK-IP30 | 5 | ||||
-rw-r--r-- | sys/arch/sgi/conf/files.sgi | 4 | ||||
-rw-r--r-- | sys/arch/sgi/pci/pci_machdep.c | 50 | ||||
-rw-r--r-- | sys/arch/sgi/pci/pci_machdep.h | 5 | ||||
-rw-r--r-- | sys/arch/sgi/xbow/xbridge.c | 504 |
8 files changed, 456 insertions, 127 deletions
diff --git a/sys/arch/sgi/conf/GENERIC-IP27 b/sys/arch/sgi/conf/GENERIC-IP27 index dc5322a7c4a..d32d0e8ae31 100644 --- a/sys/arch/sgi/conf/GENERIC-IP27 +++ b/sys/arch/sgi/conf/GENERIC-IP27 @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC-IP27,v 1.11 2009/06/24 17:27:40 miod Exp $ +# $OpenBSD: GENERIC-IP27,v 1.12 2009/07/13 21:19:22 miod Exp $ # # THIS KERNEL IS FOR Origin, Onyx, Fuel, Tezro (IP27, IP35) SYSTEMS ONLY. # @@ -127,6 +127,9 @@ video* at uvideo? udl* at uhub? wsdisplay* at udl? +ppb* at pci? # PCI-PCI bridges +pci* at ppb? + #### NICs dc* at pci? # 21143, "tulip" clone ethernet bnx* at pci? # Broadcom BCM5706/5708 GigE diff --git a/sys/arch/sgi/conf/GENERIC-IP30 b/sys/arch/sgi/conf/GENERIC-IP30 index 0e0efc38955..305372b2c8a 100644 --- a/sys/arch/sgi/conf/GENERIC-IP30 +++ b/sys/arch/sgi/conf/GENERIC-IP30 @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC-IP30,v 1.9 2009/06/24 17:27:40 miod Exp $ +# $OpenBSD: GENERIC-IP30,v 1.10 2009/07/13 21:19:22 miod Exp $ # # THIS KERNEL IS FOR Octane and Octane 2 (IP30) SYSTEMS ONLY. # @@ -127,6 +127,9 @@ video* at uvideo? udl* at uhub? wsdisplay* at udl? +ppb* at pci? # PCI-PCI bridges +pci* at ppb? + #### NICs dc* at pci? # 21143, "tulip" clone ethernet em* at pci? # Intel Pro/1000 ethernet diff --git a/sys/arch/sgi/conf/RAMDISK-IP27 b/sys/arch/sgi/conf/RAMDISK-IP27 index 9f8f6bad256..744dd20b42e 100644 --- a/sys/arch/sgi/conf/RAMDISK-IP27 +++ b/sys/arch/sgi/conf/RAMDISK-IP27 @@ -1,4 +1,4 @@ -# $OpenBSD: RAMDISK-IP27,v 1.6 2009/06/24 17:27:40 miod Exp $ +# $OpenBSD: RAMDISK-IP27,v 1.7 2009/07/13 21:19:22 miod Exp $ # # THIS KERNEL IS FOR Origin, Onyx, Fuel, Tezro (IP27, IP35) SYSTEMS ONLY. @@ -121,6 +121,9 @@ wi* at uhub? # WaveLAN IEEE 802.11DS zyd* at uhub? # Zydas ZD1211 ugen* at uhub? # USB Generic driver +ppb* at pci? # PCI-PCI bridges +pci* at ppb? + #### NICs dc* at pci? # 21143, "tulip" clone ethernet em* at pci? # Intel Pro/1000 ethernet diff --git a/sys/arch/sgi/conf/RAMDISK-IP30 b/sys/arch/sgi/conf/RAMDISK-IP30 index 07453051054..e7a98107b95 100644 --- a/sys/arch/sgi/conf/RAMDISK-IP30 +++ b/sys/arch/sgi/conf/RAMDISK-IP30 @@ -1,4 +1,4 @@ -# $OpenBSD: RAMDISK-IP30,v 1.6 2009/06/24 17:27:40 miod Exp $ +# $OpenBSD: RAMDISK-IP30,v 1.7 2009/07/13 21:19:22 miod Exp $ # # THIS KERNEL IS FOR Octane and Octane 2 (IP30) SYSTEMS ONLY. @@ -122,6 +122,9 @@ wi* at uhub? # WaveLAN IEEE 802.11DS zyd* at uhub? # Zydas ZD1211 ugen* at uhub? # USB Generic driver +ppb* at pci? # PCI-PCI bridges +pci* at ppb? + #### NICs dc* at pci? # 21143, "tulip" clone ethernet em* at pci? # Intel Pro/1000 ethernet diff --git a/sys/arch/sgi/conf/files.sgi b/sys/arch/sgi/conf/files.sgi index 028cfc9ef39..2b8b33b347f 100644 --- a/sys/arch/sgi/conf/files.sgi +++ b/sys/arch/sgi/conf/files.sgi @@ -1,4 +1,4 @@ -# $OpenBSD: files.sgi,v 1.27 2009/06/24 17:27:40 miod Exp $ +# $OpenBSD: files.sgi,v 1.28 2009/07/13 21:19:22 miod Exp $ # # maxpartitions must be first item in files.${ARCH} # @@ -72,6 +72,7 @@ include "dev/onewire/files.onewire" # # PCI Bus bridges # + device macepcibr {} : pcibus attach macepcibr at macebus file arch/sgi/pci/macepcibridge.c macepcibr needs-flag @@ -89,6 +90,7 @@ include "dev/i2o/files.i2o" # include "dev/pci/files.pci" +file arch/sgi/pci/pci_machdep.c pci # Sun HME Ethernet controllers device hme: ether, ifnet, mii, ifmedia diff --git a/sys/arch/sgi/pci/pci_machdep.c b/sys/arch/sgi/pci/pci_machdep.c new file mode 100644 index 00000000000..db8dcc051ac --- /dev/null +++ b/sys/arch/sgi/pci/pci_machdep.c @@ -0,0 +1,50 @@ +/* $OpenBSD: pci_machdep.c,v 1.1 2009/07/13 21:19:26 miod Exp $ */ + +/* + * Copyright (c) 2009 Miodrag Vallat. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/ppbreg.h> + +void +ppb_initialize(pci_chipset_tag_t pc, pcitag_t tag, uint secondary, + uint subordinate, bus_addr_t iostart, bus_addr_t ioend, + bus_addr_t memstart, bus_addr_t memend) +{ + pci_conf_write(pc, tag, PPB_REG_BUSINFO, + (secondary << 8) | (subordinate << 16)); + + pci_conf_write(pc, tag, PPB_REG_MEM, + ((memstart & 0xfff00000) >> 16) | (memend & 0xfff00000)); + pci_conf_write(pc, tag, PPB_REG_IOSTATUS, + (pci_conf_read(pc, tag, PPB_REG_IOSTATUS) & 0xffff0000) | + ((iostart & 0x0000f000) >> 8) | (ioend & 0x0000f000)); + pci_conf_write(pc, tag, PPB_REG_IO_HI, + ((iostart & 0xffff0000) >> 16) | (ioend & 0xffff0000)); + pci_conf_write(pc, tag, PPB_REG_PREFMEM, 0); + + pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, + pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG) | + PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | + PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_INVALIDATE_ENABLE | + PCI_COMMAND_SERR_ENABLE); +} diff --git a/sys/arch/sgi/pci/pci_machdep.h b/sys/arch/sgi/pci/pci_machdep.h index 3259470a3f9..e1a1dfcd68f 100644 --- a/sys/arch/sgi/pci/pci_machdep.h +++ b/sys/arch/sgi/pci/pci_machdep.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pci_machdep.h,v 1.4 2008/02/16 18:42:21 miod Exp $ */ +/* $OpenBSD: pci_machdep.h,v 1.5 2009/07/13 21:19:26 miod Exp $ */ /* * Copyright (c) 2003-2004 Opsycon AB (www.opsycon.se / www.opsycon.com) @@ -77,3 +77,6 @@ struct mips_pci_chipset { (*(c)->pc_intr_establish)((c)->pc_intr_v, (ih), (l), (h), (a), (nm)) #define pci_intr_disestablish(c, iv) \ (*(c)->pc_intr_disestablish)((c)->pc_intr_v, (iv)) + +void ppb_initialize(pci_chipset_tag_t, pcitag_t, uint, uint, bus_addr_t, + bus_addr_t, bus_addr_t, bus_addr_t); diff --git a/sys/arch/sgi/xbow/xbridge.c b/sys/arch/sgi/xbow/xbridge.c index 7316037d9f9..8e504daaa62 100644 --- a/sys/arch/sgi/xbow/xbridge.c +++ b/sys/arch/sgi/xbow/xbridge.c @@ -1,4 +1,4 @@ -/* $OpenBSD: xbridge.c,v 1.33 2009/07/06 22:46:43 miod Exp $ */ +/* $OpenBSD: xbridge.c,v 1.34 2009/07/13 21:19:28 miod Exp $ */ /* * Copyright (c) 2008, 2009 Miodrag Vallat. @@ -49,6 +49,7 @@ #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> #include <dev/pci/pcidevs.h> +#include <dev/pci/ppbreg.h> #include <mips64/archtype.h> #include <sgi/xbow/hub.h> @@ -68,9 +69,9 @@ struct xbridge_ate; struct xbridge_softc { struct device sc_dev; - int sc_revision; int sc_flags; -#define XBRIDGE_FLAGS_XBRIDGE 0x01 /* is XBridge vs Bridge */ +#define XBRIDGE_FLAGS_XBRIDGE 0x01 /* is XBridge vs Bridge */ +#define XBRIDGE_FLAGS_NO_DIRECT_IO 0x02 /* no direct I/O mapping */ int16_t sc_nasid; int sc_widget; uint sc_devio_skew; @@ -83,7 +84,6 @@ struct xbridge_softc { struct mips_bus_space *sc_io_bus_space; struct machine_bus_dma_tag *sc_dmat; - int sc_intrbit[BRIDGE_NINTRS]; struct xbridge_intr *sc_intr[BRIDGE_NINTRS]; struct { @@ -154,6 +154,7 @@ int xbridge_address_map(struct xbridge_softc *, paddr_t, bus_addr_t *, bus_addr_t *); void xbridge_address_unmap(struct xbridge_softc *, bus_addr_t, bus_size_t); uint xbridge_ate_add(struct xbridge_softc *, paddr_t); +void xbridge_ate_dump(struct xbridge_softc *); uint xbridge_ate_find(struct xbridge_softc *, paddr_t); uint64_t xbridge_ate_read(struct xbridge_softc *, uint); void xbridge_ate_unref(struct xbridge_softc *, uint, uint); @@ -172,6 +173,8 @@ void xbridge_device_setup(struct xbridge_softc *, int, int, uint32_t, struct extent **, struct extent **); struct extent * xbridge_mapping_setup(struct xbridge_softc *, int); +void xbridge_ppb_setup(struct xbridge_softc *, int, uint, uint, + struct extent **, struct extent **); void xbridge_resource_setup(struct xbridge_softc *); void xbridge_rrb_setup(struct xbridge_softc *, int); void xbridge_setup(struct xbridge_softc *); @@ -225,12 +228,13 @@ xbridge_attach(struct device *parent, struct device *self, void *aux) sc->sc_nasid = xaa->xaa_nasid; sc->sc_widget = xaa->xaa_widget; - sc->sc_revision = xaa->xaa_revision; printf(" revision %d\n", xaa->xaa_revision); if (xaa->xaa_vendor == XBOW_VENDOR_SGI3 && xaa->xaa_product == XBOW_PRODUCT_SGI3_XBRIDGE) sc->sc_flags |= XBRIDGE_FLAGS_XBRIDGE; + else if (xaa->xaa_revision < 4) + sc->sc_flags |= XBRIDGE_FLAGS_NO_DIRECT_IO; /* * Map Bridge registers. @@ -377,7 +381,7 @@ xbridge_decompose_tag(void *cookie, pcitag_t tag, int *busp, int *devp, int *funcp) { if (busp != NULL) - *busp = (tag >> 16) & 0x7; + *busp = (tag >> 16) & 0xff; if (devp != NULL) *devp = (tag >> 11) & 0x1f; if (funcp != NULL) @@ -533,21 +537,26 @@ xbridge_conf_write(void *cookie, pcitag_t tag, int offset, pcireg_t data) * the Heart or Hub widget in charge of interrupt processing. */ +struct xbridge_intrhandler { + LIST_ENTRY(xbridge_intrhandler) xih_nxt; + struct xbridge_intr *xih_main; + int (*xih_func)(void *); + void *xih_arg; + struct evcount xih_count; + int xih_level; + int xih_device; /* device slot number */ +}; + struct xbridge_intr { struct xbridge_softc *xi_bridge; int xi_intrsrc; /* interrupt source on interrupt widget */ int xi_intrbit; /* interrupt source on BRIDGE */ - int xi_device; /* device slot number */ - - int (*xi_func)(void *); - void *xi_arg; - - struct evcount xi_count; - int xi_level; + LIST_HEAD(, xbridge_intrhandler) xi_handlers; }; /* how our pci_intr_handle_t are constructed... */ -#define XBRIDGE_INTR_HANDLE(d,b) (0x100 | ((d) << 3) | (b)) +#define XBRIDGE_INTR_VALID 0x100 +#define XBRIDGE_INTR_HANDLE(d,b) (XBRIDGE_INTR_VALID | ((d) << 3) | (b)) #define XBRIDGE_INTR_DEVICE(h) (((h) >> 3) & 07) #define XBRIDGE_INTR_BIT(h) ((h) & 07) @@ -556,8 +565,9 @@ 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; + int pin; - *ihp = -1; + *ihp = 0; if (pa->pa_intrpin == 0) { /* No IRQ used. */ @@ -573,17 +583,24 @@ xbridge_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp) xbridge_decompose_tag(pa->pa_pc, pa->pa_tag, &bus, &device, NULL); - /* - * For IOC devices, the real information is in pa_intrline. - */ - if (sc->sc_devices[device].id == - PCI_ID_CODE(PCI_VENDOR_SGI, PCI_PRODUCT_SGI_IOC3)) { - intr = pa->pa_intrline; + if (pa->pa_bridgetag) { + pin = PPB_INTERRUPT_SWIZZLE(pa->pa_rawintrpin, device); + if (!ISSET(pa->pa_bridgeih[pin - 1], XBRIDGE_INTR_VALID)) + return 0; + intr = XBRIDGE_INTR_BIT(pa->pa_bridgeih[pin - 1]); } else { - 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].id == + 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); @@ -604,126 +621,143 @@ void * xbridge_intr_establish(void *cookie, pci_intr_handle_t ih, int level, int (*func)(void *), void *arg, char *name) { - struct xbridge_softc *sc = cookie; + struct xbridge_softc *sc = (struct xbridge_softc *)cookie; struct xbridge_intr *xi; + struct xbridge_intrhandler *xih; uint32_t int_addr; int intrbit = XBRIDGE_INTR_BIT(ih); int device = XBRIDGE_INTR_DEVICE(ih); int intrsrc; + int new; /* - * XXX At worst, there can only be two interrupt handlers registered - * XXX on the same pin. + * Allocate bookkeeping structure if this is the + * first time we're using this interrupt source. */ - if (sc->sc_intr[intrbit] != NULL) { - printf("%s: nested interrupts are not supported\n", __func__); - return NULL; - } + if ((xi = sc->sc_intr[intrbit]) == NULL) { + xi = (struct xbridge_intr *) + malloc(sizeof(*xi), M_DEVBUF, M_NOWAIT); + if (xi == NULL) + return NULL; - xi = (struct xbridge_intr *)malloc(sizeof(*xi), M_DEVBUF, M_NOWAIT); - if (xi == NULL) - return NULL; + xi->xi_bridge = sc; + xi->xi_intrbit = intrbit; + LIST_INIT(&xi->xi_handlers); + + if (xbow_intr_register(sc->sc_widget, level, &intrsrc) != 0) { + free(xi, M_DEVBUF); + return NULL; + } + xi->xi_intrsrc = intrsrc; + sc->sc_intr[intrbit] = xi; + } + /* - * Register the interrupt at the Heart or Hub level if it's the + * Register the interrupt at the Heart or Hub level if this is the * first time we're using this interrupt source. */ - if ((intrsrc = sc->sc_intrbit[intrbit]) == -1) { - if (xbow_intr_register(sc->sc_widget, level, &intrsrc) != 0) - return NULL; - + new = LIST_EMPTY(&xi->xi_handlers); + if (new) { /* - * We can afford registering this interrupt at `level' - * IPL since we do not support nested interrupt on a - * given source, yet. + * XXX The interrupt dispatcher is always registered + * XXX at IPL_BIO, in case the interrupt will be shared + * XXX between devices of different levels. */ if (xbow_intr_establish(xbridge_intr_handler, xi, intrsrc, - level, NULL)) { - printf("%s: unable to register interrupt handler, " - "did xheart or xhub attach?\n", + IPL_BIO, NULL)) { + printf("%s: unable to register interrupt handler\n", sc->sc_dev.dv_xname); return NULL; } - - sc->sc_intrbit[intrbit] = intrsrc; } - xi->xi_bridge = sc; - xi->xi_intrsrc = intrsrc; - xi->xi_intrbit = intrbit; - xi->xi_device = device; - xi->xi_func = func; - xi->xi_arg = arg; - xi->xi_level = level; - evcount_attach(&xi->xi_count, name, &xi->xi_level, &evcount_intr); - sc->sc_intr[intrbit] = xi; - - int_addr = ((xbow_intr_widget_register >> 30) & 0x0003ff00) | intrsrc; - - bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_ADDR(intrbit), - int_addr); - bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_IER, - bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_IER) | - (1 << intrbit)); - /* - * INT_MODE register controls which interrupt pins cause - * ``interrupt clear'' packets to be sent for high->low - * transition. - * We enable such packets to be sent in order not to have to - * clear interrupts ourselves. - */ - bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_MODE, - bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_MODE) | - (1 << intrbit)); - bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_DEV, - bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_DEV) | - (device << (intrbit * 3))); - (void)bus_space_read_4(sc->sc_iot, sc->sc_regh, WIDGET_TFLUSH); + xih = (struct xbridge_intrhandler *) + malloc(sizeof(*xih), M_DEVBUF, M_NOWAIT); + if (xih == NULL) + return NULL; + + xih->xih_main = xi; + xih->xih_func = func; + xih->xih_arg = arg; + xih->xih_level = level; + xih->xih_device = device; + evcount_attach(&xih->xih_count, name, &xi->xi_intrbit, &evcount_intr); + LIST_INSERT_HEAD(&xi->xi_handlers, xih, xih_nxt); + + if (new) { + int_addr = + ((xbow_intr_widget_register >> 30) & 0x0003ff00) | intrsrc; + + bus_space_write_4(sc->sc_iot, sc->sc_regh, + BRIDGE_INT_ADDR(intrbit), int_addr); + bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_IER, + bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_IER) | + (1 << intrbit)); + /* + * INT_MODE register controls which interrupt pins cause + * ``interrupt clear'' packets to be sent for high->low + * transition. + * We enable such packets to be sent in order not to have to + * clear interrupts ourselves. + */ + bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_MODE, + bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_MODE) | + (1 << intrbit)); + bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_DEV, + bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_DEV) | + (device << (intrbit * 3))); + (void)bus_space_read_4(sc->sc_iot, sc->sc_regh, WIDGET_TFLUSH); + } - return (void *)((uint64_t)ih); + return (void *)xih; } void xbridge_intr_disestablish(void *cookie, void *vih) { struct xbridge_softc *sc = cookie; - struct xbridge_intr *xi; - pci_intr_handle_t ih = (pci_intr_handle_t)(uint64_t)vih; - int intrbit = XBRIDGE_INTR_BIT(ih); - - /* should not happen */ - if ((xi = sc->sc_intr[intrbit]) == NULL) - return; - - bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_ADDR(intrbit), 0); - bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_IER, - bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_IER) & - ~(1 << intrbit)); - bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_MODE, - bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_MODE) & - ~(1 << intrbit)); - bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_DEV, - bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_DEV) & - ~(7 << (intrbit * 3))); - (void)bus_space_read_4(sc->sc_iot, sc->sc_regh, WIDGET_TFLUSH); + struct xbridge_intrhandler *xih = (struct xbridge_intrhandler *)vih; + struct xbridge_intr *xi = xih->xih_main; + int intrbit = xi->xi_intrbit; - evcount_detach(&xi->xi_count); + evcount_detach(&xih->xih_count); + LIST_REMOVE(xih, xih_nxt); - xbow_intr_disestablish(xi->xi_intrsrc); + if (LIST_EMPTY(&xi->xi_handlers)) { + bus_space_write_4(sc->sc_iot, sc->sc_regh, + BRIDGE_INT_ADDR(intrbit), 0); + bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_IER, + bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_IER) & + ~(1 << intrbit)); + bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_MODE, + bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_MODE) & + ~(1 << intrbit)); + bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_DEV, + bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_DEV) & + ~(7 << (intrbit * 3))); + (void)bus_space_read_4(sc->sc_iot, sc->sc_regh, WIDGET_TFLUSH); + + xbow_intr_disestablish(xi->xi_intrsrc); + /* + * Note we could free sc->sc_intr[intrbit] at this point, + * but it's not really worth doing. + */ + } - sc->sc_intr[intrbit] = NULL; - free(xi, M_DEVBUF); + free(xih, M_DEVBUF); } int xbridge_intr_handler(void *v) { - struct xbridge_intr *xi = v; + struct xbridge_intr *xi = (struct xbridge_intr *)v; struct xbridge_softc *sc = xi->xi_bridge; - int rc; + struct xbridge_intrhandler *xih; + int rc = 0; int spurious; - if (xi == NULL) { + if (LIST_EMPTY(&xi->xi_handlers)) { printf("%s: spurious irq %d\n", sc->sc_dev.dv_xname, xi->xi_intrbit); return 0; @@ -732,8 +766,9 @@ xbridge_intr_handler(void *v) /* * Flush PCI write buffers before servicing the interrupt. */ - bus_space_read_4(sc->sc_iot, sc->sc_regh, - BRIDGE_DEVICE_WBFLUSH(xi->xi_device)); + LIST_FOREACH(xih, &xi->xi_handlers, xih_nxt) + bus_space_read_4(sc->sc_iot, sc->sc_regh, + BRIDGE_DEVICE_WBFLUSH(xih->xih_device)); if ((bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_ISR) & (1 << xi->xi_intrbit)) == 0) { @@ -746,8 +781,12 @@ xbridge_intr_handler(void *v) } else spurious = 0; - if ((rc = (*xi->xi_func)(xi->xi_arg)) != 0) - xi->xi_count.ec_count++; + LIST_FOREACH(xih, &xi->xi_handlers, xih_nxt) { + if ((*xih->xih_func)(xih->xih_arg) != 0) { + xih->xih_count.ec_count++; + rc = 1; + } + } if (rc == 0 && spurious == 0) printf("%s: spurious irq %d\n", sc->sc_dev.dv_xname, xi->xi_intrbit); @@ -975,6 +1014,54 @@ struct xbridge_ate { paddr_t xa_pa; }; +#ifdef ATE_DEBUG +void +xbridge_ate_dump(struct xbridge_softc *sc) +{ + struct xbridge_ate *ate; + uint a; + + printf("%s ATE list (in array order)\n", sc->sc_dev.dv_xname); + for (a = 0, ate = sc->sc_ate; a < sc->sc_atecnt; a++, ate++) { + printf("%03x %p %02u", a, ate->xa_pa, ate->xa_refcnt); + if ((a % 3) == 2) + printf("\n"); + else + printf(" "); + } + if ((a % 3) != 0) + printf("\n"); + + printf("%s USED ATE list (in link order)\n", sc->sc_dev.dv_xname); + a = 0; + LIST_FOREACH(ate, &sc->sc_used_ate, xa_nxt) { + printf("%03x %p %02u", + ate - sc->sc_ate, ate->xa_pa, ate->xa_refcnt); + if ((a % 3) == 2) + printf("\n"); + else + printf(" "); + a++; + } + if ((a % 3) != 0) + printf("\n"); + + printf("%s FREE ATE list (in link order)\n", sc->sc_dev.dv_xname); + a = 0; + LIST_FOREACH(ate, &sc->sc_free_ate, xa_nxt) { + printf("%03x %p %02u", + ate - sc->sc_ate, ate->xa_pa, ate->xa_refcnt); + if ((a % 3) == 2) + printf("\n"); + else + printf(" "); + a++; + } + if ((a % 3) != 0) + printf("\n"); +} +#endif + void xbridge_ate_setup(struct xbridge_softc *sc) { @@ -1106,7 +1193,7 @@ xbridge_ate_add(struct xbridge_softc *sc, paddr_t pa) #endif xbridge_ate_write(sc, a, ate->xa_pa | - (xbow_intr_widget << ATE_WIDGET_SHIFT) | ATE_V); + (xbow_intr_widget << ATE_WIDGET_SHIFT) | ATE_COH | ATE_V); return a; } @@ -1199,6 +1286,11 @@ xbridge_address_map(struct xbridge_softc *sc, paddr_t pa, bus_addr_t *mapping, return 0; } + printf("%s: out of ATE\n", sc->sc_dev.dv_xname); +#ifdef ATE_DEBUG + xbridge_ate_dump(sc); +#endif + mtx_leave(&sc->sc_atemtx); /* @@ -1582,7 +1674,7 @@ xbridge_setup(struct xbridge_softc *sc) { paddr_t pa; uint32_t ctrl; - int dev, i; + int dev; /* * Gather device identification for all slots. @@ -1659,9 +1751,6 @@ xbridge_setup(struct xbridge_softc *sc) (uint32_t)xbow_intr_widget_register); (void)bus_space_read_4(sc->sc_iot, sc->sc_regh, WIDGET_TFLUSH); - - for (i = 0; i < BRIDGE_NINTRS; i++) - sc->sc_intrbit[i] = -1; } /* @@ -1768,6 +1857,7 @@ xbridge_resource_setup(struct xbridge_softc *sc) pcireg_t id, bhlcr; uint32_t devio; int need_setup; + uint curppb, nppb; const struct pci_quirkdata *qd; struct extent *io_ex = NULL, *mem_ex = NULL; @@ -1794,6 +1884,11 @@ xbridge_resource_setup(struct xbridge_softc *sc) mem_ex = xbridge_mapping_setup(sc, 0); } + /* + * Configure all regular PCI devices. + */ + + curppb = nppb = 0; for (dev = 0; dev < BRIDGE_NSLOTS; dev++) { id = sc->sc_devices[dev].id; @@ -1801,6 +1896,15 @@ xbridge_resource_setup(struct xbridge_softc *sc) continue; /* + * Count ppb devices, we will need their number later. + */ + + tag = pci_make_tag(pc, 0, dev, 0); + bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG); + if (PCI_HDRTYPE(bhlcr) == 1) + nppb++; + + /* * We want to avoid changing mapping configuration for * devices which have been setup by ARCS. * @@ -1851,6 +1955,11 @@ xbridge_resource_setup(struct xbridge_softc *sc) */ devio &= ~BRIDGE_DEVICE_PREFETCH; + /* + * Force cache coherency. + */ + devio |= BRIDGE_DEVICE_COHERENT; + xbridge_set_devio(sc, dev, devio); if (need_setup == 0) @@ -1869,9 +1978,6 @@ xbridge_resource_setup(struct xbridge_softc *sc) */ qd = pci_lookup_quirkdata(PCI_VENDOR(id), PCI_PRODUCT(id)); - tag = pci_make_tag(pc, 0, dev, 0); - bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG); - if (PCI_HDRTYPE_MULTIFN(bhlcr) || (qd != NULL && (qd->quirks & PCI_QUIRK_MULTIFUNCTION) != 0)) nfuncs = 8; @@ -1881,6 +1987,39 @@ xbridge_resource_setup(struct xbridge_softc *sc) xbridge_device_setup(sc, dev, nfuncs, devio, &io_ex, &mem_ex); } + /* + * Configure PCI-PCI bridges, if any. + * + * We do this after all the other PCI devices have been configured + * in order to favour them during resource allocation. + */ + + for (dev = 0; dev < BRIDGE_NSLOTS; dev++) { + id = sc->sc_devices[dev].id; + + if (PCI_VENDOR(id) == PCI_VENDOR_INVALID || PCI_VENDOR(id) == 0) + continue; + + tag = pci_make_tag(pc, 0, dev, 0); + bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG); + + if (PCI_HDRTYPE(bhlcr) != 1) + continue; + + /* + * Being PCI bus #0, we can allocate #1-#255 bus numbers + * to the PCI-PCI bridges. + * We'll simply split this 255 bus number space accross + * all bridges. + * Thus, given N bridges on the bus, bridge #M will get + * 1 + M * (255/N) .. (M + 1) * (255 / N). + */ + + xbridge_ppb_setup(sc, dev, 1 + curppb * (255 / nppb), + (curppb + 1) * (255 / nppb), &io_ex, &mem_ex); + curppb++; + } + if (io_ex != NULL) extent_destroy(io_ex); if (mem_ex != NULL) @@ -1904,8 +2043,7 @@ xbridge_mapping_setup(struct xbridge_softc *sc, int io) * didn't work was the byteswap logic). */ - if (ISSET(sc->sc_flags, XBRIDGE_FLAGS_XBRIDGE) || - sc->sc_revision >= 4) { + if (!ISSET(sc->sc_flags, XBRIDGE_FLAGS_NO_DIRECT_IO)) { offs = BRIDGE_PCI_IO_SPACE_BASE; len = BRIDGE_PCI_IO_SPACE_LENGTH; base = xbow_widget_map_space(sc->sc_dev.dv_parent, @@ -2317,6 +2455,130 @@ xbridge_device_setup(struct xbridge_softc *sc, int dev, int nfuncs, extent_destroy(ioex); } +void +xbridge_ppb_setup(struct xbridge_softc *sc, int dev, uint secondary, + uint subordinate, struct extent **large_ioex, struct extent **large_memex) +{ + pci_chipset_tag_t pc = &sc->sc_pc; + pcitag_t tag; + uint32_t devio; + uint32_t base; + bus_addr_t iostart, ioend; + bus_addr_t memstart, memend; + bus_size_t exsize; + u_long exstart; + int devio_idx; + int tries; + + iostart = memstart = 0; + ioend = memend = 0xffffffff; + devio = bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_DEVICE(dev)); + + /* + * Since we can't know in advance how much resource space will be + * needed by the devices behind the bridge, try to be generous. + * + * We'll try to get large mappings, and provide 1/8 of the range + * to the bridge. If this isn't possible, we'll try to allocate + * the largest possible devio range. This can still fail since + * we want to provide both I/O and memory resources and might + * not have enough devio slots available. + */ + + if (*large_memex == NULL) + *large_memex = xbridge_mapping_setup(sc, 0); + if (*large_memex == NULL) { + devio_idx = xbridge_allocate_devio(sc, dev, 1); + if (devio_idx < 0) + devio_idx = xbridge_allocate_devio(sc, dev, 0); + if (devio_idx < 0) { + /* no resources */ + } else { + base = (sc->sc_devio_skew << 24) | + BRIDGE_DEVIO_OFFS(devio_idx); + xbridge_set_devio(sc, devio_idx, devio | + BRIDGE_DEVICE_IO_MEM | + (base >> BRIDGE_DEVICE_BASE_SHIFT)); + memstart = base; + memend = base + BRIDGE_DEVIO_SIZE(devio_idx) - 1; + } + } else { + /* + * We know that the direct memory resource range fits + * within the 32 bit address space, and is limited to + * 30 bits, so our allocation, if successfull, will work + * as a 32 bit memory range. + */ + exsize = (*large_memex)->ex_end + 1 - (*large_memex)->ex_start; + if ((*large_memex)->ex_start == 1) + exsize++; + exsize /= BRIDGE_NSLOTS; + /* no need to round, exsize is >= 1 << 25 */ + + for (tries = 0; tries < 5; tries++) { + if (extent_alloc(*large_memex, exsize, + 1UL << 20, 0, 0, EX_NOWAIT | EX_MALLOCOK, + &exstart) == 0) { + memstart = exstart; + memend = exstart + exsize - 1; + break; + } + exsize >>= 1; + } + } + + if (*large_ioex == NULL) + *large_ioex = xbridge_mapping_setup(sc, 1); + if (*large_ioex == NULL) { + devio_idx = xbridge_allocate_devio(sc, dev, 1); + if (devio_idx < 0) + devio_idx = xbridge_allocate_devio(sc, dev, 0); + if (devio_idx < 0) { + /* no resources */ + } else { + base = (sc->sc_devio_skew << 24) | + BRIDGE_DEVIO_OFFS(devio_idx); + xbridge_set_devio(sc, devio_idx, devio | + (base >> BRIDGE_DEVICE_BASE_SHIFT)); + iostart = base; + ioend = base + BRIDGE_DEVIO_SIZE(devio_idx) - 1; + } + } else { + /* + * We know that the direct I/O resource range fits + * within the 32 bit address space, so our allocation, + * if successfull, will work as a 32 bit i/o range. + */ + exsize = (*large_ioex)->ex_end + 1 - (*large_ioex)->ex_start; + if ((*large_ioex)->ex_start == 1) + exsize++; + exsize /= BRIDGE_NSLOTS; + /* no need to round, exsize is >= 1 << 25 */ + + for (tries = 0; tries < 5; tries++) { + if (extent_alloc(*large_ioex, exsize, + 1UL << 12, 0, 0, EX_NOWAIT | EX_MALLOCOK, + &exstart) == 0) { + iostart = exstart; + ioend = exstart + exsize - 1; + break; + } + exsize >>= 1; + } + } + + /* + * Finally program the bridge registers. + * + * We do not expect PCI-PCI bridges to be multifunction + * devices, so we'll only configure function #0. + */ + + tag = pci_make_tag(pc, 0, dev, 0); + ppb_initialize(pc, tag, secondary, subordinate, iostart, ioend, + memstart, memend); +} + int xbridge_allocate_devio(struct xbridge_softc *sc, int dev, int wantlarge) { @@ -2324,7 +2586,7 @@ xbridge_allocate_devio(struct xbridge_softc *sc, int dev, int wantlarge) /* * If the preferred slot is available and matches the size requested, - * use it (we do n. + * use it. */ if (sc->sc_devices[dev].devio == 0) { |