diff options
author | Miod Vallat <miod@cvs.openbsd.org> | 2009-05-03 19:44:29 +0000 |
---|---|---|
committer | Miod Vallat <miod@cvs.openbsd.org> | 2009-05-03 19:44:29 +0000 |
commit | 068967e49d34f75ebd3e56b19eaf19a5769ef8e0 (patch) | |
tree | 34c11bd94f6ce8d28b63ac3c334d45ab509827e6 /sys/arch/sgi/xbow | |
parent | 0e69e6e05d5002e9d80cf857b68d96b8310f55e5 (diff) |
Complete overhaul of the PCI bridge initialization. It will now allocate
resources to cards not configured by the PROM. There are still some
shortcomings, but this is a good start.
Tested on IP35 with an fxp(4).
Diffstat (limited to 'sys/arch/sgi/xbow')
-rw-r--r-- | sys/arch/sgi/xbow/xbridge.c | 469 | ||||
-rw-r--r-- | sys/arch/sgi/xbow/xbridgereg.h | 6 |
2 files changed, 380 insertions, 95 deletions
diff --git a/sys/arch/sgi/xbow/xbridge.c b/sys/arch/sgi/xbow/xbridge.c index 7522bd6780f..08e115d2079 100644 --- a/sys/arch/sgi/xbow/xbridge.c +++ b/sys/arch/sgi/xbow/xbridge.c @@ -1,7 +1,7 @@ -/* $OpenBSD: xbridge.c,v 1.13 2009/05/02 21:30:13 miod Exp $ */ +/* $OpenBSD: xbridge.c,v 1.14 2009/05/03 19:44:28 miod Exp $ */ /* - * Copyright (c) 2008 Miodrag Vallat. + * Copyright (c) 2008, 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 @@ -26,7 +26,7 @@ #include <sys/device.h> #include <sys/evcount.h> #include <sys/malloc.h> -#include <sys/proc.h> +#include <sys/extent.h> #include <machine/atomic.h> #include <machine/autoconf.h> @@ -61,7 +61,8 @@ struct xbridge_softc { struct mips_bus_space *sc_mem_bus_space; struct mips_bus_space *sc_io_bus_space; - struct machine_bus_dma_tag sc_dmatag; + struct extent *sc_mem_ex; + struct extent *sc_io_ex; bus_space_tag_t sc_iot; bus_space_handle_t sc_regh; @@ -113,13 +114,20 @@ void xbridge_read_raw_8(bus_space_tag_t, bus_space_handle_t, bus_addr_t, uint8_t *, bus_size_t); void xbridge_write_raw_8(bus_space_tag_t, bus_space_handle_t, bus_addr_t, const uint8_t *, bus_size_t); + int xbridge_space_map_short(bus_space_tag_t, bus_addr_t, bus_size_t, int, bus_space_handle_t *); bus_addr_t xbridge_pa_to_device(paddr_t); paddr_t xbridge_device_to_pa(bus_addr_t); -const struct machine_bus_dma_tag xbridge_dma_tag = { +void xbridge_setup(struct xbridge_softc *); +void xbridge_resource_manage(struct xbridge_softc *, pcitag_t, + struct extent *); +void xbridge_resource_setup(struct xbridge_softc *); +void xbridge_rrb_setup(struct xbridge_softc *, int); + +struct machine_bus_dma_tag xbridge_dma_tag = { NULL, /* _cookie */ _dmamap_create, _dmamap_destroy, @@ -136,7 +144,7 @@ const struct machine_bus_dma_tag xbridge_dma_tag = { _dmamem_mmap, xbridge_pa_to_device, xbridge_device_to_pa, - 0ULL /* no mask */ + BRIDGE_DMA_DIRECT_LENGTH - 1 }; int @@ -161,7 +169,6 @@ xbridge_attach(struct device *parent, struct device *self, void *aux) struct xbridge_softc *sc = (struct xbridge_softc *)self; struct pcibus_attach_args pba; struct xbow_attach_args *xaa = aux; - int i; sc->sc_widget = xaa->xaa_widget; @@ -200,6 +207,9 @@ xbridge_attach(struct device *parent, struct device *self, void *aux) bcopy(xaa->xaa_long_tag, sc->sc_mem_bus_space, sizeof(*sc->sc_mem_bus_space)); sc->sc_mem_bus_space->bus_base += BRIDGE_PCI_MEM_SPACE_BASE; + sc->sc_mem_ex = extent_create("pcimem", + 0, BRIDGE_PCI_MEM_SPACE_LENGTH - 1, + M_DEVBUF, NULL, 0, EX_NOWAIT); if (!ISSET(sc->sc_flags, XBRIDGE_FLAGS_XBRIDGE) && xaa->xaa_revision >= 4) { @@ -208,6 +218,10 @@ xbridge_attach(struct device *parent, struct device *self, void *aux) sizeof(*sc->sc_io_bus_space)); sc->sc_io_bus_space->bus_base += BRIDGE_PCI_IO_SPACE_BASE; + + sc->sc_io_ex = extent_create("pciio", + 0, BRIDGE_PCI_IO_SPACE_LENGTH - 1, + M_DEVBUF, NULL, 0, EX_NOWAIT); } else { /* Programmable I/O mappings in the small window */ bcopy(xaa->xaa_short_tag, sc->sc_io_bus_space, @@ -267,101 +281,23 @@ xbridge_attach(struct device *parent, struct device *self, void *aux) sc->sc_pc.pc_intr_disestablish = xbridge_intr_disestablish; /* - * Configure the direct DMA window to access the low 2GB of memory. + * Configure Bridge for proper operation (DMA, I/O mappings, + * RRB allocation, etc). */ - if (sys_config.system_type == SGI_OCTANE) - bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_DIR_MAP, - (xbow_intr_widget << BRIDGE_DIRMAP_WIDGET_SHIFT) | - BRIDGE_DIRMAP_ADD_512MB); - else - bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_DIR_MAP, - xbow_intr_widget << BRIDGE_DIRMAP_WIDGET_SHIFT); - - /* - * Gather device identification for all slots. - * We need this to be able to allocate RRBs correctly, and also - * to be able to check quickly whether a given device is an IOC3. - */ - - for (i = 0; i < BRIDGE_NSLOTS; i++) { - paddr_t pa; - - pa = sc->sc_regh + BRIDGE_PCI_CFG_SPACE + - (i << 12) + PCI_ID_REG; - if (guarded_read_4(pa, &sc->sc_devices[i]) != 0) - sc->sc_devices[i] = 0xffffffff; - } - -#if 0 /* XXX write proper RRB allocation code */ - if (sys_config.system_type == SGI_OCTANE) { - bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_RRB_EVEN, - 0x99889988 | 0x44440000); - bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_RRB_ODD, - 0x99889988 | 0x44440000); - } else { - bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_RRB_EVEN, - 0xba98ba98 | 0x44440000); - bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_RRB_ODD, - 0xba98ba98 | 0x44440000); - } -#endif - -#if 0 /* XXX write proper I/O mapping allocation code */ - for (i = 0; i < BRIDGE_NSLOTS; i++) { - uint32_t dio; - - if (sc->sc_devices[i] == - PCI_ID_CODE(PCI_VENDOR_SGI, PCI_PRODUCT_SGI_IOC3)) - continue; - - dio = bus_space_read_4(sc->sc_iot, sc->sc_regh, - BRIDGE_DEVICE(i)); - dio |= BRIDGE_DEVICE_SWAP_PMU | BRIDGE_DEVICE_SWAP_DIR | - BRIDGE_DEVICE_COHERENT | BRIDGE_DEVICE_BARRIER; - bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_DEVICE(i), - dio); - } -#endif - - (void)bus_space_read_4(sc->sc_iot, sc->sc_regh, WIDGET_TFLUSH); - - /* - * Setup interrupt handling. - */ - - bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_IER, 0); - bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_MODE, 0); - bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_DEV, 0); - - bus_space_write_4(sc->sc_iot, sc->sc_regh, WIDGET_INTDEST_ADDR_UPPER, - (xbow_intr_widget_register >> 32) | (xbow_intr_widget << 16)); - bus_space_write_4(sc->sc_iot, sc->sc_regh, WIDGET_INTDEST_ADDR_LOWER, - (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; + xbridge_setup(sc); /* * Attach children. */ - bcopy(&xbridge_dma_tag, &sc->sc_dmatag, sizeof(xbridge_dma_tag)); - if (sys_config.system_type == SGI_OCTANE) { - /* - * Make sure we do not risk crossing the direct mapping - * window. - */ - sc->sc_dmatag._dma_mask = BRIDGE_DMA_DIRECT_LENGTH - 1; - } - bzero(&pba, sizeof(pba)); pba.pba_busname = "pci"; pba.pba_iot = sc->sc_io_bus_space; pba.pba_memt = sc->sc_mem_bus_space; - pba.pba_dmat = &sc->sc_dmatag; + pba.pba_dmat = &xbridge_dma_tag; + pba.pba_ioex = sc->sc_io_ex; + pba.pba_memex = sc->sc_mem_ex; pba.pba_pc = &sc->sc_pc; pba.pba_domain = pci_ndomains++; pba.pba_bus = 0; @@ -829,8 +765,8 @@ xbridge_write_raw_8(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o, } /* - * On IP27, we can not use the default xbow space_map_short because - * of the games we play with bus addresses. + * On IP27 and IP35, we can not use the default xbow space_map_short + * because of the games we play with bus addresses. */ int xbridge_space_map_short(bus_space_tag_t t, bus_addr_t offs, bus_size_t size, @@ -891,3 +827,350 @@ xbridge_device_to_pa(bus_addr_t addr) return pa; } + +/* + * Bridge configuration code. + */ + +void +xbridge_setup(struct xbridge_softc *sc) +{ + paddr_t pa; + int dev, i; + + /* + * Gather device identification for all slots. + * We need this to be able to allocate RRBs correctly, and also + * to be able to check quickly whether a given device is an IOC3. + */ + + for (dev = 0; dev < BRIDGE_NSLOTS; dev++) { + pa = sc->sc_regh + BRIDGE_PCI_CFG_SPACE + + (dev << 12) + PCI_ID_REG; + if (guarded_read_4(pa, &sc->sc_devices[dev]) != 0) + sc->sc_devices[dev] = + PCI_ID_CODE(PCI_VENDOR_INVALID, 0xffff); + } + + /* + * Configure the direct DMA window to access the low 2GB of memory. + */ + + if (sys_config.system_type == SGI_OCTANE) + bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_DIR_MAP, + (xbow_intr_widget << BRIDGE_DIRMAP_WIDGET_SHIFT) | + BRIDGE_DIRMAP_ADD_512MB); + else + bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_DIR_MAP, + xbow_intr_widget << BRIDGE_DIRMAP_WIDGET_SHIFT); + + /* + * Allocate RRB for the existing devices. + */ + + xbridge_rrb_setup(sc, 0); + xbridge_rrb_setup(sc, 1); + + /* + * The PROM will only configure the onboard devices. Set up + * any other device we might encounter. + */ + + xbridge_resource_setup(sc); + + /* + * Setup interrupt handling. + */ + + bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_IER, 0); + bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_MODE, 0); + bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_DEV, 0); + + bus_space_write_4(sc->sc_iot, sc->sc_regh, WIDGET_INTDEST_ADDR_UPPER, + (xbow_intr_widget_register >> 32) | (xbow_intr_widget << 16)); + bus_space_write_4(sc->sc_iot, sc->sc_regh, WIDGET_INTDEST_ADDR_LOWER, + (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; +} + +/* + * Build a not-so-pessimistic RRB allocation register value. + */ +void +xbridge_rrb_setup(struct xbridge_softc *sc, int odd) +{ + uint rrb[BRIDGE_NSLOTS / 2]; /* tentative rrb assignment */ + uint total; /* rrb count */ + uint32_t proto; /* proto rrb value */ + int dev, i, j; + + /* + * First, try to allocate as many RRBs per device as possible. + */ + + total = 0; + for (i = 0; i < BRIDGE_NSLOTS / 2; i++) { + dev = (i << 1) + !!odd; + if (PCI_VENDOR(sc->sc_devices[dev]) == PCI_VENDOR_INVALID) + rrb[i] = 0; + else + rrb[i] = 4; /* optimistic value */ + total += rrb[i]; + } + + /* + * Then, try to reduce greed until we do not claim more than + * the 8 RRBs we can afford. + */ + + if (total > 8) { + /* + * All devices should be able to live with 3 RRBs, so + * reduce their allocation from 4 to 3. + */ + for (i = 0; i < BRIDGE_NSLOTS / 2; i++) { + if (rrb[i] == 4) { + rrb[i]--; + if (--total == 8) + break; + } + } + } + + if (total > 8) { + /* + * There are too many devices for 3 RRBs per device to + * be possible. Attempt to reduce from 3 to 2, except + * for isp(4) devices. + */ + for (i = 0; i < BRIDGE_NSLOTS / 2; i++) { + if (rrb[i] == 3) { + dev = (i << 1) + !!odd; + if (PCI_VENDOR(sc->sc_devices[dev]) != + PCI_VENDOR_QLOGIC) { + rrb[i]--; + if (--total == 8) + break; + } + } + } + } + + if (total > 8) { + /* + * Too bad, we need to shrink the RRB allocation for + * isp devices too. We'll try to favour the lowest + * slots, though, hence the reversed loop order. + */ + for (i = BRIDGE_NSLOTS / 2 - 1; i >= 0; i--) { + if (rrb[i] == 3) { + rrb[i]--; + if (--total == 8) + break; + } + } + } + + /* + * Now build the RRB register value proper. + */ + + proto = 0; + for (i = 0; i < BRIDGE_NSLOTS / 2; i++) { + for (j = 0; j < rrb[i]; j++) + proto = (proto << RRB_SHIFT) | (RRB_VALID | i); + } + + bus_space_write_4(sc->sc_iot, sc->sc_regh, + odd ? BRIDGE_RRB_ODD : BRIDGE_RRB_EVEN, proto); +} + +void +xbridge_resource_setup(struct xbridge_softc *sc) +{ + pci_chipset_tag_t pc = &sc->sc_pc; + int dev, function, nfuncs; + pcitag_t tag; + pcireg_t id, bhlcr; + const struct pci_quirkdata *qd; + uint32_t devio, basewin; + int need_setup; + struct extent *ioex; + + /* + * On Octane, the firmware will setup the I/O registers + * correctly for the on-board devices. Other PCI buses, + * and other systems, need more attention. + * + * XXX Another reason not to enter the loop below on the Octane + * XXX main Bridge widget is that it uses a sligthly different + * XXX devio window allocation scheme, with only one large devio + * XXX (used by the first isp controller). + */ + if (sys_config.system_type == SGI_OCTANE && sc->sc_widget == WIDGET_MAX) + return; + + for (dev = 0; dev < BRIDGE_NSLOTS; dev++) { + id = sc->sc_devices[dev]; + + if (PCI_VENDOR(id) == PCI_VENDOR_INVALID || PCI_VENDOR(id) == 0) + continue; + + /* + * Devices which have been configured by the firmware + * have their I/O window pointing to the bridge widget. + */ + devio = bus_space_read_4(sc->sc_iot, sc->sc_regh, + BRIDGE_DEVICE(dev)); + need_setup = ((devio & BRIDGE_DEVICE_BASE_MASK) >> + (24 - BRIDGE_DEVICE_BASE_SHIFT)) != sc->sc_widget; + + if (need_setup) { + basewin = + (sc->sc_widget << 24) | BRIDGE_DEVIO_OFFS(dev); + + devio &= ~BRIDGE_DEVICE_BASE_MASK; + + /* + * XXX This defaults to I/O resources only. + * XXX However some devices may carry only + * XXX memory mappings. + * XXX This code should assign devio in a more + * XXX flexible way... + */ + devio &= ~BRIDGE_DEVICE_IO_MEM; + + devio |= (basewin >> BRIDGE_DEVICE_BASE_SHIFT); + } + + /* + * Enable byte swapping for PIO and DMA, except on IOC3 and + * RAD1 devices. + */ + if (id == PCI_ID_CODE(PCI_VENDOR_SGI, PCI_PRODUCT_SGI_IOC3) || + id == PCI_ID_CODE(PCI_VENDOR_SGI, PCI_PRODUCT_SGI_RAD1)) + devio &= ~(BRIDGE_DEVICE_SWAP_DIR | + BRIDGE_DEVICE_SWAP_PMU); + else + devio |= BRIDGE_DEVICE_SWAP_DIR | + BRIDGE_DEVICE_SWAP_PMU; + + bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_DEVICE(dev), + devio); + (void)bus_space_read_4(sc->sc_iot, sc->sc_regh, WIDGET_TFLUSH); + + /* + * If we can manage I/O resource allocation in the MI code, + * we do not need to do anything more at this stage... + */ + + if (sc->sc_io_ex != NULL) + continue; + + /* + * ...otherwise, we need to perform the resource allocation + * ourselves, within the devio window we have configured + * above, for the devices which have not been setup by the + * firmware already. + */ + + if (need_setup == 0) + continue; + + ioex = extent_create("pciio", + basewin, basewin + BRIDGE_DEVIO_SIZE(dev) - 1, + M_DEVBUF, NULL, 0, EX_NOWAIT); + if (ioex == NULL) + continue; + + 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; + else + nfuncs = 1; + + for (function = 0; function < nfuncs; function++) { + tag = pci_make_tag(pc, 0, dev, function); + id = pci_conf_read(pc, tag, PCI_ID_REG); + + if (PCI_VENDOR(id) == PCI_VENDOR_INVALID || + PCI_VENDOR(id) == 0) + continue; + + xbridge_resource_manage(sc, tag, ioex); + } + + extent_destroy(ioex); + } +} + +void +xbridge_resource_manage(struct xbridge_softc *sc, pcitag_t tag, + struct extent *ioex) +{ + pci_chipset_tag_t pc = &sc->sc_pc; + pcireg_t bhlc, type; + bus_addr_t base; + bus_size_t size; + int reg, reg_start, reg_end; + + bhlc = pci_conf_read(pc, tag, PCI_BHLC_REG); + switch (PCI_HDRTYPE(bhlc)) { + case 0: + reg_start = PCI_MAPREG_START; + reg_end = PCI_MAPREG_END; + break; + case 1: /* PCI-PCI bridge */ + reg_start = PCI_MAPREG_START; + reg_end = PCI_MAPREG_PPB_END; + break; + case 2: /* PCI-CardBus bridge */ + reg_start = PCI_MAPREG_START; + reg_end = PCI_MAPREG_PCB_END; + break; + default: + return; + } + + for (reg = reg_start; reg < reg_end; reg += 4) { + if (pci_mapreg_probe(pc, tag, reg, &type) == 0) + continue; + + if (pci_mapreg_info(pc, tag, reg, type, &base, &size, NULL)) + continue; + + switch (type) { + case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT: + case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_64BIT: + /* + * XXX Eventually do something if sc->sc_mem_ex is + * XXX NULL... + */ + break; + case PCI_MAPREG_TYPE_IO: + if (base != 0) { + if (extent_alloc_region(ioex, base, size, + EX_NOWAIT)) + printf("io address conflict" + " 0x%x/0x%x\n", base, size); + } else { + if (extent_alloc(ioex, size, size, 0, 0, 0, + &base) == 0) + pci_conf_write(pc, tag, reg, base); + /* otherwise the resource remains disabled */ + } + break; + } + + if (type & PCI_MAPREG_MEM_TYPE_64BIT) + reg += 4; + } +} diff --git a/sys/arch/sgi/xbow/xbridgereg.h b/sys/arch/sgi/xbow/xbridgereg.h index 81675a01688..8b9e2718778 100644 --- a/sys/arch/sgi/xbow/xbridgereg.h +++ b/sys/arch/sgi/xbow/xbridgereg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: xbridgereg.h,v 1.2 2009/05/02 21:30:13 miod Exp $ */ +/* $OpenBSD: xbridgereg.h,v 1.3 2009/05/03 19:44:28 miod Exp $ */ /* * Copyright (c) 2008 Miodrag Vallat. @@ -90,7 +90,7 @@ #define BRIDGE_DEVICE_COHERENT 0x00010000 #define BRIDGE_DEVICE_BARRIER 0x00008000 #define BRIDGE_DEVICE_SWAP 0x00002000 -#define BRIDGE_DEVICE_IO 0x00001000 +#define BRIDGE_DEVICE_IO_MEM 0x00001000 /* clear if I/O */ #define BRIDGE_DEVICE_BASE_MASK 0x00000fff #define BRIDGE_DEVICE_BASE_SHIFT 20 @@ -102,6 +102,8 @@ (BRIDGE_DEVIO_BASE + \ BRIDGE_DEVIO_LARGE * ((d) < 2 ? (d) : 2) + \ BRIDGE_DEVIO_SHORT * ((d) < 2 ? 0 : (d) - 2)) +#define BRIDGE_DEVIO_SIZE(d) \ + ((d) < 2 ? BRIDGE_DEVIO_LARGE : BRIDGE_DEVIO_SHORT) /* * Read Response Buffer configuration registers |