diff options
Diffstat (limited to 'sys/powerpc/mpc85xx/pci_ocp.c')
-rw-r--r-- | sys/powerpc/mpc85xx/pci_ocp.c | 781 |
1 files changed, 781 insertions, 0 deletions
diff --git a/sys/powerpc/mpc85xx/pci_ocp.c b/sys/powerpc/mpc85xx/pci_ocp.c new file mode 100644 index 0000000..66c2d65 --- /dev/null +++ b/sys/powerpc/mpc85xx/pci_ocp.c @@ -0,0 +1,781 @@ +/*- + * Copyright 2006 by Juniper Networks. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/ktr.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/endian.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcib_private.h> + +#include "pcib_if.h" + +#include <machine/resource.h> +#include <machine/bus.h> +#include <machine/ocpbus.h> +#include <machine/spr.h> + +#include <powerpc/mpc85xx/ocpbus.h> + +#define REG_CFG_ADDR 0x0000 +#define CONFIG_ACCESS_ENABLE 0x80000000 + +#define REG_CFG_DATA 0x0004 +#define REG_INT_ACK 0x0008 + +#define REG_POTAR(n) (0x0c00 + 0x20 * (n)) +#define REG_POTEAR(n) (0x0c04 + 0x20 * (n)) +#define REG_POWBAR(n) (0x0c08 + 0x20 * (n)) +#define REG_POWAR(n) (0x0c10 + 0x20 * (n)) + +#define REG_PITAR(n) (0x0e00 - 0x20 * (n)) +#define REG_PIWBAR(n) (0x0e08 - 0x20 * (n)) +#define REG_PIWBEAR(n) (0x0e0c - 0x20 * (n)) +#define REG_PIWAR(n) (0x0e10 - 0x20 * (n)) + +struct pci_ocp_softc { + device_t sc_dev; + + struct rman sc_iomem; + bus_addr_t sc_iomem_va; /* Virtual mapping. */ + bus_addr_t sc_iomem_alloc; /* Next allocation. */ + struct rman sc_ioport; + bus_addr_t sc_ioport_va; /* Virtual mapping. */ + bus_addr_t sc_ioport_alloc; /* Next allocation. */ + + struct resource *sc_res; + bus_space_handle_t sc_bsh; + bus_space_tag_t sc_bst; + int sc_rid; + + int sc_busnr; + int sc_pcie:1; +}; + +static int pci_ocp_busnr = 0; + +static int pci_ocp_attach(device_t); +static int pci_ocp_probe(device_t); + +static struct resource *pci_ocp_alloc_resource(device_t, device_t, int, int *, + u_long, u_long, u_long, u_int); +static int pci_ocp_read_ivar(device_t, device_t, int, uintptr_t *); +static int pci_ocp_release_resource(device_t, device_t, int, int, + struct resource *); +static int pci_ocp_write_ivar(device_t, device_t, int, uintptr_t); + +static int pci_ocp_maxslots(device_t); +static uint32_t pci_ocp_read_config(device_t, u_int, u_int, u_int, u_int, int); +static void pci_ocp_write_config(device_t, u_int, u_int, u_int, u_int, + uint32_t, int); + +/* + * Bus interface definitions. + */ +static device_method_t pci_ocp_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pci_ocp_probe), + DEVMETHOD(device_attach, pci_ocp_attach), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_read_ivar, pci_ocp_read_ivar), + DEVMETHOD(bus_write_ivar, pci_ocp_write_ivar), + DEVMETHOD(bus_alloc_resource, pci_ocp_alloc_resource), + DEVMETHOD(bus_release_resource, pci_ocp_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + /* pcib interface */ + DEVMETHOD(pcib_maxslots, pci_ocp_maxslots), + DEVMETHOD(pcib_read_config, pci_ocp_read_config), + DEVMETHOD(pcib_write_config, pci_ocp_write_config), + DEVMETHOD(pcib_route_interrupt, pcib_route_interrupt), + + { 0, 0 } +}; + +static driver_t pci_ocp_driver = { + "pcib", + pci_ocp_methods, + sizeof(struct pci_ocp_softc), +}; + +devclass_t pcib_devclass; + +DRIVER_MODULE(pcib, ocpbus, pci_ocp_driver, pcib_devclass, 0, 0); + +static uint32_t +pci_ocp_cfgread(struct pci_ocp_softc *sc, u_int bus, u_int slot, u_int func, + u_int reg, int bytes) +{ + uint32_t addr, data; + + if (bus == sc->sc_busnr && !sc->sc_pcie) + bus = 0; + + addr = CONFIG_ACCESS_ENABLE; + addr |= (bus & 0xff) << 16; + addr |= (slot & 0x1f) << 11; + addr |= (func & 0x7) << 8; + addr |= reg & 0xfc; + if (sc->sc_pcie) + addr |= (reg & 0xf00) << 16; + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_CFG_ADDR, addr); + + switch (bytes) { + case 1: + data = bus_space_read_1(sc->sc_bst, sc->sc_bsh, + REG_CFG_DATA + (reg & 3)); + break; + case 2: + data = le16toh(bus_space_read_2(sc->sc_bst, sc->sc_bsh, + REG_CFG_DATA + (reg & 2))); + break; + case 4: + data = le32toh(bus_space_read_4(sc->sc_bst, sc->sc_bsh, + REG_CFG_DATA)); + break; + default: + data = ~0; + break; + } + return (data); +} + +static void +pci_ocp_cfgwrite(struct pci_ocp_softc *sc, u_int bus, u_int slot, u_int func, + u_int reg, uint32_t data, int bytes) +{ + uint32_t addr; + + if (bus == sc->sc_busnr && !sc->sc_pcie) + bus = 0; + + addr = CONFIG_ACCESS_ENABLE; + addr |= (bus & 0xff) << 16; + addr |= (slot & 0x1f) << 11; + addr |= (func & 0x7) << 8; + addr |= reg & 0xfc; + if (sc->sc_pcie) + addr |= (reg & 0xf00) << 16; + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_CFG_ADDR, addr); + + switch (bytes) { + case 1: + bus_space_write_1(sc->sc_bst, sc->sc_bsh, + REG_CFG_DATA + (reg & 3), data); + break; + case 2: + bus_space_write_2(sc->sc_bst, sc->sc_bsh, + REG_CFG_DATA + (reg & 2), htole16(data)); + break; + case 4: + bus_space_write_4(sc->sc_bst, sc->sc_bsh, + REG_CFG_DATA, htole32(data)); + break; + } +} + +#if 0 +static void +dump(struct pci_ocp_softc *sc) +{ + unsigned int i; + +#define RD(o) bus_space_read_4(sc->sc_bst, sc->sc_bsh, o) + for (i = 0; i < 5; i++) { + printf("POTAR%u =0x%08x\n", i, RD(REG_POTAR(i))); + printf("POTEAR%u =0x%08x\n", i, RD(REG_POTEAR(i))); + printf("POWBAR%u =0x%08x\n", i, RD(REG_POWBAR(i))); + printf("POWAR%u =0x%08x\n", i, RD(REG_POWAR(i))); + } + printf("\n"); + for (i = 1; i < 4; i++) { + printf("PITAR%u =0x%08x\n", i, RD(REG_PITAR(i))); + printf("PIWBAR%u =0x%08x\n", i, RD(REG_PIWBAR(i))); + printf("PIWBEAR%u=0x%08x\n", i, RD(REG_PIWBEAR(i))); + printf("PIWAR%u =0x%08x\n", i, RD(REG_PIWAR(i))); + } + printf("\n"); +#undef RD + + for (i = 0; i < 0x48; i += 4) { + printf("cfg%02x=0x%08x\n", i, pci_ocp_cfgread(sc, 0, 0, 0, + i, 4)); + } +} +#endif + +static int +pci_ocp_maxslots(device_t dev) +{ + struct pci_ocp_softc *sc = device_get_softc(dev); + + return ((sc->sc_pcie) ? 0 : 30); +} + +static uint32_t +pci_ocp_read_config(device_t dev, u_int bus, u_int slot, u_int func, + u_int reg, int bytes) +{ + struct pci_ocp_softc *sc = device_get_softc(dev); + + if (bus == sc->sc_busnr && !sc->sc_pcie && slot < 10) + return (~0); + return (pci_ocp_cfgread(sc, bus, slot, func, reg, bytes)); +} + +static void +pci_ocp_write_config(device_t dev, u_int bus, u_int slot, u_int func, + u_int reg, uint32_t val, int bytes) +{ + struct pci_ocp_softc *sc = device_get_softc(dev); + + if (bus == sc->sc_busnr && !sc->sc_pcie && slot < 10) + return; + pci_ocp_cfgwrite(sc, bus, slot, func, reg, val, bytes); +} + +static int +pci_ocp_probe(device_t dev) +{ + char buf[128]; + struct pci_ocp_softc *sc; + const char *mpcid, *type; + device_t parent; + u_long start, size; + uintptr_t devtype; + uint32_t cfgreg; + int error; + + parent = device_get_parent(dev); + error = BUS_READ_IVAR(parent, dev, OCPBUS_IVAR_DEVTYPE, &devtype); + if (error) + return (error); + if (devtype != OCPBUS_DEVTYPE_PCIB) + return (ENXIO); + + sc = device_get_softc(dev); + + sc->sc_rid = 0; + sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, + RF_ACTIVE); + if (sc->sc_res == NULL) + return (ENXIO); + + sc->sc_bst = rman_get_bustag(sc->sc_res); + sc->sc_bsh = rman_get_bushandle(sc->sc_res); + sc->sc_busnr = 0; + + error = ENOENT; + cfgreg = pci_ocp_cfgread(sc, 0, 0, 0, PCIR_VENDOR, 2); + if (cfgreg != 0x1057 && cfgreg != 0x1957) + goto out; + cfgreg = pci_ocp_cfgread(sc, 0, 0, 0, PCIR_DEVICE, 2); + switch (cfgreg) { + case 0x000a: + mpcid = "8555E"; + break; + case 0x0012: + mpcid = "8548E"; + break; + case 0x0013: + mpcid = "8548"; + break; + /* + * Documentation from Freescale is incorrect. + * Use right values after documentation is corrected. + */ + case 0x0030: + mpcid = "8544E"; + break; + case 0x0031: + mpcid = "8544"; + break; + case 0x0032: + mpcid = "8544"; + break; + default: + goto out; + } + + type = "PCI"; + cfgreg = pci_ocp_cfgread(sc, 0, 0, 0, PCIR_CAP_PTR, 1); + while (cfgreg != 0) { + cfgreg = pci_ocp_cfgread(sc, 0, 0, 0, cfgreg, 2); + switch (cfgreg & 0xff) { + case PCIY_PCIX: /* PCI-X */ + type = "PCI-X"; + break; + case PCIY_EXPRESS: /* PCI Express */ + type = "PCI Express"; + sc->sc_pcie = 1; + break; + } + cfgreg = (cfgreg >> 8) & 0xff; + } + + bus_get_resource(dev, SYS_RES_MEMORY, 1, &start, &size); + if (start == 0 || size == 0) + goto out; + + snprintf(buf, sizeof(buf), + "Freescale MPC%s %s host controller", mpcid, type); + device_set_desc_copy(dev, buf); + error = BUS_PROBE_DEFAULT; + +out: + bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); + return (error); +} + +static int +pci_ocp_init_bar(struct pci_ocp_softc *sc, int bus, int slot, int func, + int barno) +{ + bus_addr_t *allocp; + uint32_t addr, bar, mask, size; + int reg, width; + + reg = PCIR_BAR(barno); + bar = pci_ocp_read_config(sc->sc_dev, bus, slot, func, reg, 4); + if (bar == 0) + return (1); + width = ((bar & 7) == 4) ? 2 : 1; + + pci_ocp_write_config(sc->sc_dev, bus, slot, func, reg, ~0, 4); + size = pci_ocp_read_config(sc->sc_dev, bus, slot, func, reg, 4); + + if (bar & 1) { /* I/O port */ + allocp = &sc->sc_ioport_alloc; + size &= ~3; + if ((size & 0xffff0000) == 0) + size |= 0xffff0000; + } else { /* memory */ + allocp = &sc->sc_iomem_alloc; + size &= ~15; + } + mask = ~size; + size = mask + 1; + /* Sanity check (must be a power of 2). */ + if (size & mask) + return (width); + + addr = (*allocp + mask) & ~mask; + *allocp = addr + size; + + if (bootverbose) + printf("PCI %u:%u:%u: reg %x: size=%08x: addr=%08x\n", + bus, slot, func, reg, size, addr); + + pci_ocp_write_config(sc->sc_dev, bus, slot, func, reg, addr, 4); + if (width == 2) + pci_ocp_write_config(sc->sc_dev, bus, slot, func, reg + 4, + 0, 4); + return (width); +} + +static void +pci_ocp_init(struct pci_ocp_softc *sc, int bus) +{ + int slot, maxslot; + int func, maxfunc; + int bar, maxbar; + uint16_t vendor, device; + uint8_t cr8, command, hdrtype, class, subclass; + uint8_t intline, intpin; + + maxslot = (sc->sc_pcie) ? 1 : 31; + for (slot = 0; slot < maxslot; slot++) { + maxfunc = 0; + for (func = 0; func <= maxfunc; func++) { + hdrtype = pci_ocp_read_config(sc->sc_dev, bus, slot, + func, PCIR_HDRTYPE, 1); + if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) + continue; + + if (func == 0 && (hdrtype & PCIM_MFDEV)) + maxfunc = PCI_FUNCMAX; + + command = pci_ocp_read_config(sc->sc_dev, bus, slot, + func, PCIR_COMMAND, 1); + command &= ~(PCIM_CMD_MEMEN | PCIM_CMD_PORTEN); + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_COMMAND, command, 1); + + vendor = pci_ocp_read_config(sc->sc_dev, bus, slot, + func, PCIR_VENDOR, 2); + device = pci_ocp_read_config(sc->sc_dev, bus, slot, + func, PCIR_DEVICE, 2); + + /* + * Make sure the ATA controller on the VIA82C686 + * South bridge is enabled. + */ + if (vendor == 0x1106 && device == 0x0686) { + /* Enable the ATA controller. */ + cr8 = pci_ocp_read_config(sc->sc_dev, bus, + slot, func, 0x48, 1); + if (cr8 & 2) { + device_printf(sc->sc_dev, + "enabling ATA controller\n"); + pci_ocp_write_config(sc->sc_dev, bus, + slot, func, 0x48, cr8 & ~2, 1); + } + } + if (vendor == 0x1106 && device == 0x0571) { + pci_ocp_write_config(sc->sc_dev, bus, slot, + func, 0xc4, 0x00, 1); + /* Set legacy mode. */ + pci_ocp_write_config(sc->sc_dev, bus, slot, + func, 0x40, 0x08, 1); + pci_ocp_write_config(sc->sc_dev, bus, slot, + func, PCIR_PROGIF, 0x00, 1); + pci_ocp_write_config(sc->sc_dev, bus, slot, + func, 0x42, 0x09, 1); + pci_ocp_write_config(sc->sc_dev, bus, slot, + func, 0x40, 0x0b, 1); + } + + /* Program the base address registers. */ + maxbar = (hdrtype & PCIM_HDRTYPE) ? 0 : 6; + bar = 0; + while (bar < maxbar) + bar += pci_ocp_init_bar(sc, bus, slot, func, + bar); + + /* Perform interrupt routing. */ + intpin = pci_ocp_read_config(sc->sc_dev, bus, slot, + func, PCIR_INTPIN, 1); + if (intpin != 0) { + intline = intpin - 1; + intline += (bus != sc->sc_busnr) ? slot : 0; + intline = PIC_IRQ_EXT(intline & 3); + } else + intline = 0xff; + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_INTLINE, intline, 1); + + command |= PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN | + PCIM_CMD_PORTEN; + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_COMMAND, command, 1); + + /* + * Handle PCI-PCI bridges + */ + class = pci_ocp_read_config(sc->sc_dev, bus, slot, + func, PCIR_CLASS, 1); + if (class != PCIC_BRIDGE) + continue; + subclass = pci_ocp_read_config(sc->sc_dev, bus, slot, + func, PCIR_SUBCLASS, 1); + if (subclass != PCIS_BRIDGE_PCI) + continue; + + /* Program I/O decoder. */ + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_IOBASEL_1, sc->sc_ioport.rm_start >> 8, 1); + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_IOLIMITL_1, sc->sc_ioport.rm_end >> 8, 1); + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_IOBASEH_1, sc->sc_ioport.rm_start >> 16, 2); + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_IOLIMITH_1, sc->sc_ioport.rm_end >> 16, 2); + + /* Program (non-prefetchable) memory decoder. */ + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_MEMBASE_1, sc->sc_iomem.rm_start >> 16, 2); + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_MEMLIMIT_1, sc->sc_iomem.rm_end >> 16, 2); + + /* Program prefetchable memory decoder. */ + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_PMBASEL_1, 0x0010, 2); + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_PMLIMITL_1, 0x000f, 2); + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_PMBASEH_1, 0x00000000, 4); + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_PMLIMITH_1, 0x00000000, 4); + + pci_ocp_busnr++; + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_PRIBUS_1, bus, 1); + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_SECBUS_1, pci_ocp_busnr, 1); + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_SUBBUS_1, 0xff, 1); + + pci_ocp_init(sc, pci_ocp_busnr); + + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_SUBBUS_1, pci_ocp_busnr, 1); + } + } +} + +static void +pci_ocp_inbound(struct pci_ocp_softc *sc, int wnd, int tgt, u_long start, + u_long size, u_long pci_start) +{ + uint32_t attr, bar, tar; + + KASSERT(wnd > 0, ("%s: inbound window 0 is invalid", __func__)); + + switch (tgt) { + case OCP85XX_TGTIF_RAM: + attr = 0xa0f55000 | (ffsl(size) - 2); + break; + default: + attr = 0; + break; + } + tar = start >> 12; + bar = pci_start >> 12; + + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PITAR(wnd), tar); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWBEAR(wnd), 0); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWBAR(wnd), bar); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWAR(wnd), attr); +} + +static void +pci_ocp_outbound(struct pci_ocp_softc *sc, int wnd, int res, u_long start, + u_long size, u_long pci_start) +{ + uint32_t attr, bar, tar; + + switch (res) { + case SYS_RES_MEMORY: + attr = 0x80044000 | (ffsl(size) - 2); + break; + case SYS_RES_IOPORT: + attr = 0x80088000 | (ffsl(size) - 2); + break; + default: + attr = 0x0004401f; + break; + } + bar = start >> 12; + tar = pci_start >> 12; + + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POTAR(wnd), tar); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POTEAR(wnd), 0); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POWBAR(wnd), bar); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POWAR(wnd), attr); +} + +static int +pci_ocp_iorange(struct pci_ocp_softc *sc, int type, int wnd) +{ + struct rman *rm; + u_long start, end, size, alloc; + bus_addr_t pci_start, pci_end; + bus_addr_t *vap, *allocp; + int error; + + bus_get_resource(sc->sc_dev, type, 1, &start, &size); + end = start + size - 1; + + switch (type) { + case SYS_RES_IOPORT: + rm = &sc->sc_ioport; + pci_start = 0x0000; + pci_end = 0xffff; + alloc = 0x1000; + vap = &sc->sc_ioport_va; + allocp = &sc->sc_ioport_alloc; + break; + case SYS_RES_MEMORY: + rm = &sc->sc_iomem; + pci_start = start; + pci_end = end; + alloc = 0; + vap = &sc->sc_iomem_va; + allocp = &sc->sc_iomem_alloc; + break; + default: + return (EINVAL); + } + + rm->rm_type = RMAN_ARRAY; + rm->rm_start = pci_start; + rm->rm_end = pci_end; + error = rman_init(rm); + if (error) + return (error); + + error = rman_manage_region(rm, pci_start, pci_end); + if (error) { + rman_fini(rm); + return (error); + } + + *allocp = pci_start + alloc; + *vap = (uintptr_t)pmap_mapdev(start, size); + pci_ocp_outbound(sc, wnd, type, start, size, pci_start); + return (0); +} + +static int +pci_ocp_attach(device_t dev) +{ + struct pci_ocp_softc *sc; + uint32_t cfgreg; + int error; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + + sc->sc_rid = 0; + sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, + RF_ACTIVE); + if (sc->sc_res == NULL) { + device_printf(dev, "could not map I/O memory\n"); + return (ENXIO); + } + sc->sc_bst = rman_get_bustag(sc->sc_res); + sc->sc_bsh = rman_get_bushandle(sc->sc_res); + + cfgreg = pci_ocp_cfgread(sc, 0, 0, 0, PCIR_COMMAND, 2); + cfgreg |= PCIM_CMD_SERRESPEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN | + PCIM_CMD_PORTEN; + pci_ocp_cfgwrite(sc, 0, 0, 0, PCIR_COMMAND, cfgreg, 2); + + pci_ocp_outbound(sc, 0, -1, 0, 0, 0); + error = pci_ocp_iorange(sc, SYS_RES_MEMORY, 1); + error = pci_ocp_iorange(sc, SYS_RES_IOPORT, 2); + pci_ocp_outbound(sc, 3, -1, 0, 0, 0); + pci_ocp_outbound(sc, 4, -1, 0, 0, 0); + + pci_ocp_inbound(sc, 1, -1, 0, 0, 0); + pci_ocp_inbound(sc, 2, -1, 0, 0, 0); + pci_ocp_inbound(sc, 3, OCP85XX_TGTIF_RAM, 0, 2U*1024U*1024U*1024U, 0); + + sc->sc_busnr = pci_ocp_busnr; + pci_ocp_init(sc, sc->sc_busnr); + pci_ocp_busnr++; + + device_add_child(dev, "pci", -1); + return (bus_generic_attach(dev)); +} + +static struct resource * +pci_ocp_alloc_resource(device_t dev, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + struct pci_ocp_softc *sc = device_get_softc(dev); + struct rman *rm; + struct resource *res; + bus_addr_t va; + + switch (type) { + case SYS_RES_IOPORT: + rm = &sc->sc_ioport; + va = sc->sc_ioport_va; + break; + case SYS_RES_MEMORY: + rm = &sc->sc_iomem; + va = sc->sc_iomem_va; + break; + case SYS_RES_IRQ: + /* ISA interrupts are routed to IRQ 0 on the PIC. */ + if (start < PIC_IRQ_START) { + device_printf(dev, "%s requested ISA interrupt %lu\n", + device_get_nameunit(child), start); + /* XXX */ + start = PIC_IRQ_EXT(0); + } + return (BUS_ALLOC_RESOURCE(device_get_parent(dev), child, + type, rid, start, end, count, flags)); + default: + return (NULL); + } + + res = rman_reserve_resource(rm, start, end, count, flags, child); + if (res == NULL) + return (NULL); + + rman_set_bustag(res, &bs_le_tag); + rman_set_bushandle(res, va + rman_get_start(res) - rm->rm_start); + return (res); +} + +static int +pci_ocp_release_resource(device_t dev, device_t child, int type, int rid, + struct resource *res) +{ + + return (rman_release_resource(res)); +} + +static int +pci_ocp_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) +{ + struct pci_ocp_softc *sc = device_get_softc(dev); + + switch (which) { + case PCIB_IVAR_BUS: + *result = sc->sc_busnr; + return (0); + } + return (ENOENT); +} + +static int +pci_ocp_write_ivar(device_t dev, device_t child, int which, uintptr_t value) +{ + struct pci_ocp_softc *sc = device_get_softc(dev); + + switch (which) { + case PCIB_IVAR_BUS: + sc->sc_busnr = value; + return (0); + } + return (ENOENT); +} |