diff options
Diffstat (limited to 'sys/mips/cavium/octopci.c')
-rw-r--r-- | sys/mips/cavium/octopci.c | 884 |
1 files changed, 884 insertions, 0 deletions
diff --git a/sys/mips/cavium/octopci.c b/sys/mips/cavium/octopci.c new file mode 100644 index 000000000000..d065ae399ccf --- /dev/null +++ b/sys/mips/cavium/octopci.c @@ -0,0 +1,884 @@ +/*- + * Copyright (c) 2010 Juli Mallett <jmallett@FreeBSD.org> + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> + +#include <sys/bus.h> +#include <sys/endian.h> +#include <sys/interrupt.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/rman.h> + +#include <vm/vm.h> +#include <vm/pmap.h> +#include <vm/vm_extern.h> + +#include <machine/bus.h> +#include <machine/cpu.h> +#include <machine/pmap.h> + +#include <contrib/octeon-sdk/cvmx.h> +#include <contrib/octeon-sdk/cvmx-interrupt.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> + +#include <dev/pci/pcib_private.h> + +#include <mips/cavium/octopcireg.h> +#include <mips/cavium/octopcivar.h> + +#include "pcib_if.h" + +#define NPI_WRITE(addr, value) cvmx_write64_uint32((addr) ^ 4, (value)) +#define NPI_READ(addr) cvmx_read64_uint32((addr) ^ 4) + +struct octopci_softc { + device_t sc_dev; + + unsigned sc_domain; + unsigned sc_bus; + + unsigned sc_io_next; + struct rman sc_io; + + unsigned sc_mem1_next; + struct rman sc_mem1; +}; + +static void octopci_identify(driver_t *, device_t); +static int octopci_probe(device_t); +static int octopci_attach(device_t); +static int octopci_read_ivar(device_t, device_t, int, + uintptr_t *); +static struct resource *octopci_alloc_resource(device_t, device_t, int, int *, + u_long, u_long, u_long, u_int); +static int octopci_activate_resource(device_t, device_t, int, int, + struct resource *); +static int octopci_maxslots(device_t); +static uint32_t octopci_read_config(device_t, u_int, u_int, u_int, u_int, int); +static void octopci_write_config(device_t, u_int, u_int, u_int, u_int, + uint32_t, int); +static int octopci_route_interrupt(device_t, device_t, int); + +static void octopci_init_bar(device_t, unsigned, unsigned, unsigned, unsigned, uint8_t *); +static unsigned octopci_init_device(device_t, unsigned, unsigned, unsigned, unsigned); +static unsigned octopci_init_bus(device_t, unsigned); +static uint64_t octopci_cs_addr(unsigned, unsigned, unsigned, unsigned); + +static void +octopci_identify(driver_t *drv, device_t parent) +{ + BUS_ADD_CHILD(parent, 0, "pcib", 0); +} + +static int +octopci_probe(device_t dev) +{ + if (device_get_unit(dev) != 0) + return (ENXIO); + if (octeon_has_feature(OCTEON_FEATURE_PCIE)) + return (ENXIO); + /* XXX Check sysinfo flag. */ + device_set_desc(dev, "Cavium Octeon PCI bridge"); + return (0); +} + +static int +octopci_attach(device_t dev) +{ + struct octopci_softc *sc; + cvmx_npi_mem_access_subid_t npi_mem_access_subid; + cvmx_npi_pci_int_arb_cfg_t npi_pci_int_arb_cfg; + cvmx_npi_ctl_status_t npi_ctl_status; + cvmx_pci_ctl_status_2_t pci_ctl_status_2; + cvmx_pci_cfg56_t pci_cfg56; + cvmx_pci_cfg22_t pci_cfg22; + cvmx_pci_cfg16_t pci_cfg16; + cvmx_pci_cfg19_t pci_cfg19; + cvmx_pci_cfg01_t pci_cfg01; + unsigned subbus; + unsigned i; + int error; + + /* + * Reset the PCI bus. + */ + cvmx_write_csr(CVMX_CIU_SOFT_PRST, 0x1); + cvmx_read_csr(CVMX_CIU_SOFT_PRST); + + DELAY(2000); + + npi_ctl_status.u64 = 0; + npi_ctl_status.s.max_word = 1; + npi_ctl_status.s.timer = 1; + cvmx_write_csr(CVMX_NPI_CTL_STATUS, npi_ctl_status.u64); + + /* + * Set host mode. + */ + switch (cvmx_sysinfo_get()->board_type) { +#if defined(OCTEON_VENDOR_LANNER) + case CVMX_BOARD_TYPE_CUST_LANNER_MR320: + case CVMX_BOARD_TYPE_CUST_LANNER_MR955: + /* 32-bit PCI-X */ + cvmx_write_csr(CVMX_CIU_SOFT_PRST, 0x0); + break; +#endif + default: + /* 64-bit PCI-X */ + cvmx_write_csr(CVMX_CIU_SOFT_PRST, 0x4); + break; + } + cvmx_read_csr(CVMX_CIU_SOFT_PRST); + + DELAY(2000); + + /* + * Enable BARs and configure big BAR mode. + */ + pci_ctl_status_2.u32 = 0; + pci_ctl_status_2.s.bb1_hole = 5; /* 256MB hole in BAR1 */ + pci_ctl_status_2.s.bb1_siz = 1; /* BAR1 is 2GB */ + pci_ctl_status_2.s.bb_ca = 1; /* Bypass cache for big BAR */ + pci_ctl_status_2.s.bb_es = 1; /* Do big BAR byte-swapping */ + pci_ctl_status_2.s.bb1 = 1; /* BAR1 is big */ + pci_ctl_status_2.s.bb0 = 1; /* BAR0 is big */ + pci_ctl_status_2.s.bar2pres = 1; /* BAR2 present */ + pci_ctl_status_2.s.pmo_amod = 1; /* Round-robin priority */ + pci_ctl_status_2.s.tsr_hwm = 1; + pci_ctl_status_2.s.bar2_enb = 1; /* Enable BAR2 */ + pci_ctl_status_2.s.bar2_esx = 1; /* Do BAR2 byte-swapping */ + pci_ctl_status_2.s.bar2_cax = 1; /* Bypass cache for BAR2 */ + + NPI_WRITE(CVMX_NPI_PCI_CTL_STATUS_2, pci_ctl_status_2.u32); + + DELAY(2000); + + pci_ctl_status_2.u32 = NPI_READ(CVMX_NPI_PCI_CTL_STATUS_2); + + device_printf(dev, "%u-bit PCI%s bus.\n", + pci_ctl_status_2.s.ap_64ad ? 64 : 32, + pci_ctl_status_2.s.ap_pcix ? "-X" : ""); + + /* + * Set up transaction splitting, etc., parameters. + */ + pci_cfg19.u32 = 0; + pci_cfg19.s.mrbcm = 1; + if (pci_ctl_status_2.s.ap_pcix) { + pci_cfg19.s.mdrrmc = 0; + pci_cfg19.s.tdomc = 4; + } else { + pci_cfg19.s.mdrrmc = 2; + pci_cfg19.s.tdomc = 1; + } + NPI_WRITE(CVMX_NPI_PCI_CFG19, pci_cfg19.u32); + NPI_READ(CVMX_NPI_PCI_CFG19); + + /* + * Set up PCI error handling and memory access. + */ + pci_cfg01.u32 = 0; + pci_cfg01.s.fbbe = 1; + pci_cfg01.s.see = 1; + pci_cfg01.s.pee = 1; + pci_cfg01.s.me = 1; + pci_cfg01.s.msae = 1; + if (pci_ctl_status_2.s.ap_pcix) { + pci_cfg01.s.fbb = 0; + } else { + pci_cfg01.s.fbb = 1; + } + NPI_WRITE(CVMX_NPI_PCI_CFG01, pci_cfg01.u32); + NPI_READ(CVMX_NPI_PCI_CFG01); + + /* + * Enable the Octeon bus arbiter. + */ + npi_pci_int_arb_cfg.u64 = 0; + npi_pci_int_arb_cfg.s.en = 1; + cvmx_write_csr(CVMX_NPI_PCI_INT_ARB_CFG, npi_pci_int_arb_cfg.u64); + + /* + * Disable master latency timer. + */ + pci_cfg16.u32 = 0; + pci_cfg16.s.mltd = 1; + NPI_WRITE(CVMX_NPI_PCI_CFG16, pci_cfg16.u32); + NPI_READ(CVMX_NPI_PCI_CFG16); + + /* + * Configure master arbiter. + */ + pci_cfg22.u32 = 0; + pci_cfg22.s.flush = 1; + pci_cfg22.s.mrv = 255; + NPI_WRITE(CVMX_NPI_PCI_CFG22, pci_cfg22.u32); + NPI_READ(CVMX_NPI_PCI_CFG22); + + /* + * Set up PCI-X capabilities. + */ + if (pci_ctl_status_2.s.ap_pcix) { + pci_cfg56.u32 = 0; + pci_cfg56.s.most = 3; + pci_cfg56.s.roe = 1; /* Enable relaxed ordering */ + pci_cfg56.s.dpere = 1; + pci_cfg56.s.ncp = 0xe8; + pci_cfg56.s.pxcid = 7; + NPI_WRITE(CVMX_NPI_PCI_CFG56, pci_cfg56.u32); + NPI_READ(CVMX_NPI_PCI_CFG56); + } + + NPI_WRITE(CVMX_NPI_PCI_READ_CMD_6, 0x22); + NPI_READ(CVMX_NPI_PCI_READ_CMD_6); + NPI_WRITE(CVMX_NPI_PCI_READ_CMD_C, 0x33); + NPI_READ(CVMX_NPI_PCI_READ_CMD_C); + NPI_WRITE(CVMX_NPI_PCI_READ_CMD_E, 0x33); + NPI_READ(CVMX_NPI_PCI_READ_CMD_E); + + /* + * Configure MEM1 sub-DID access. + */ + npi_mem_access_subid.u64 = 0; + npi_mem_access_subid.s.esr = 1; /* Byte-swap on read */ + npi_mem_access_subid.s.esw = 1; /* Byte-swap on write */ + switch (cvmx_sysinfo_get()->board_type) { +#if defined(OCTEON_VENDOR_LANNER) + case CVMX_BOARD_TYPE_CUST_LANNER_MR955: + npi_mem_access_subid.s.shortl = 1; + break; +#endif + default: + break; + } + cvmx_write_csr(CVMX_NPI_MEM_ACCESS_SUBID3, npi_mem_access_subid.u64); + + /* + * Configure BAR2. Linux says this has to come first. + */ + NPI_WRITE(CVMX_NPI_PCI_CFG08, 0x00000000); + NPI_READ(CVMX_NPI_PCI_CFG08); + NPI_WRITE(CVMX_NPI_PCI_CFG09, 0x00000080); + NPI_READ(CVMX_NPI_PCI_CFG09); + + /* + * Disable BAR1 IndexX. + */ + for (i = 0; i < 32; i++) { + NPI_WRITE(CVMX_NPI_PCI_BAR1_INDEXX(i), 0); + NPI_READ(CVMX_NPI_PCI_BAR1_INDEXX(i)); + } + + /* + * Configure BAR0 and BAR1. + */ + NPI_WRITE(CVMX_NPI_PCI_CFG04, 0x00000000); + NPI_READ(CVMX_NPI_PCI_CFG04); + NPI_WRITE(CVMX_NPI_PCI_CFG05, 0x00000000); + NPI_READ(CVMX_NPI_PCI_CFG05); + + NPI_WRITE(CVMX_NPI_PCI_CFG06, 0x80000000); + NPI_READ(CVMX_NPI_PCI_CFG06); + NPI_WRITE(CVMX_NPI_PCI_CFG07, 0x00000000); + NPI_READ(CVMX_NPI_PCI_CFG07); + + /* + * Clear PCI interrupts. + */ + cvmx_write_csr(CVMX_NPI_PCI_INT_SUM2, 0xffffffffffffffffull); + + sc = device_get_softc(dev); + sc->sc_dev = dev; + sc->sc_domain = 0; + sc->sc_bus = 0; + + sc->sc_io.rm_type = RMAN_ARRAY; + sc->sc_io.rm_descr = "Cavium Octeon PCI I/O Ports"; + error = rman_init(&sc->sc_io); + if (error != 0) + return (error); + + error = rman_manage_region(&sc->sc_io, CVMX_OCT_PCI_IO_BASE, + CVMX_OCT_PCI_IO_BASE + CVMX_OCT_PCI_IO_SIZE); + if (error != 0) + return (error); + + sc->sc_mem1.rm_type = RMAN_ARRAY; + sc->sc_mem1.rm_descr = "Cavium Octeon PCI Memory"; + error = rman_init(&sc->sc_mem1); + if (error != 0) + return (error); + + error = rman_manage_region(&sc->sc_mem1, CVMX_OCT_PCI_MEM1_BASE, + CVMX_OCT_PCI_MEM1_BASE + CVMX_OCT_PCI_MEM1_SIZE); + if (error != 0) + return (error); + + /* + * Next offsets for resource allocation in octopci_init_bar. + */ + sc->sc_io_next = 0; + sc->sc_mem1_next = 0; + + /* + * Configure devices. + */ + octopci_write_config(dev, 0, 0, 0, PCIR_SUBBUS_1, 0xff, 1); + subbus = octopci_init_bus(dev, 0); + octopci_write_config(dev, 0, 0, 0, PCIR_SUBBUS_1, subbus, 1); + + device_add_child(dev, "pci", 0); + + return (bus_generic_attach(dev)); +} + +static int +octopci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) +{ + struct octopci_softc *sc; + + sc = device_get_softc(dev); + + switch (which) { + case PCIB_IVAR_DOMAIN: + *result = sc->sc_domain; + return (0); + case PCIB_IVAR_BUS: + *result = sc->sc_bus; + return (0); + + } + return (ENOENT); +} + +static struct resource * +octopci_alloc_resource(device_t bus, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + struct octopci_softc *sc; + struct resource *res; + struct rman *rm; + int error; + + sc = device_get_softc(bus); + + switch (type) { + case SYS_RES_IRQ: + res = bus_generic_alloc_resource(bus, child, type, rid, start, + end, count, flags); + if (res != NULL) + return (res); + return (NULL); + case SYS_RES_MEMORY: + rm = &sc->sc_mem1; + break; + case SYS_RES_IOPORT: + rm = &sc->sc_io; + break; + default: + return (NULL); + } + + res = rman_reserve_resource(rm, start, end, count, flags, child); + if (res == NULL) + return (NULL); + + rman_set_rid(res, *rid); + rman_set_bustag(res, octopci_bus_space); + + switch (type) { + case SYS_RES_MEMORY: + rman_set_bushandle(res, CVMX_ADDR_DID(CVMX_FULL_DID(CVMX_OCT_DID_PCI, CVMX_OCT_SUBDID_PCI_MEM1)) + rman_get_start(res)); + break; + case SYS_RES_IOPORT: + rman_set_bushandle(res, CVMX_ADDR_DID(CVMX_FULL_DID(CVMX_OCT_DID_PCI, CVMX_OCT_SUBDID_PCI_IO)) + rman_get_start(res)); +#if __mips_n64 + rman_set_virtual(res, (void *)rman_get_bushandle(res)); +#else + /* + * XXX + * We can't access ports via a 32-bit pointer. + */ + rman_set_virtual(res, NULL); +#endif + break; + } + + if ((flags & RF_ACTIVE) != 0) { + error = bus_activate_resource(child, type, *rid, res); + if (error != 0) { + rman_release_resource(res); + return (NULL); + } + } + + return (res); +} + +static int +octopci_activate_resource(device_t bus, device_t child, int type, int rid, + struct resource *res) +{ + bus_space_handle_t bh; + int error; + + switch (type) { + case SYS_RES_IRQ: + error = bus_generic_activate_resource(bus, child, type, rid, + res); + if (error != 0) + return (error); + return (0); + case SYS_RES_MEMORY: + case SYS_RES_IOPORT: + error = bus_space_map(rman_get_bustag(res), + rman_get_bushandle(res), rman_get_size(res), 0, &bh); + if (error != 0) + return (error); + rman_set_bushandle(res, bh); + break; + default: + return (ENXIO); + } + + error = rman_activate_resource(res); + if (error != 0) + return (error); + return (0); +} + +static int +octopci_maxslots(device_t dev) +{ + return (PCI_SLOTMAX); +} + +static uint32_t +octopci_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, + int bytes) +{ + struct octopci_softc *sc; + uint64_t addr; + uint32_t data; + + sc = device_get_softc(dev); + + addr = octopci_cs_addr(bus, slot, func, reg); + + switch (bytes) { + case 4: + data = le32toh(cvmx_read64_uint32(addr)); + return (data); + case 2: + data = le16toh(cvmx_read64_uint16(addr)); + return (data); + case 1: + data = cvmx_read64_uint8(addr); + return (data); + default: + return ((uint32_t)-1); + } +} + +static void +octopci_write_config(device_t dev, u_int bus, u_int slot, u_int func, + u_int reg, uint32_t data, int bytes) +{ + struct octopci_softc *sc; + uint64_t addr; + + sc = device_get_softc(dev); + + addr = octopci_cs_addr(bus, slot, func, reg); + + switch (bytes) { + case 4: + cvmx_write64_uint32(addr, htole32(data)); + return; + case 2: + cvmx_write64_uint16(addr, htole16(data)); + return; + case 1: + cvmx_write64_uint8(addr, data); + return; + default: + return; + } +} + +static int +octopci_route_interrupt(device_t dev, device_t child, int pin) +{ + struct octopci_softc *sc; + unsigned bus, slot, func; + unsigned irq; + + sc = device_get_softc(dev); + + bus = pci_get_bus(child); + slot = pci_get_slot(child); + func = pci_get_function(child); + + /* + * Board types we have to know at compile-time. + */ +#if defined(OCTEON_BOARD_CAPK_0100ND) + if (bus == 0 && slot == 12 && func == 0) + return (CVMX_IRQ_PCI_INT2); +#endif + + /* + * For board types we can determine at runtime. + */ + switch (cvmx_sysinfo_get()->board_type) { +#if defined(OCTEON_VENDOR_LANNER) + case CVMX_BOARD_TYPE_CUST_LANNER_MR955: + return (CVMX_IRQ_PCI_INT0 + pin - 1); + case CVMX_BOARD_TYPE_CUST_LANNER_MR320: + if (slot < 32) { + if (slot == 3 || slot == 9) + irq = pin; + else + irq = pin - 1; + return (CVMX_IRQ_PCI_INT0 + (irq & 3)); + } + break; +#endif + default: + break; + } + + irq = slot + pin - 3; + + return (CVMX_IRQ_PCI_INT0 + (irq & 3)); +} + +static void +octopci_init_bar(device_t dev, unsigned b, unsigned s, unsigned f, unsigned barnum, uint8_t *commandp) +{ + struct octopci_softc *sc; + uint32_t bar; + unsigned size; + + sc = device_get_softc(dev); + + octopci_write_config(dev, b, s, f, PCIR_BAR(barnum), 0xffffffff, 4); + bar = octopci_read_config(dev, b, s, f, PCIR_BAR(barnum), 4); + + if (bar == 0) { + /* Bar not implemented. */ + return; + } + + /* XXX Some of this is wrong for 64-bit busses. */ + + if (PCI_BAR_IO(bar)) { + size = ~(bar & PCIM_BAR_IO_BASE) + 1; + + sc->sc_io_next = (sc->sc_io_next + size - 1) & ~(size - 1); + if (sc->sc_io_next + size > CVMX_OCT_PCI_IO_SIZE) { + device_printf(dev, "%02x.%02x:%02x: no ports for BAR%u.\n", + b, s, f, barnum); + return; + } + octopci_write_config(dev, b, s, f, PCIR_BAR(barnum), + CVMX_OCT_PCI_IO_BASE + sc->sc_io_next, 4); + sc->sc_io_next += size; + + /* + * Enable I/O ports. + */ + *commandp |= PCIM_CMD_PORTEN; + } else { + size = ~(bar & (uint32_t)PCIM_BAR_MEM_BASE) + 1; + + sc->sc_mem1_next = (sc->sc_mem1_next + size - 1) & ~(size - 1); + if (sc->sc_mem1_next + size > CVMX_OCT_PCI_MEM1_SIZE) { + device_printf(dev, "%02x.%02x:%02x: no memory for BAR%u.\n", + b, s, f, barnum); + return; + } + octopci_write_config(dev, b, s, f, PCIR_BAR(barnum), + CVMX_OCT_PCI_MEM1_BASE + sc->sc_mem1_next, 4); + sc->sc_mem1_next += size; + + /* + * Enable memory access. + */ + *commandp |= PCIM_CMD_MEMEN; + } +} + +static unsigned +octopci_init_device(device_t dev, unsigned b, unsigned s, unsigned f, unsigned secbus) +{ + unsigned barnum, bars; + uint8_t brctl; + uint8_t class, subclass; + uint8_t command; + uint8_t hdrtype; + + /* Read header type (again.) */ + hdrtype = octopci_read_config(dev, b, s, f, PCIR_HDRTYPE, 1); + + /* + * Disable memory and I/O while programming BARs. + */ + command = octopci_read_config(dev, b, s, f, PCIR_COMMAND, 1); + command &= ~(PCIM_CMD_MEMEN | PCIM_CMD_PORTEN); + octopci_write_config(dev, b, s, f, PCIR_COMMAND, command, 1); + + DELAY(10000); + + /* Program BARs. */ + switch (hdrtype & PCIM_HDRTYPE) { + case PCIM_HDRTYPE_NORMAL: + bars = 6; + break; + case PCIM_HDRTYPE_BRIDGE: + bars = 2; + break; + case PCIM_HDRTYPE_CARDBUS: + bars = 0; + break; + default: + device_printf(dev, "%02x.%02x:%02x: invalid header type %#x\n", + b, s, f, hdrtype); + return (secbus); + } + + for (barnum = 0; barnum < bars; barnum++) + octopci_init_bar(dev, b, s, f, barnum, &command); + + /* Enable bus mastering. */ + command |= PCIM_CMD_BUSMASTEREN; + + /* Enable whatever facilities the BARs require. */ + octopci_write_config(dev, b, s, f, PCIR_COMMAND, command, 1); + + DELAY(10000); + + /* + * Set cache line size. On Octeon it should be 128 bytes, + * but according to Linux some Intel bridges have trouble + * with values over 64 bytes, so use 64 bytes. + */ + octopci_write_config(dev, b, s, f, PCIR_CACHELNSZ, 16, 1); + + /* Set latency timer. */ + octopci_write_config(dev, b, s, f, PCIR_LATTIMER, 48, 1); + + /* Board-specific or device-specific fixups and workarounds. */ + switch (cvmx_sysinfo_get()->board_type) { +#if defined(OCTEON_VENDOR_LANNER) + case CVMX_BOARD_TYPE_CUST_LANNER_MR955: + if (b == 1 && s == 7 && f == 0) { + bus_addr_t busaddr, unitbusaddr; + uint32_t bar; + uint32_t tmp; + unsigned unit; + + /* + * Set Tx DMA power. + */ + bar = octopci_read_config(dev, b, s, f, + PCIR_BAR(3), 4); + busaddr = CVMX_ADDR_DID(CVMX_FULL_DID(CVMX_OCT_DID_PCI, + CVMX_OCT_SUBDID_PCI_MEM1)); + busaddr += (bar & (uint32_t)PCIM_BAR_MEM_BASE); + for (unit = 0; unit < 4; unit++) { + unitbusaddr = busaddr + 0x430 + (unit << 8); + tmp = le32toh(cvmx_read64_uint32(unitbusaddr)); + tmp &= ~0x700; + tmp |= 0x300; + cvmx_write64_uint32(unitbusaddr, htole32(tmp)); + } + } + break; +#endif + default: + break; + } + + /* Configure PCI-PCI bridges. */ + class = octopci_read_config(dev, b, s, f, PCIR_CLASS, 1); + if (class != PCIC_BRIDGE) + return (secbus); + + subclass = octopci_read_config(dev, b, s, f, PCIR_SUBCLASS, 1); + if (subclass != PCIS_BRIDGE_PCI) + return (secbus); + + /* Enable memory and I/O access. */ + command |= PCIM_CMD_MEMEN | PCIM_CMD_PORTEN; + octopci_write_config(dev, b, s, f, PCIR_COMMAND, command, 1); + + /* Enable errors and parity checking. Do a bus reset. */ + brctl = octopci_read_config(dev, b, s, f, PCIR_BRIDGECTL_1, 1); + brctl |= PCIB_BCR_PERR_ENABLE | PCIB_BCR_SERR_ENABLE; + + /* Perform a secondary bus reset. */ + brctl |= PCIB_BCR_SECBUS_RESET; + octopci_write_config(dev, b, s, f, PCIR_BRIDGECTL_1, brctl, 1); + DELAY(100000); + brctl &= ~PCIB_BCR_SECBUS_RESET; + octopci_write_config(dev, b, s, f, PCIR_BRIDGECTL_1, brctl, 1); + + secbus++; + + /* Program memory and I/O ranges. */ + octopci_write_config(dev, b, s, f, PCIR_MEMBASE_1, + CVMX_OCT_PCI_MEM1_BASE >> 16, 2); + octopci_write_config(dev, b, s, f, PCIR_MEMLIMIT_1, + (CVMX_OCT_PCI_MEM1_BASE + CVMX_OCT_PCI_MEM1_SIZE - 1) >> 16, 2); + + octopci_write_config(dev, b, s, f, PCIR_IOBASEL_1, + CVMX_OCT_PCI_IO_BASE >> 8, 1); + octopci_write_config(dev, b, s, f, PCIR_IOBASEH_1, + CVMX_OCT_PCI_IO_BASE >> 16, 2); + + octopci_write_config(dev, b, s, f, PCIR_IOLIMITL_1, + (CVMX_OCT_PCI_IO_BASE + CVMX_OCT_PCI_IO_SIZE - 1) >> 8, 1); + octopci_write_config(dev, b, s, f, PCIR_IOLIMITH_1, + (CVMX_OCT_PCI_IO_BASE + CVMX_OCT_PCI_IO_SIZE - 1) >> 16, 2); + + /* Program prefetchable memory decoder. */ + /* XXX */ + + /* Probe secondary/subordinate buses. */ + octopci_write_config(dev, b, s, f, PCIR_PRIBUS_1, b, 1); + octopci_write_config(dev, b, s, f, PCIR_SECBUS_1, secbus, 1); + octopci_write_config(dev, b, s, f, PCIR_SUBBUS_1, 0xff, 1); + + /* Perform a secondary bus reset. */ + brctl |= PCIB_BCR_SECBUS_RESET; + octopci_write_config(dev, b, s, f, PCIR_BRIDGECTL_1, brctl, 1); + DELAY(100000); + brctl &= ~PCIB_BCR_SECBUS_RESET; + octopci_write_config(dev, b, s, f, PCIR_BRIDGECTL_1, brctl, 1); + + /* Give the bus time to settle now before reading configspace. */ + DELAY(100000); + + secbus = octopci_init_bus(dev, secbus); + + octopci_write_config(dev, b, s, f, PCIR_SUBBUS_1, secbus, 1); + + return (secbus); +} + +static unsigned +octopci_init_bus(device_t dev, unsigned b) +{ + unsigned s, f; + uint8_t hdrtype; + unsigned secbus; + + secbus = b; + + for (s = 0; s <= PCI_SLOTMAX; s++) { + for (f = 0; f <= PCI_FUNCMAX; f++) { + hdrtype = octopci_read_config(dev, b, s, f, PCIR_HDRTYPE, 1); + + if (hdrtype == 0xff) { + if (f == 0) + break; /* Next slot. */ + continue; /* Next function. */ + } + + secbus = octopci_init_device(dev, b, s, f, secbus); + + if (f == 0 && (hdrtype & PCIM_MFDEV) == 0) + break; /* Next slot. */ + } + } + + return (secbus); +} + +static uint64_t +octopci_cs_addr(unsigned bus, unsigned slot, unsigned func, unsigned reg) +{ + octeon_pci_config_space_address_t pci_addr; + + pci_addr.u64 = 0; + pci_addr.s.upper = 2; + pci_addr.s.io = 1; + pci_addr.s.did = 3; + pci_addr.s.subdid = CVMX_OCT_SUBDID_PCI_CFG; + pci_addr.s.endian_swap = 1; + pci_addr.s.bus = bus; + pci_addr.s.dev = slot; + pci_addr.s.func = func; + pci_addr.s.reg = reg; + + return (pci_addr.u64); +} + +static device_method_t octopci_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, octopci_identify), + DEVMETHOD(device_probe, octopci_probe), + DEVMETHOD(device_attach, octopci_attach), + + /* Bus interface */ + DEVMETHOD(bus_read_ivar, octopci_read_ivar), + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_alloc_resource, octopci_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource,octopci_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), + + DEVMETHOD(bus_add_child, bus_generic_add_child), + + /* pcib interface */ + DEVMETHOD(pcib_maxslots, octopci_maxslots), + DEVMETHOD(pcib_read_config, octopci_read_config), + DEVMETHOD(pcib_write_config, octopci_write_config), + DEVMETHOD(pcib_route_interrupt, octopci_route_interrupt), + + {0, 0} +}; + +static driver_t octopci_driver = { + "pcib", + octopci_methods, + sizeof(struct octopci_softc), +}; +static devclass_t octopci_devclass; +DRIVER_MODULE(octopci, ciu, octopci_driver, octopci_devclass, 0, 0); |