diff options
-rw-r--r-- | sys/arm/nvidia/tegra_pcie.c | 882 |
1 files changed, 410 insertions, 472 deletions
diff --git a/sys/arm/nvidia/tegra_pcie.c b/sys/arm/nvidia/tegra_pcie.c index c36cae5..dbc21bc 100644 --- a/sys/arm/nvidia/tegra_pcie.c +++ b/sys/arm/nvidia/tegra_pcie.c @@ -33,20 +33,20 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/bus.h> +#include <sys/devmap.h> +#include <sys/proc.h> #include <sys/kernel.h> -#include <sys/lock.h> #include <sys/malloc.h> #include <sys/module.h> #include <sys/mutex.h> -#include <sys/queue.h> -#include <sys/bus.h> #include <sys/rman.h> -#include <sys/endian.h> -#include <sys/devmap.h> #include <machine/intr.h> #include <vm/vm.h> +#include <vm/vm_extern.h> +#include <vm/vm_kern.h> #include <vm/pmap.h> #include <dev/extres/clk/clk.h> @@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$"); #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> #include <dev/ofw/ofw_pci.h> +#include <dev/ofw/ofwpci.h> #include <dev/pci/pcivar.h> #include <dev/pci/pcireg.h> #include <dev/pci/pcib_private.h> @@ -64,100 +65,14 @@ __FBSDID("$FreeBSD$"); #include <machine/resource.h> #include <machine/bus.h> -#include "ofw_bus_if.h" -#include "pcib_if.h" - #include <arm/nvidia/tegra_pmc.h> -/* --- Move to ofw_pci.c/.h ----------------------- */ - -struct tegra_pci_range { - /* parsed phys.hi */ - int nonrelocatable; - int prefetchable; - int aliased; - int space_code; /* In native format (not shifted)*/ - int bus; - int device; - int function; - int reg; - pci_addr_t pci_addr; /* PCI Address */ - bus_addr_t host_addr; /* Host bus address*/ - bus_size_t size; /* Range size */ -}; - -static int -tegra_pci_get_ranges(phandle_t node, struct tegra_pci_range **ranges) -{ - int host_address_cells, pci_address_cells, size_cells; - cell_t *base_ranges; - ssize_t nbase_ranges; - int nranges; - int i, j, k; - uint32_t flags; - uint64_t tmp; - - host_address_cells = 1; - pci_address_cells = 3; - size_cells = 2; - OF_getencprop(OF_parent(node), "#address-cells", &host_address_cells, - sizeof(host_address_cells)); - OF_getencprop(node, "#address-cells", &pci_address_cells, - sizeof(pci_address_cells)); - OF_getencprop(node, "#size-cells", &size_cells, sizeof(size_cells)); - - nbase_ranges = OF_getproplen(node, "ranges"); - if (nbase_ranges <= 0) - return (-1); - nranges = nbase_ranges / sizeof(cell_t) / - (pci_address_cells + host_address_cells + size_cells); - - *ranges = malloc(nranges * sizeof(struct tegra_pci_range), - M_DEVBUF, M_WAITOK); - base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK); - OF_getencprop(node, "ranges", base_ranges, nbase_ranges); - - for (i = 0, j = 0; i < nranges; i++) { - flags = base_ranges[j++]; - (*ranges)[i].nonrelocatable = - flags & OFW_PCI_PHYS_HI_NONRELOCATABLE ? 1 : 0; - (*ranges)[i].prefetchable = - flags & OFW_PCI_PHYS_HI_PREFETCHABLE ? 1 : 0; - (*ranges)[i].aliased = - flags & OFW_PCI_PHYS_HI_ALIASED ? 1 : 0; - (*ranges)[i].space_code = flags & OFW_PCI_PHYS_HI_SPACEMASK; - (*ranges)[i].bus = OFW_PCI_PHYS_HI_BUS(flags); - (*ranges)[i].device = OFW_PCI_PHYS_HI_DEVICE(flags); - (*ranges)[i].function = OFW_PCI_PHYS_HI_FUNCTION(flags); - (*ranges)[i].reg = flags & OFW_PCI_PHYS_HI_REGISTERMASK; - - tmp = 0; - for (k = 0; k < pci_address_cells - 1; k++) { - tmp <<= 32; - tmp |= base_ranges[j++]; - } - (*ranges)[i].pci_addr = (pci_addr_t)tmp; - - tmp = 0; - for (k = 0; k < host_address_cells; k++) { - tmp <<= 32; - tmp |= base_ranges[j++]; - } - (*ranges)[i].host_addr = (bus_addr_t)tmp; - tmp = 0; - - for (k = 0; k < size_cells; k++) { - tmp <<= 32; - tmp |= base_ranges[j++]; - } - (*ranges)[i].size = (bus_size_t)tmp; - } +#include "ofw_bus_if.h" +#include "msi_if.h" +#include "pcib_if.h" +#include "pic_if.h" - free(base_ranges, M_DEVBUF); - return (nranges); -} -/* -------------------------------------------------------------------------- */ #define AFI_AXI_BAR0_SZ 0x000 #define AFI_AXI_BAR1_SZ 0x004 #define AFI_AXI_BAR2_SZ 0x008 @@ -179,17 +94,10 @@ tegra_pci_get_ranges(phandle_t node, struct tegra_pci_range **ranges) #define AFI_MSI_BAR_SZ 0x060 #define AFI_MSI_FPCI_BAR_ST 0x064 #define AFI_MSI_AXI_BAR_ST 0x068 - - -#define AFI_AXI_BAR6_SZ 0x134 -#define AFI_AXI_BAR7_SZ 0x138 -#define AFI_AXI_BAR8_SZ 0x13c -#define AFI_AXI_BAR6_START 0x140 -#define AFI_AXI_BAR7_START 0x144 -#define AFI_AXI_BAR8_START 0x148 -#define AFI_FPCI_BAR6 0x14c -#define AFI_FPCI_BAR7 0x150 -#define AFI_FPCI_BAR8 0x154 +#define AFI_MSI_VEC(x) (0x06c + 4 * (x)) +#define AFI_MSI_EN_VEC(x) (0x08c + 4 * (x)) +#define AFI_MSI_INTR_IN_REG 32 +#define AFI_MSI_REGS 8 #define AFI_CONFIGURATION 0x0ac #define AFI_CONFIGURATION_EN_FPCI (1 << 0) @@ -296,6 +204,8 @@ tegra_pci_get_ranges(phandle_t node, struct tegra_pci_range **ranges) /* Wait 50 ms (per port) for link. */ #define TEGRA_PCIE_LINKUP_TIMEOUT 50000 +#define TEGRA_PCIB_MSI_ENABLE + #define DEBUG #ifdef DEBUG #define debugf(fmt, args...) do { printf(fmt,##args); } while (0) @@ -345,6 +255,13 @@ static struct ofw_compat_data compat_data[] = { {NULL, 0}, }; +#define TEGRA_FLAG_MSI_USED 0x0001 +struct tegra_pcib_irqsrc { + struct intr_irqsrc isrc; + u_int irq; + u_int flags; +}; + struct tegra_pcib_port { int enabled; int port_idx; /* chip port index */ @@ -358,13 +275,11 @@ struct tegra_pcib_port { }; #define TEGRA_PCIB_MAX_PORTS 3 +#define TEGRA_PCIB_MAX_MSI AFI_MSI_INTR_IN_REG * AFI_MSI_REGS struct tegra_pcib_softc { + struct ofw_pci_softc ofw_pci; device_t dev; struct mtx mtx; - struct ofw_bus_iinfo pci_iinfo; - struct rman pref_mem_rman; - struct rman mem_rman; - struct rman io_rman; struct resource *pads_mem_res; struct resource *afi_mem_res; struct resource *cfg_mem_res; @@ -373,18 +288,18 @@ struct tegra_pcib_softc { void *intr_cookie; void *msi_intr_cookie; - struct tegra_pci_range mem_range; - struct tegra_pci_range pref_mem_range; - struct tegra_pci_range io_range; + struct ofw_pci_range mem_range; + struct ofw_pci_range pref_mem_range; + struct ofw_pci_range io_range; phy_t phy; clk_t clk_pex; clk_t clk_afi; clk_t clk_pll_e; clk_t clk_cml; - hwreset_t hwreset_pex; - hwreset_t hwreset_afi; - hwreset_t hwreset_pcie_x; + hwreset_t hwreset_pex; + hwreset_t hwreset_afi; + hwreset_t hwreset_pcie_x; regulator_t supply_avddio_pex; regulator_t supply_dvddio_pex; regulator_t supply_avdd_pex_pll; @@ -393,8 +308,7 @@ struct tegra_pcib_softc { regulator_t supply_vddio_pex_ctl; regulator_t supply_avdd_pll_erefe; - int busnr; /* host bridge bus number */ - uint32_t msi_bitmap; + vm_offset_t msi_page; /* VA of MSI page */ bus_addr_t cfg_base_addr; /* base address of config */ bus_size_t cfg_cur_offs; /* currently mapped window */ bus_space_handle_t cfg_handle; /* handle of config window */ @@ -402,276 +316,28 @@ struct tegra_pcib_softc { int lanes_cfg; int num_ports; struct tegra_pcib_port *ports[TEGRA_PCIB_MAX_PORTS]; + struct tegra_pcib_irqsrc *isrcs; }; -/* ------------------------------------------------------------------------- */ -/* - * Resource manager - */ -static int -tegra_pcib_rman_init(struct tegra_pcib_softc *sc) -{ - int err; - char buf[64]; - - /* Memory management. */ - sc->pref_mem_rman.rm_type = RMAN_ARRAY; - snprintf(buf, sizeof(buf), "%s prefetchable memory space", - device_get_nameunit(sc->dev)); - sc->pref_mem_rman.rm_descr = strdup(buf, M_DEVBUF); - err = rman_init(&sc->pref_mem_rman); - if (err) - return (err); - - sc->mem_rman.rm_type = RMAN_ARRAY; - snprintf(buf, sizeof(buf), "%s non prefetchable memory space", - device_get_nameunit(sc->dev)); - sc->mem_rman.rm_descr = strdup(buf, M_DEVBUF); - err = rman_init(&sc->mem_rman); - if (err) - return (err); - - sc->io_rman.rm_type = RMAN_ARRAY; - snprintf(buf, sizeof(buf), "%s I/O space", - device_get_nameunit(sc->dev)); - sc->io_rman.rm_descr = strdup(buf, M_DEVBUF); - err = rman_init(&sc->io_rman); - if (err) { - rman_fini(&sc->mem_rman); - return (err); - } - - err = rman_manage_region(&sc->pref_mem_rman, - sc->pref_mem_range.host_addr, - sc->pref_mem_range.host_addr + sc->pref_mem_range.size - 1); - if (err) - goto error; - err = rman_manage_region(&sc->mem_rman, - sc->mem_range.host_addr, - sc->mem_range.host_addr + sc->mem_range.size - 1); - if (err) - goto error; - err = rman_manage_region(&sc->io_rman, - sc->io_range.pci_addr, - sc->io_range.pci_addr + sc->io_range.size - 1); - if (err) - goto error; - return (0); - -error: - rman_fini(&sc->pref_mem_rman); - rman_fini(&sc->mem_rman); - rman_fini(&sc->io_rman); - return (err); -} - -static struct rman * -tegra_pcib_rman(struct tegra_pcib_softc *sc, int type, u_int flags) -{ - - switch (type) { - case SYS_RES_IOPORT: - return (&sc->io_rman); - case SYS_RES_MEMORY: - if (flags & RF_PREFETCHABLE) - return (&sc->pref_mem_rman); - else - return (&sc->mem_rman); - default: - break; - } - - return (NULL); -} - -static struct resource * -tegra_pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, - rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) -{ - struct tegra_pcib_softc *sc; - struct rman *rm; - struct resource *res; - - debugf("%s: enter %d start %#jx end %#jx count %#jx\n", __func__, - type, start, end, count); - sc = device_get_softc(dev); - -#if defined(NEW_PCIB) && defined(PCI_RES_BUS) - if (type == PCI_RES_BUS) { - return (pci_domain_alloc_bus(0, child, rid, start, end, count, - flags)); - } -#endif - - rm = tegra_pcib_rman(sc, type, flags); - - if (rm == NULL) { - res = BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, - type, rid, start, end, count, flags); - - return (res); - } - - if (bootverbose) { - device_printf(dev, - "rman_reserve_resource: start=%#jx, end=%#jx, count=%#jx\n", - start, end, count); - } - - res = rman_reserve_resource(rm, start, end, count, flags, child); - if (res == NULL) - goto fail; - rman_set_rid(res, *rid); - if (flags & RF_ACTIVE) { - if (bus_activate_resource(child, type, *rid, res)) { - rman_release_resource(res); - goto fail; - } - } - return (res); - -fail: - if (bootverbose) { - device_printf(dev, "%s FAIL: type=%d, rid=%d, " - "start=%016jx, end=%016jx, count=%016jx, flags=%x\n", - __func__, type, *rid, start, end, count, flags); - } - - return (NULL); -} - -static int -tegra_pcib_release_resource(device_t dev, device_t child, int type, int rid, - struct resource *res) -{ - struct tegra_pcib_softc *sc; - struct rman *rm; - - sc = device_get_softc(dev); - debugf("%s: %d rid %x\n", __func__, type, rid); - -#if defined(NEW_PCIB) && defined(PCI_RES_BUS) - if (type == PCI_RES_BUS) - return (pci_domain_release_bus(0, child, rid, res)); -#endif - - rm = tegra_pcib_rman(sc, type, rman_get_flags(res)); - if (rm != NULL) { - KASSERT(rman_is_region_manager(res, rm), ("rman mismatch")); - rman_release_resource(res); - } - - return (bus_generic_release_resource(dev, child, type, rid, res)); -} - -static int -tegra_pcib_adjust_resource(device_t dev, device_t child, int type, - struct resource *res, rman_res_t start, rman_res_t end) -{ - struct tegra_pcib_softc *sc; - struct rman *rm; - - sc = device_get_softc(dev); - debugf("%s: %d start %jx end %jx \n", __func__, type, start, end); - -#if defined(NEW_PCIB) && defined(PCI_RES_BUS) - if (type == PCI_RES_BUS) - return (pci_domain_adjust_bus(0, child, res, start, end)); -#endif - - rm = tegra_pcib_rman(sc, type, rman_get_flags(res)); - if (rm != NULL) - return (rman_adjust_resource(res, start, end)); - return (bus_generic_adjust_resource(dev, child, type, res, start, end)); -} -extern bus_space_tag_t fdtbus_bs_tag; -static int -tegra_pcib_pcie_activate_resource(device_t dev, device_t child, int type, - int rid, struct resource *r) -{ - struct tegra_pcib_softc *sc; - vm_offset_t start; - void *p; - int rv; - - sc = device_get_softc(dev); - rv = rman_activate_resource(r); - if (rv != 0) - return (rv); - switch(type) { - case SYS_RES_IOPORT: - start = rman_get_start(r) + sc->io_range.host_addr; - break; - default: - start = rman_get_start(r); - rman_get_start(r); - break; - } - - if (bootverbose) - printf("%s: start %zx, len %jd\n", __func__, start, - rman_get_size(r)); - - p = pmap_mapdev(start, (vm_size_t)rman_get_size(r)); - rman_set_virtual(r, p); - rman_set_bustag(r, fdtbus_bs_tag); - rman_set_bushandle(r, (u_long)p); - return (0); -} - -/* ------------------------------------------------------------------------- */ -/* - * IVARs - */ -static int -tegra_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) -{ - struct tegra_pcib_softc *sc = device_get_softc(dev); - - switch (which) { - case PCIB_IVAR_BUS: - *result = sc->busnr; - return (0); - case PCIB_IVAR_DOMAIN: - *result = device_get_unit(dev); - return (0); - } - - return (ENOENT); -} - -static int -tegra_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value) -{ - struct tegra_pcib_softc *sc = device_get_softc(dev); - - switch (which) { - case PCIB_IVAR_BUS: - sc->busnr = value; - return (0); - } - - return (ENOENT); -} - static int tegra_pcib_maxslots(device_t dev) { return (16); } - static int tegra_pcib_route_interrupt(device_t bus, device_t dev, int pin) { struct tegra_pcib_softc *sc; + u_int irq; sc = device_get_softc(bus); - device_printf(bus, "route pin %d for device %d.%d to %ju\n", + irq = intr_map_clone_irq(rman_get_start(sc->irq_res)); + device_printf(bus, "route pin %d for device %d.%d to %u\n", pin, pci_get_slot(dev), pci_get_function(dev), - rman_get_start(sc->irq_res)); + irq); - return (rman_get_start(sc->irq_res)); + return (irq); } static int @@ -812,84 +478,319 @@ static int tegra_pci_intr(void *arg) return (FILTER_HANDLED); } -#if defined(TEGRA_PCI_MSI) +/* ----------------------------------------------------------------------- + * + * PCI MSI interface + */ +static int +tegra_pcib_alloc_msi(device_t pci, device_t child, int count, int maxcount, + int *irqs) +{ + phandle_t msi_parent; + + /* XXXX ofw_bus_msimap() don't works for Tegra DT. + ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, + NULL); + */ + msi_parent = OF_xref_from_node(ofw_bus_get_node(pci)); + return (intr_alloc_msi(pci, child, msi_parent, count, maxcount, + irqs)); +} + static int -tegra_pcib_map_msi(device_t dev, device_t child, int irq, uint64_t *addr, +tegra_pcib_release_msi(device_t pci, device_t child, int count, int *irqs) +{ + phandle_t msi_parent; + + /* XXXX ofw_bus_msimap() don't works for Tegra DT. + ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, + NULL); + */ + msi_parent = OF_xref_from_node(ofw_bus_get_node(pci)); + return (intr_release_msi(pci, child, msi_parent, count, irqs)); +} + +static int +tegra_pcib_map_msi(device_t pci, device_t child, int irq, uint64_t *addr, uint32_t *data) { + phandle_t msi_parent; + + /* XXXX ofw_bus_msimap() don't works for Tegra DT. + ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, + NULL); + */ + msi_parent = OF_xref_from_node(ofw_bus_get_node(pci)); + return (intr_map_msi(pci, child, msi_parent, irq, addr, data)); +} + +#ifdef TEGRA_PCIB_MSI_ENABLE + +/* -------------------------------------------------------------------------- + * + * Interrupts + * + */ + +static inline void +tegra_pcib_isrc_mask(struct tegra_pcib_softc *sc, + struct tegra_pcib_irqsrc *tgi, uint32_t val) +{ + uint32_t reg; + int offs, bit; + + offs = tgi->irq / AFI_MSI_INTR_IN_REG; + bit = 1 << (tgi->irq % AFI_MSI_INTR_IN_REG); + + if (val != 0) + AFI_WR4(sc, AFI_MSI_VEC(offs), bit); + reg = AFI_RD4(sc, AFI_MSI_EN_VEC(offs)); + if (val != 0) + reg |= bit; + else + reg &= ~bit; + AFI_WR4(sc, AFI_MSI_EN_VEC(offs), reg); +} + +static int +tegra_pcib_msi_intr(void *arg) +{ + u_int irq, i, bit, reg; + struct tegra_pcib_softc *sc; + struct trapframe *tf; + struct tegra_pcib_irqsrc *tgi; + + sc = (struct tegra_pcib_softc *)arg; + tf = curthread->td_intr_frame; + + for (i = 0; i < AFI_MSI_REGS; i++) { + reg = AFI_RD4(sc, AFI_MSI_VEC(i)); + /* Handle one vector. */ + while (reg != 0) { + bit = ffs(reg) - 1; + /* Send EOI */ + AFI_WR4(sc, AFI_MSI_VEC(i), 1 << bit); + irq = i * AFI_MSI_INTR_IN_REG + bit; + tgi = &sc->isrcs[irq]; + if (intr_isrc_dispatch(&tgi->isrc, tf) != 0) { + /* Disable stray. */ + tegra_pcib_isrc_mask(sc, tgi, 0); + device_printf(sc->dev, + "Stray irq %u disabled\n", irq); + } + reg = AFI_RD4(sc, AFI_MSI_VEC(i)); + } + } + return (FILTER_HANDLED); +} + +static int +tegra_pcib_msi_attach(struct tegra_pcib_softc *sc) +{ + int error; + uint32_t irq; + const char *name; + + sc->isrcs = malloc(sizeof(*sc->isrcs) * TEGRA_PCIB_MAX_MSI, M_DEVBUF, + M_WAITOK | M_ZERO); + + name = device_get_nameunit(sc->dev); + for (irq = 0; irq < TEGRA_PCIB_MAX_MSI; irq++) { + sc->isrcs[irq].irq = irq; + error = intr_isrc_register(&sc->isrcs[irq].isrc, + sc->dev, 0, "%s,%u", name, irq); + if (error != 0) + return (error); /* XXX deregister ISRCs */ + } + if (intr_msi_register(sc->dev, + OF_xref_from_node(ofw_bus_get_node(sc->dev))) != 0) + return (ENXIO); + + return (0); +} + +static int +tegra_pcib_msi_detach(struct tegra_pcib_softc *sc) +{ + + /* + * There has not been established any procedure yet + * how to detach PIC from living system correctly. + */ + device_printf(sc->dev, "%s: not implemented yet\n", __func__); + return (EBUSY); +} + + +static void +tegra_pcib_msi_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ struct tegra_pcib_softc *sc; + struct tegra_pcib_irqsrc *tgi; sc = device_get_softc(dev); - irq = irq - MSI_IRQ; + tgi = (struct tegra_pcib_irqsrc *)isrc; + tegra_pcib_isrc_mask(sc, tgi, 0); +} - /* validate parameters */ - if (isclr(&sc->msi_bitmap, irq)) { - device_printf(dev, "invalid MSI 0x%x\n", irq); - return (EINVAL); - } +static void +tegra_pcib_msi_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct tegra_pcib_softc *sc; + struct tegra_pcib_irqsrc *tgi; - tegra_msi_data(irq, addr, data); + sc = device_get_softc(dev); + tgi = (struct tegra_pcib_irqsrc *)isrc; + tegra_pcib_isrc_mask(sc, tgi, 1); +} - debugf("%s: irq: %d addr: %jx data: %x\n", - __func__, irq, *addr, *data); +/* MSI interrupts are edge trigered -> do nothing */ +static void +tegra_pcib_msi_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ +} +static void +tegra_pcib_msi_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ +} + +static void +tegra_pcib_msi_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ +} + +static int +tegra_pcib_msi_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct tegra_pcib_softc *sc; + struct tegra_pcib_irqsrc *tgi; + + sc = device_get_softc(dev); + tgi = (struct tegra_pcib_irqsrc *)isrc; + + if (data == NULL || data->type != INTR_MAP_DATA_MSI) + return (ENOTSUP); + + if (isrc->isrc_handlers == 0) + tegra_pcib_msi_enable_intr(dev, isrc); + + return (0); +} + +static int +tegra_pcib_msi_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct tegra_pcib_softc *sc; + struct tegra_pcib_irqsrc *tgi; + + sc = device_get_softc(dev); + tgi = (struct tegra_pcib_irqsrc *)isrc; + + if (isrc->isrc_handlers == 0) + tegra_pcib_isrc_mask(sc, tgi, 0); return (0); } + static int -tegra_pcib_alloc_msi(device_t dev, device_t child, int count, - int maxcount __unused, int *irqs) +tegra_pcib_msi_alloc_msi(device_t dev, device_t child, int count, int maxcount, + device_t *pic, struct intr_irqsrc **srcs) { struct tegra_pcib_softc *sc; - u_int start = 0, i; + int i, irq, end_irq; + bool found; - if (powerof2(count) == 0 || count > MSI_IRQ_NUM) - return (EINVAL); + KASSERT(powerof2(count), ("%s: bad count", __func__)); + KASSERT(powerof2(maxcount), ("%s: bad maxcount", __func__)); sc = device_get_softc(dev); mtx_lock(&sc->mtx); - for (start = 0; (start + count) < MSI_IRQ_NUM; start++) { - for (i = start; i < start + count; i++) { - if (isset(&sc->msi_bitmap, i)) + found = false; + for (irq = 0; irq < TEGRA_PCIB_MAX_MSI && !found; irq++) { + /* Start on an aligned interrupt */ + if ((irq & (maxcount - 1)) != 0) + continue; + + /* Assume we found a valid range until shown otherwise */ + found = true; + + /* Check this range is valid */ + for (end_irq = irq; end_irq != irq + count - 1; end_irq++) { + /* No free interrupts */ + if (end_irq == (TEGRA_PCIB_MAX_MSI - 1)) { + found = false; break; + } + + /* This is already used */ + if ((sc->isrcs[irq].flags & TEGRA_FLAG_MSI_USED) == + TEGRA_FLAG_MSI_USED) { + found = false; + break; + } } - if (i == start + count) - break; } - if ((start + count) == MSI_IRQ_NUM) { + /* Not enough interrupts were found */ + if (!found || irq == (TEGRA_PCIB_MAX_MSI - 1)) { mtx_unlock(&sc->mtx); return (ENXIO); } - for (i = start; i < start + count; i++) { - setbit(&sc->msi_bitmap, i); - irqs[i] = MSI_IRQ + i; - } - debugf("%s: start: %x count: %x\n", __func__, start, count); + for (i = 0; i < count; i++) { + /* Mark the interrupt as used */ + sc->isrcs[irq + i].flags |= TEGRA_FLAG_MSI_USED; + } mtx_unlock(&sc->mtx); + + for (i = 0; i < count; i++) + srcs[i] = (struct intr_irqsrc *)&sc->isrcs[irq + i]; + *pic = device_get_parent(dev); return (0); } static int -tegra_pcib_release_msi(device_t dev, device_t child, int count, int *irqs) +tegra_pcib_msi_release_msi(device_t dev, device_t child, int count, + struct intr_irqsrc **isrc) { struct tegra_pcib_softc *sc; - u_int i; + struct tegra_pcib_irqsrc *ti; + int i; sc = device_get_softc(dev); mtx_lock(&sc->mtx); + for (i = 0; i < count; i++) { + ti = (struct tegra_pcib_irqsrc *)isrc; - for (i = 0; i < count; i++) - clrbit(&sc->msi_bitmap, irqs[i] - MSI_IRQ); + KASSERT((ti->flags & TEGRA_FLAG_MSI_USED) == TEGRA_FLAG_MSI_USED, + ("%s: Trying to release an unused MSI-X interrupt", + __func__)); - mtx_unlock(&sc->mtx); + ti->flags &= ~TEGRA_FLAG_MSI_USED; + mtx_unlock(&sc->mtx); + } + return (0); +} + +static int +tegra_pcib_msi_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc, + uint64_t *addr, uint32_t *data) +{ + struct tegra_pcib_softc *sc = device_get_softc(dev); + struct tegra_pcib_irqsrc *ti = (struct tegra_pcib_irqsrc *)isrc; + + *addr = vtophys(sc->msi_page); + *data = ti->irq; return (0); } #endif +/* ------------------------------------------------------------------- */ static bus_size_t tegra_pcib_pex_ctrl(struct tegra_pcib_softc *sc, int port) { @@ -948,7 +849,6 @@ tegra_pcib_enable_fdt_resources(struct tegra_pcib_softc *sc) "Cannot enable 'avdd-pex-pll' regulator\n"); return (rv); } - rv = regulator_enable(sc->supply_hvdd_pex); if (rv != 0) { device_printf(sc->dev, @@ -992,13 +892,11 @@ tegra_pcib_enable_fdt_resources(struct tegra_pcib_softc *sc) device_printf(sc->dev, "Cannot enable 'afi' clock\n"); return (rv); } - rv = clk_enable(sc->clk_cml); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'cml' clock\n"); return (rv); } - rv = clk_enable(sc->clk_pll_e); if (rv != 0) { device_printf(sc->dev, "Cannot enable 'pll_e' clock\n"); @@ -1193,12 +1091,13 @@ tegra_pcib_parse_fdt_resources(struct tegra_pcib_softc *sc, phandle_t node) static int tegra_pcib_decode_ranges(struct tegra_pcib_softc *sc, - struct tegra_pci_range *ranges, int nranges) + struct ofw_pci_range *ranges, int nranges) { int i; for (i = 2; i < nranges; i++) { - if (ranges[i].space_code == OFW_PCI_PHYS_HI_SPACE_IO) { + if ((ranges[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) == + OFW_PCI_PHYS_HI_SPACE_IO) { if (sc->io_range.size != 0) { device_printf(sc->dev, "Duplicated IO range found in DT\n"); @@ -1206,23 +1105,25 @@ tegra_pcib_decode_ranges(struct tegra_pcib_softc *sc, } sc->io_range = ranges[i]; } - if ((ranges[i].space_code == OFW_PCI_PHYS_HI_SPACE_MEM32) && - !ranges[i].prefetchable) { - if (sc->mem_range.size != 0) { - device_printf(sc->dev, - "Duplicated memory range found in DT\n"); - return (ENXIO); - } - sc->mem_range = ranges[i]; - } - if ((ranges[i].space_code == OFW_PCI_PHYS_HI_SPACE_MEM32) && - ranges[i].prefetchable) { - if (sc->pref_mem_range.size != 0) { - device_printf(sc->dev, - "Duplicated memory range found in DT\n"); - return (ENXIO); + if (((ranges[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) == + OFW_PCI_PHYS_HI_SPACE_MEM32)) { + if (ranges[i].pci_hi & OFW_PCI_PHYS_HI_PREFETCHABLE) { + if (sc->pref_mem_range.size != 0) { + device_printf(sc->dev, + "Duplicated memory range found " + "in DT\n"); + return (ENXIO); + } + sc->pref_mem_range = ranges[i]; + } else { + if (sc->mem_range.size != 0) { + device_printf(sc->dev, + "Duplicated memory range found " + "in DT\n"); + return (ENXIO); + } + sc->mem_range = ranges[i]; } - sc->pref_mem_range = ranges[i]; } } if ((sc->io_range.size == 0) || (sc->mem_range.size == 0) @@ -1457,16 +1358,16 @@ tegra_pcib_enable(struct tegra_pcib_softc *sc, uint32_t port) FPCI_MAP_EXT_TYPE1_CONFIG, rman_get_size(sc->cfg_mem_res), 0); /* BAR 1 - downstream I/O. */ - tegra_pcib_set_bar(sc, 1, sc->io_range.host_addr, FPCI_MAP_IO, + tegra_pcib_set_bar(sc, 1, sc->io_range.host, FPCI_MAP_IO, sc->io_range.size, 0); /* BAR 2 - downstream prefetchable memory 1:1. */ - tegra_pcib_set_bar(sc, 2, sc->pref_mem_range.host_addr, - sc->pref_mem_range.host_addr, sc->pref_mem_range.size, 1); + tegra_pcib_set_bar(sc, 2, sc->pref_mem_range.host, + sc->pref_mem_range.host, sc->pref_mem_range.size, 1); /* BAR 3 - downstream not prefetchable memory 1:1 .*/ - tegra_pcib_set_bar(sc, 3, sc->mem_range.host_addr, - sc->mem_range.host_addr, sc->mem_range.size, 1); + tegra_pcib_set_bar(sc, 3, sc->mem_range.host, + sc->mem_range.host, sc->mem_range.size, 1); /* BAR 3-8 clear. */ tegra_pcib_set_bar(sc, 4, 0, 0, 0, 0); @@ -1480,6 +1381,52 @@ tegra_pcib_enable(struct tegra_pcib_softc *sc, uint32_t port) return(0); } +#ifdef TEGRA_PCIB_MSI_ENABLE +static int +tegra_pcib_attach_msi(device_t dev) +{ + struct tegra_pcib_softc *sc; + uint32_t reg; + int i, rv; + + sc = device_get_softc(dev); + + sc->msi_page = kmem_alloc_contig(kernel_arena, PAGE_SIZE, M_WAITOK, + 0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0, VM_MEMATTR_DEFAULT); + + /* MSI BAR */ + tegra_pcib_set_bar(sc, 9, vtophys(sc->msi_page), vtophys(sc->msi_page), + PAGE_SIZE, 0); + + /* Disble and clear all interrupts. */ + for (i = 0; i < AFI_MSI_REGS; i++) { + AFI_WR4(sc, AFI_MSI_EN_VEC(i), 0); + AFI_WR4(sc, AFI_MSI_VEC(i), 0xFFFFFFFF); + } + rv = bus_setup_intr(dev, sc->msi_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + tegra_pcib_msi_intr, NULL, sc, &sc->msi_intr_cookie); + if (rv != 0) { + device_printf(dev, "cannot setup MSI interrupt handler\n"); + rv = ENXIO; + goto out; + } + + if (tegra_pcib_msi_attach(sc) != 0) { + device_printf(dev, "WARNING: unable to attach PIC\n"); + tegra_pcib_msi_detach(sc); + goto out; + } + + /* Unmask MSI interrupt. */ + reg = AFI_RD4(sc, AFI_INTR_MASK); + reg |= AFI_INTR_MASK_MSI_MASK; + AFI_WR4(sc, AFI_INTR_MASK, reg); + +out: + return (rv); +} +#endif + static int tegra_pcib_probe(device_t dev) { @@ -1501,8 +1448,6 @@ tegra_pcib_attach(device_t dev) uint32_t unit; int rv; int rid; - int nranges; - struct tegra_pci_range *ranges; struct tegra_pcib_port *port; int i; @@ -1511,7 +1456,6 @@ tegra_pcib_attach(device_t dev) unit = fdt_get_unit(dev); mtx_init(&sc->mtx, "msi_mtx", NULL, MTX_DEF); - node = ofw_bus_get_node(dev); rv = tegra_pcib_parse_fdt_resources(sc, node); @@ -1520,14 +1464,6 @@ tegra_pcib_attach(device_t dev) return (rv); } - nranges = tegra_pci_get_ranges(node, &ranges); - if (nranges != 5) { - device_printf(sc->dev, "Unexpected number of ranges: %d\n", - nranges); - rv = ENXIO; - goto out; - } - /* Allocate bus_space resources. */ rid = 0; sc->pads_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, @@ -1579,9 +1515,8 @@ tegra_pcib_attach(device_t dev) } /* - * Get PCI interrupt info. + * Get PCI interrupt */ - ofw_bus_setup_iinfo(node, &sc->pci_iinfo, sizeof(pcell_t)); rid = 0; sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); @@ -1600,23 +1535,22 @@ tegra_pcib_attach(device_t dev) goto out; } - if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO | INTR_MPSAFE, - tegra_pci_intr, NULL, sc, &sc->intr_cookie)) { - device_printf(dev, "cannot setup interrupt handler\n"); - rv = ENXIO; + sc->ofw_pci.sc_range_mask = 0x3; + rv = ofw_pci_init(dev); + if (rv != 0) goto out; - } - /* Memory management. */ - rv = tegra_pcib_decode_ranges(sc, ranges, nranges); + rv = tegra_pcib_decode_ranges(sc, sc->ofw_pci.sc_range, + sc->ofw_pci.sc_nrange); if (rv != 0) goto out; - rv = tegra_pcib_rman_init(sc); - if (rv != 0) + if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + tegra_pci_intr, NULL, sc, &sc->intr_cookie)) { + device_printf(dev, "cannot setup interrupt handler\n"); + rv = ENXIO; goto out; - free(ranges, M_DEVBUF); - ranges = NULL; + } /* * Enable PCIE device. @@ -1633,13 +1567,16 @@ tegra_pcib_attach(device_t dev) tegra_pcib_port_disable(sc, i); } +#ifdef TEGRA_PCIB_MSI_ENABLE + rv = tegra_pcib_attach_msi(dev); + if (rv != 0) + goto out; +#endif device_add_child(dev, "pci", -1); return (bus_generic_attach(dev)); out: - if (ranges != NULL) - free(ranges, M_DEVBUF); return (rv); } @@ -1651,13 +1588,6 @@ static device_method_t tegra_pcib_methods[] = { DEVMETHOD(device_attach, tegra_pcib_attach), /* Bus interface */ - DEVMETHOD(bus_read_ivar, tegra_pcib_read_ivar), - DEVMETHOD(bus_write_ivar, tegra_pcib_write_ivar), - DEVMETHOD(bus_alloc_resource, tegra_pcib_alloc_resource), - DEVMETHOD(bus_adjust_resource, tegra_pcib_adjust_resource), - DEVMETHOD(bus_release_resource, tegra_pcib_release_resource), - DEVMETHOD(bus_activate_resource, tegra_pcib_pcie_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), @@ -1666,11 +1596,24 @@ static device_method_t tegra_pcib_methods[] = { DEVMETHOD(pcib_read_config, tegra_pcib_read_config), DEVMETHOD(pcib_write_config, tegra_pcib_write_config), DEVMETHOD(pcib_route_interrupt, tegra_pcib_route_interrupt), - -#if defined(TEGRA_PCI_MSI) DEVMETHOD(pcib_alloc_msi, tegra_pcib_alloc_msi), DEVMETHOD(pcib_release_msi, tegra_pcib_release_msi), DEVMETHOD(pcib_map_msi, tegra_pcib_map_msi), + +#ifdef TEGRA_PCIB_MSI_ENABLE + /* MSI/MSI-X */ + DEVMETHOD(msi_alloc_msi, tegra_pcib_msi_alloc_msi), + DEVMETHOD(msi_release_msi, tegra_pcib_msi_release_msi), + DEVMETHOD(msi_map_msi, tegra_pcib_msi_map_msi), + + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_intr, tegra_pcib_msi_disable_intr), + DEVMETHOD(pic_enable_intr, tegra_pcib_msi_enable_intr), + DEVMETHOD(pic_setup_intr, tegra_pcib_msi_setup_intr), + DEVMETHOD(pic_teardown_intr, tegra_pcib_msi_teardown_intr), + DEVMETHOD(pic_post_filter, tegra_pcib_msi_post_filter), + DEVMETHOD(pic_post_ithread, tegra_pcib_msi_post_ithread), + DEVMETHOD(pic_pre_ithread, tegra_pcib_msi_pre_ithread), #endif /* OFW bus interface */ @@ -1683,12 +1626,7 @@ static device_method_t tegra_pcib_methods[] = { DEVMETHOD_END }; -static driver_t tegra_pcib_driver = { - "pcib", - tegra_pcib_methods, - sizeof(struct tegra_pcib_softc), -}; - +DEFINE_CLASS_1(pcib, tegra_pcib_driver, tegra_pcib_methods, + sizeof(struct tegra_pcib_softc), ofw_pci_driver); devclass_t pcib_devclass; - DRIVER_MODULE(pcib, simplebus, tegra_pcib_driver, pcib_devclass, 0, 0); |