aboutsummaryrefslogtreecommitdiff
path: root/sys/mips/cavium/octopci.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/mips/cavium/octopci.c')
-rw-r--r--sys/mips/cavium/octopci.c884
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);