diff --git a/sys/arm/annapurna/alpine/alpine_pci.c b/sys/arm/annapurna/alpine/alpine_pci.c index d031587b5283..0245b6834f43 100644 --- a/sys/arm/annapurna/alpine/alpine_pci.c +++ b/sys/arm/annapurna/alpine/alpine_pci.c @@ -1,157 +1,157 @@ /*- * Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates * All rights reserved. * * Developed by Semihalf. * * 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. */ /* * Alpine PCI/PCI-Express controller driver. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" #include "contrib/alpine-hal/al_hal_unit_adapter_regs.h" #include "contrib/alpine-hal/al_hal_pcie.h" #include "contrib/alpine-hal/al_hal_pcie_axi_reg.h" #define ANNAPURNA_VENDOR_ID 0x1c36 /* Forward prototypes */ static int al_pcib_probe(device_t); static int al_pcib_attach(device_t); static void al_pcib_fixup(device_t); static struct ofw_compat_data compat_data[] = { {"annapurna-labs,al-internal-pcie", true}, {"annapurna-labs,alpine-internal-pcie", true}, {NULL, false} }; /* * Bus interface definitions. */ static device_method_t al_pcib_methods[] = { /* Device interface */ DEVMETHOD(device_probe, al_pcib_probe), DEVMETHOD(device_attach, al_pcib_attach), DEVMETHOD_END }; DEFINE_CLASS_1(pcib, al_pcib_driver, al_pcib_methods, sizeof(struct generic_pcie_fdt_softc), generic_pcie_fdt_driver); DRIVER_MODULE(alpine_pcib, simplebus, al_pcib_driver, 0, 0); DRIVER_MODULE(alpine_pcib, ofwbus, al_pcib_driver, 0, 0); static int al_pcib_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) return (ENXIO); device_set_desc(dev, "Annapurna-Labs Integrated Internal PCI-E Controller"); return (BUS_PROBE_DEFAULT); } static int al_pcib_attach(device_t dev) { int rv; - rv = pci_host_generic_attach(dev); + rv = pci_host_generic_fdt_attach(dev); /* Annapurna quirk: configure vendor-specific registers */ if (rv == 0) al_pcib_fixup(dev); return (rv); } static void al_pcib_fixup(device_t dev) { uint32_t val; uint16_t vid; uint8_t hdrtype; int bus, slot, func, maxfunc; /* Fixup is only needed on bus 0 */ bus = 0; for (slot = 0; slot <= PCI_SLOTMAX; slot++) { maxfunc = 0; for (func = 0; func <= maxfunc; func++) { hdrtype = PCIB_READ_CONFIG(dev, bus, slot, func, PCIR_HDRTYPE, 1); if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) continue; if (func == 0 && (hdrtype & PCIM_MFDEV) != 0) maxfunc = PCI_FUNCMAX; vid = PCIB_READ_CONFIG(dev, bus, slot, func, PCIR_VENDOR, 2); if (vid == ANNAPURNA_VENDOR_ID) { val = PCIB_READ_CONFIG(dev, bus, slot, func, AL_PCI_AXI_CFG_AND_CTR_0, 4); val |= PCIE_AXI_PF_AXI_ATTR_OVRD_FUNC_CTRL_2_PF_VEC_PH_VEC_OVRD_FROM_AXUSER_MASK; PCIB_WRITE_CONFIG(dev, bus, slot, func, AL_PCI_AXI_CFG_AND_CTR_0, val, 4); val = PCIB_READ_CONFIG(dev, bus, slot, func, AL_PCI_APP_CONTROL, 4); val &= ~0xffff; val |= PCIE_AXI_PF_AXI_ATTR_OVRD_FUNC_CTRL_4_PF_VEC_MEM_ADDR54_63_SEL_TGTID_MASK; PCIB_WRITE_CONFIG(dev, bus, slot, func, AL_PCI_APP_CONTROL, val, 4); } } } } diff --git a/sys/arm64/cavium/thunder_pcie_fdt.c b/sys/arm64/cavium/thunder_pcie_fdt.c index c0876b1a19bd..2c71efdbb659 100644 --- a/sys/arm64/cavium/thunder_pcie_fdt.c +++ b/sys/arm64/cavium/thunder_pcie_fdt.c @@ -1,304 +1,304 @@ /* * Copyright (C) 2016 Cavium Inc. * All rights reserved. * * Developed by Semihalf. * * 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. */ #include "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "thunder_pcie_common.h" #include "pcib_if.h" #ifdef THUNDERX_PASS_1_1_ERRATA static struct resource * thunder_pcie_fdt_alloc_resource(device_t, device_t, int, int *, rman_res_t, rman_res_t, rman_res_t, u_int); static int thunder_pcie_fdt_release_resource(device_t, device_t, int, int, struct resource*); #endif static int thunder_pcie_fdt_attach(device_t); static int thunder_pcie_fdt_probe(device_t); static int thunder_pcie_fdt_get_id(device_t, device_t, enum pci_id_type, uintptr_t *); static const struct ofw_bus_devinfo *thunder_pcie_ofw_get_devinfo(device_t, device_t); /* OFW bus interface */ struct thunder_pcie_ofw_devinfo { struct ofw_bus_devinfo di_dinfo; struct resource_list di_rl; }; static device_method_t thunder_pcie_fdt_methods[] = { /* Device interface */ DEVMETHOD(device_probe, thunder_pcie_fdt_probe), DEVMETHOD(device_attach, thunder_pcie_fdt_attach), #ifdef THUNDERX_PASS_1_1_ERRATA DEVMETHOD(bus_alloc_resource, thunder_pcie_fdt_alloc_resource), DEVMETHOD(bus_release_resource, thunder_pcie_fdt_release_resource), #endif /* pcib interface */ DEVMETHOD(pcib_get_id, thunder_pcie_fdt_get_id), /* ofw interface */ DEVMETHOD(ofw_bus_get_devinfo, thunder_pcie_ofw_get_devinfo), DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), /* End */ DEVMETHOD_END }; DEFINE_CLASS_1(pcib, thunder_pcie_fdt_driver, thunder_pcie_fdt_methods, sizeof(struct generic_pcie_fdt_softc), generic_pcie_fdt_driver); DRIVER_MODULE(thunder_pcib, simplebus, thunder_pcie_fdt_driver, 0, 0); DRIVER_MODULE(thunder_pcib, ofwbus, thunder_pcie_fdt_driver, 0, 0); static const struct ofw_bus_devinfo * thunder_pcie_ofw_get_devinfo(device_t bus __unused, device_t child) { struct thunder_pcie_ofw_devinfo *di; di = device_get_ivars(child); return (&di->di_dinfo); } static void get_addr_size_cells(phandle_t node, pcell_t *addr_cells, pcell_t *size_cells) { *addr_cells = 2; /* Find address cells if present */ OF_getencprop(node, "#address-cells", addr_cells, sizeof(*addr_cells)); *size_cells = 2; /* Find size cells if present */ OF_getencprop(node, "#size-cells", size_cells, sizeof(*size_cells)); } static int thunder_pcie_ofw_bus_attach(device_t dev) { struct thunder_pcie_ofw_devinfo *di; device_t child; phandle_t parent, node; pcell_t addr_cells, size_cells; parent = ofw_bus_get_node(dev); if (parent > 0) { get_addr_size_cells(parent, &addr_cells, &size_cells); /* Iterate through all bus subordinates */ for (node = OF_child(parent); node > 0; node = OF_peer(node)) { /* Allocate and populate devinfo. */ di = malloc(sizeof(*di), M_DEVBUF, M_WAITOK | M_ZERO); if (ofw_bus_gen_setup_devinfo(&di->di_dinfo, node) != 0) { free(di, M_DEVBUF); continue; } /* Initialize and populate resource list. */ resource_list_init(&di->di_rl); ofw_bus_reg_to_rl(dev, node, addr_cells, size_cells, &di->di_rl); ofw_bus_intr_to_rl(dev, node, &di->di_rl, NULL); /* Add newbus device for this FDT node */ child = device_add_child(dev, NULL, -1); if (child == NULL) { resource_list_free(&di->di_rl); ofw_bus_gen_destroy_devinfo(&di->di_dinfo); free(di, M_DEVBUF); continue; } device_set_ivars(child, di); } } return (0); } static int thunder_pcie_fdt_probe(device_t dev) { /* Check if we're running on Cavium ThunderX */ if (!CPU_MATCH(CPU_IMPL_MASK | CPU_PART_MASK, CPU_IMPL_CAVIUM, CPU_PART_THUNDERX, 0, 0)) return (ENXIO); if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "pci-host-ecam-generic") || ofw_bus_is_compatible(dev, "cavium,thunder-pcie") || ofw_bus_is_compatible(dev, "cavium,pci-host-thunder-ecam")) { device_set_desc(dev, "Cavium Integrated PCI/PCI-E Controller"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int thunder_pcie_fdt_attach(device_t dev) { struct generic_pcie_fdt_softc *sc; sc = device_get_softc(dev); thunder_pcie_identify_ecam(dev, &sc->base.ecam); sc->base.coherent = 1; /* Attach OFW bus */ if (thunder_pcie_ofw_bus_attach(dev) != 0) return (ENXIO); - return (pci_host_generic_attach(dev)); + return (pci_host_generic_fdt_attach(dev)); } static int thunder_pcie_fdt_get_id(device_t pci, device_t child, enum pci_id_type type, uintptr_t *id) { phandle_t node; int bsf; if (type != PCI_ID_MSI) return (pcib_get_id(pci, child, type, id)); node = ofw_bus_get_node(pci); if (OF_hasprop(node, "msi-map")) return (generic_pcie_get_id(pci, child, type, id)); bsf = pci_get_rid(child); *id = (pci_get_domain(child) << PCI_RID_DOMAIN_SHIFT) | bsf; return (0); } #ifdef THUNDERX_PASS_1_1_ERRATA struct resource * thunder_pcie_fdt_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 generic_pcie_fdt_softc *sc; struct thunder_pcie_ofw_devinfo *di; struct resource_list_entry *rle; int i; /* * For PCIe devices that do not have FDT nodes pass * the request to the core driver. */ if ((int)ofw_bus_get_node(child) <= 0) return (thunder_pcie_alloc_resource(dev, child, type, rid, start, end, count, flags)); /* For other devices use OFW method */ sc = device_get_softc(dev); if (RMAN_IS_DEFAULT_RANGE(start, end)) { if ((di = device_get_ivars(child)) == NULL) return (NULL); if (type == SYS_RES_IOPORT) type = SYS_RES_MEMORY; /* Find defaults for this rid */ rle = resource_list_find(&di->di_rl, type, *rid); if (rle == NULL) return (NULL); start = rle->start; end = rle->end; count = rle->count; } if (type == SYS_RES_MEMORY) { /* Remap through ranges property */ for (i = 0; i < MAX_RANGES_TUPLES; i++) { if (start >= sc->base.ranges[i].phys_base && end < (sc->base.ranges[i].pci_base + sc->base.ranges[i].size)) { start -= sc->base.ranges[i].phys_base; start += sc->base.ranges[i].pci_base; end -= sc->base.ranges[i].phys_base; end += sc->base.ranges[i].pci_base; break; } } if (i == MAX_RANGES_TUPLES) { device_printf(dev, "Could not map resource " "%#jx-%#jx\n", start, end); return (NULL); } } return (bus_generic_alloc_resource(dev, child, type, rid, start, end, count, flags)); } static int thunder_pcie_fdt_release_resource(device_t dev, device_t child, int type, int rid, struct resource *res) { if ((int)ofw_bus_get_node(child) <= 0) return (pci_host_generic_core_release_resource(dev, child, type, rid, res)); return (bus_generic_release_resource(dev, child, type, rid, res)); } #endif diff --git a/sys/dev/pci/pci_host_generic_fdt.c b/sys/dev/pci/pci_host_generic_fdt.c index 57ce03d0b94d..49c03625733c 100644 --- a/sys/dev/pci/pci_host_generic_fdt.c +++ b/sys/dev/pci/pci_host_generic_fdt.c @@ -1,512 +1,512 @@ /*- * Copyright (c) 2015 Ruslan Bukin * Copyright (c) 2014,2016 The FreeBSD Foundation * All rights reserved. * * This software was developed by Andrew Turner under * the sponsorship of the FreeBSD Foundation. * * This software was developed by Semihalf under * the sponsorship of the FreeBSD Foundation. * * 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. */ /* Generic ECAM PCIe driver FDT attachment */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #if defined(INTRNG) #include #endif #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" #define SPACE_CODE_SHIFT 24 #define SPACE_CODE_MASK 0x3 #define SPACE_CODE_IO_SPACE 0x1 #define PROPS_CELL_SIZE 1 #define PCI_ADDR_CELL_SIZE 2 struct pci_ofw_devinfo { STAILQ_ENTRY(pci_ofw_devinfo) pci_ofw_link; struct ofw_bus_devinfo di_dinfo; uint8_t slot; uint8_t func; uint8_t bus; }; /* Forward prototypes */ static int generic_pcie_fdt_probe(device_t dev); static int parse_pci_mem_ranges(device_t, struct generic_pcie_core_softc *); static int generic_pcie_ofw_bus_attach(device_t); static const struct ofw_bus_devinfo *generic_pcie_ofw_get_devinfo(device_t, device_t); static int generic_pcie_fdt_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "pci-host-ecam-generic")) { device_set_desc(dev, "Generic PCI host controller"); return (BUS_PROBE_GENERIC); } if (ofw_bus_is_compatible(dev, "arm,gem5_pcie")) { device_set_desc(dev, "GEM5 PCIe host controller"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } int pci_host_generic_setup_fdt(device_t dev) { struct generic_pcie_fdt_softc *sc; phandle_t node; int error; sc = device_get_softc(dev); STAILQ_INIT(&sc->pci_ofw_devlist); /* Retrieve 'ranges' property from FDT */ if (bootverbose) device_printf(dev, "parsing FDT for ECAM%d:\n", sc->base.ecam); if (parse_pci_mem_ranges(dev, &sc->base)) return (ENXIO); /* Attach OFW bus */ if (generic_pcie_ofw_bus_attach(dev) != 0) return (ENXIO); node = ofw_bus_get_node(dev); if (sc->base.coherent == 0) { sc->base.coherent = OF_hasprop(node, "dma-coherent"); } if (bootverbose) device_printf(dev, "Bus is%s cache-coherent\n", sc->base.coherent ? "" : " not"); /* TODO parse FDT bus ranges */ sc->base.bus_start = 0; sc->base.bus_end = 0xFF; /* * ofw_pcib uses device unit as PCI domain number. * Do the same. Some boards have multiple RCs handled * by different drivers, this ensures that there are * no collisions. */ sc->base.ecam = device_get_unit(dev); error = pci_host_generic_core_attach(dev); if (error != 0) return (error); if (ofw_bus_is_compatible(dev, "marvell,armada8k-pcie-ecam") || ofw_bus_is_compatible(dev, "socionext,synquacer-pcie-ecam") || ofw_bus_is_compatible(dev, "snps,dw-pcie-ecam")) { device_set_desc(dev, "Synopsys DesignWare PCIe Controller"); sc->base.quirks |= PCIE_ECAM_DESIGNWARE_QUIRK; } ofw_bus_setup_iinfo(node, &sc->pci_iinfo, sizeof(cell_t)); return (0); } int -pci_host_generic_attach(device_t dev) +pci_host_generic_fdt_attach(device_t dev) { int error; error = pci_host_generic_setup_fdt(dev); if (error != 0) return (error); device_add_child(dev, "pci", -1); return (bus_generic_attach(dev)); } static int parse_pci_mem_ranges(device_t dev, struct generic_pcie_core_softc *sc) { pcell_t pci_addr_cells, parent_addr_cells; pcell_t attributes, size_cells; cell_t *base_ranges; int nbase_ranges; phandle_t node; int i, j, k; int tuple; node = ofw_bus_get_node(dev); OF_getencprop(node, "#address-cells", &pci_addr_cells, sizeof(pci_addr_cells)); OF_getencprop(node, "#size-cells", &size_cells, sizeof(size_cells)); OF_getencprop(OF_parent(node), "#address-cells", &parent_addr_cells, sizeof(parent_addr_cells)); if (parent_addr_cells > 2 || pci_addr_cells != 3 || size_cells > 2) { device_printf(dev, "Unexpected number of address or size cells in FDT\n"); return (ENXIO); } nbase_ranges = OF_getproplen(node, "ranges"); sc->nranges = nbase_ranges / sizeof(cell_t) / (parent_addr_cells + pci_addr_cells + size_cells); base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK); OF_getencprop(node, "ranges", base_ranges, nbase_ranges); for (i = 0, j = 0; i < sc->nranges; i++) { attributes = (base_ranges[j++] >> SPACE_CODE_SHIFT) & \ SPACE_CODE_MASK; if (attributes == SPACE_CODE_IO_SPACE) { sc->ranges[i].flags |= FLAG_TYPE_IO; } else { sc->ranges[i].flags |= FLAG_TYPE_MEM; } sc->ranges[i].pci_base = 0; for (k = 0; k < (pci_addr_cells - 1); k++) { sc->ranges[i].pci_base <<= 32; sc->ranges[i].pci_base |= base_ranges[j++]; } sc->ranges[i].phys_base = 0; for (k = 0; k < parent_addr_cells; k++) { sc->ranges[i].phys_base <<= 32; sc->ranges[i].phys_base |= base_ranges[j++]; } sc->ranges[i].size = 0; for (k = 0; k < size_cells; k++) { sc->ranges[i].size <<= 32; sc->ranges[i].size |= base_ranges[j++]; } } for (; i < MAX_RANGES_TUPLES; i++) { /* zero-fill remaining tuples to mark empty elements in array */ sc->ranges[i].pci_base = 0; sc->ranges[i].phys_base = 0; sc->ranges[i].size = 0; } if (bootverbose) { for (tuple = 0; tuple < MAX_RANGES_TUPLES; tuple++) { device_printf(dev, "\tPCI addr: 0x%jx, CPU addr: 0x%jx, Size: 0x%jx\n", sc->ranges[tuple].pci_base, sc->ranges[tuple].phys_base, sc->ranges[tuple].size); } } free(base_ranges, M_DEVBUF); return (0); } static int generic_pcie_fdt_route_interrupt(device_t bus, device_t dev, int pin) { struct generic_pcie_fdt_softc *sc; struct ofw_pci_register reg; uint32_t pintr, mintr[4]; phandle_t iparent; int intrcells; sc = device_get_softc(bus); pintr = pin; bzero(®, sizeof(reg)); reg.phys_hi = (pci_get_bus(dev) << OFW_PCI_PHYS_HI_BUSSHIFT) | (pci_get_slot(dev) << OFW_PCI_PHYS_HI_DEVICESHIFT) | (pci_get_function(dev) << OFW_PCI_PHYS_HI_FUNCTIONSHIFT); intrcells = ofw_bus_lookup_imap(ofw_bus_get_node(dev), &sc->pci_iinfo, ®, sizeof(reg), &pintr, sizeof(pintr), mintr, sizeof(mintr), &iparent); if (intrcells) { pintr = ofw_bus_map_intr(dev, iparent, intrcells, mintr); return (pintr); } device_printf(bus, "could not route pin %d for device %d.%d\n", pin, pci_get_slot(dev), pci_get_function(dev)); return (PCI_INVALID_IRQ); } static int generic_pcie_fdt_alloc_msi(device_t pci, device_t child, int count, int maxcount, int *irqs) { #if defined(INTRNG) phandle_t msi_parent; int err; err = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, NULL); if (err != 0) return (err); return (intr_alloc_msi(pci, child, msi_parent, count, maxcount, irqs)); #else return (ENXIO); #endif } static int generic_pcie_fdt_release_msi(device_t pci, device_t child, int count, int *irqs) { #if defined(INTRNG) phandle_t msi_parent; int err; err = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, NULL); if (err != 0) return (err); return (intr_release_msi(pci, child, msi_parent, count, irqs)); #else return (ENXIO); #endif } static int generic_pcie_fdt_map_msi(device_t pci, device_t child, int irq, uint64_t *addr, uint32_t *data) { #if defined(INTRNG) phandle_t msi_parent; int err; err = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, NULL); if (err != 0) return (err); return (intr_map_msi(pci, child, msi_parent, irq, addr, data)); #else return (ENXIO); #endif } static int generic_pcie_fdt_alloc_msix(device_t pci, device_t child, int *irq) { #if defined(INTRNG) phandle_t msi_parent; int err; err = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, NULL); if (err != 0) return (err); return (intr_alloc_msix(pci, child, msi_parent, irq)); #else return (ENXIO); #endif } static int generic_pcie_fdt_release_msix(device_t pci, device_t child, int irq) { #if defined(INTRNG) phandle_t msi_parent; int err; err = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, NULL); if (err != 0) return (err); return (intr_release_msix(pci, child, msi_parent, irq)); #else return (ENXIO); #endif } static int generic_pcie_get_iommu(device_t pci, device_t child, uintptr_t *id) { struct pci_id_ofw_iommu *iommu; uint32_t iommu_rid; uint32_t iommu_xref; uint16_t pci_rid; phandle_t node; int err; node = ofw_bus_get_node(pci); pci_rid = pci_get_rid(child); iommu = (struct pci_id_ofw_iommu *)id; err = ofw_bus_iommu_map(node, pci_rid, &iommu_xref, &iommu_rid); if (err == 0) { iommu->id = iommu_rid; iommu->xref = iommu_xref; } return (err); } int generic_pcie_get_id(device_t pci, device_t child, enum pci_id_type type, uintptr_t *id) { phandle_t node; int err; uint32_t rid; uint16_t pci_rid; if (type == PCI_ID_OFW_IOMMU) return (generic_pcie_get_iommu(pci, child, id)); if (type != PCI_ID_MSI) return (pcib_get_id(pci, child, type, id)); node = ofw_bus_get_node(pci); pci_rid = pci_get_rid(child); err = ofw_bus_msimap(node, pci_rid, NULL, &rid); if (err != 0) return (err); *id = rid; return (0); } static const struct ofw_bus_devinfo * generic_pcie_ofw_get_devinfo(device_t bus, device_t child) { struct generic_pcie_fdt_softc *sc; struct pci_ofw_devinfo *di; uint8_t slot, func, busno; sc = device_get_softc(bus); slot = pci_get_slot(child); func = pci_get_function(child); busno = pci_get_bus(child); STAILQ_FOREACH(di, &sc->pci_ofw_devlist, pci_ofw_link) if (slot == di->slot && func == di->func && busno == di->bus) return (&di->di_dinfo); return (NULL); } /* Helper functions */ static int generic_pcie_ofw_bus_attach(device_t dev) { struct generic_pcie_fdt_softc *sc; struct pci_ofw_devinfo *di; phandle_t parent, node; pcell_t reg[5]; ssize_t len; sc = device_get_softc(dev); parent = ofw_bus_get_node(dev); if (parent == 0) return (0); /* Iterate through all bus subordinates */ for (node = OF_child(parent); node > 0; node = OF_peer(node)) { len = OF_getencprop(node, "reg", reg, sizeof(reg)); if (len != 5 * sizeof(pcell_t)) continue; /* Allocate and populate devinfo. */ di = malloc(sizeof(*di), M_DEVBUF, M_WAITOK | M_ZERO); if (ofw_bus_gen_setup_devinfo(&di->di_dinfo, node) != 0) { free(di, M_DEVBUF); continue; } di->func = OFW_PCI_PHYS_HI_FUNCTION(reg[0]); di->slot = OFW_PCI_PHYS_HI_DEVICE(reg[0]); di->bus = OFW_PCI_PHYS_HI_BUS(reg[0]); STAILQ_INSERT_TAIL(&sc->pci_ofw_devlist, di, pci_ofw_link); } return (0); } static device_method_t generic_pcie_fdt_methods[] = { DEVMETHOD(device_probe, generic_pcie_fdt_probe), - DEVMETHOD(device_attach, pci_host_generic_attach), + DEVMETHOD(device_attach, pci_host_generic_fdt_attach), DEVMETHOD(bus_alloc_resource, pci_host_generic_core_alloc_resource), DEVMETHOD(bus_release_resource, pci_host_generic_core_release_resource), /* pcib interface */ DEVMETHOD(pcib_route_interrupt, generic_pcie_fdt_route_interrupt), DEVMETHOD(pcib_alloc_msi, generic_pcie_fdt_alloc_msi), DEVMETHOD(pcib_release_msi, generic_pcie_fdt_release_msi), DEVMETHOD(pcib_alloc_msix, generic_pcie_fdt_alloc_msix), DEVMETHOD(pcib_release_msix, generic_pcie_fdt_release_msix), DEVMETHOD(pcib_map_msi, generic_pcie_fdt_map_msi), DEVMETHOD(pcib_get_id, generic_pcie_get_id), DEVMETHOD(pcib_request_feature, pcib_request_feature_allow), DEVMETHOD(ofw_bus_get_devinfo, generic_pcie_ofw_get_devinfo), DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), DEVMETHOD_END }; DEFINE_CLASS_1(pcib, generic_pcie_fdt_driver, generic_pcie_fdt_methods, sizeof(struct generic_pcie_fdt_softc), generic_pcie_core_driver); DRIVER_MODULE(pcib, simplebus, generic_pcie_fdt_driver, 0, 0); DRIVER_MODULE(pcib, ofwbus, generic_pcie_fdt_driver, 0, 0); diff --git a/sys/dev/pci/pci_host_generic_fdt.h b/sys/dev/pci/pci_host_generic_fdt.h index a5d75eddc84d..65c50a772a92 100644 --- a/sys/dev/pci/pci_host_generic_fdt.h +++ b/sys/dev/pci/pci_host_generic_fdt.h @@ -1,54 +1,54 @@ /* * Copyright (c) 2015 Ruslan Bukin * Copyright (c) 2015 The FreeBSD Foundation * All rights reserved. * * This software was developed by Semihalf. * * 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$ * */ #ifndef __PCI_HOST_GENERIC_FDT_H_ #define __PCI_HOST_GENERIC_FDT_H_ struct pci_ofw_devinfo; struct generic_pcie_fdt_softc { struct generic_pcie_core_softc base; struct ofw_bus_iinfo pci_iinfo; STAILQ_HEAD(, pci_ofw_devinfo) pci_ofw_devlist; }; DECLARE_CLASS(generic_pcie_fdt_driver); struct resource *pci_host_generic_alloc_resource(device_t, device_t, int, int *, rman_res_t, rman_res_t, rman_res_t, u_int); int pci_host_generic_setup_fdt(device_t); -int pci_host_generic_attach(device_t); +int pci_host_generic_fdt_attach(device_t); int generic_pcie_get_id(device_t, device_t, enum pci_id_type, uintptr_t *); #endif /* __PCI_HOST_GENERIC_FDT_H_ */ diff --git a/sys/dev/xilinx/xlnx_pcib.c b/sys/dev/xilinx/xlnx_pcib.c index 50865d525267..2cba7f7f7282 100644 --- a/sys/dev/xilinx/xlnx_pcib.c +++ b/sys/dev/xilinx/xlnx_pcib.c @@ -1,789 +1,789 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2020 Ruslan Bukin * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory (Department of Computer Science and * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the * DARPA SSITH research programme. * * 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. */ #include "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xlnx_pcib.h" #include "ofw_bus_if.h" #include "msi_if.h" #include "pcib_if.h" #include "pic_if.h" #define XLNX_PCIB_MAX_MSI 64 static int xlnx_pcib_fdt_attach(device_t); static int xlnx_pcib_fdt_probe(device_t); static int xlnx_pcib_fdt_get_id(device_t, device_t, enum pci_id_type, uintptr_t *); static void xlnx_pcib_msi_mask(device_t dev, struct intr_irqsrc *isrc, bool mask); struct xlnx_pcib_softc { struct generic_pcie_fdt_softc fdt_sc; struct resource *res[4]; struct mtx mtx; vm_offset_t msi_page; struct xlnx_pcib_irqsrc *isrcs; device_t dev; void *intr_cookie[3]; }; static struct resource_spec xlnx_pcib_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_IRQ, 1, RF_ACTIVE }, { SYS_RES_IRQ, 2, RF_ACTIVE }, { -1, 0 } }; struct xlnx_pcib_irqsrc { struct intr_irqsrc isrc; u_int irq; #define XLNX_IRQ_FLAG_USED (1 << 0) u_int flags; }; static void xlnx_pcib_clear_err_interrupts(struct generic_pcie_core_softc *sc) { uint32_t reg; reg = bus_read_4(sc->res, XLNX_PCIE_RPERRFRR); if (reg & RPERRFRR_VALID) { device_printf(sc->dev, "Requested ID: %x\n", reg & RPERRFRR_REQ_ID_M); bus_write_4(sc->res, XLNX_PCIE_RPERRFRR, ~0U); } } static int xlnx_pcib_intr(void *arg) { struct generic_pcie_fdt_softc *fdt_sc; struct generic_pcie_core_softc *sc; struct xlnx_pcib_softc *xlnx_sc; uint32_t val, mask, status; xlnx_sc = arg; fdt_sc = &xlnx_sc->fdt_sc; sc = &fdt_sc->base; val = bus_read_4(sc->res, XLNX_PCIE_IDR); mask = bus_read_4(sc->res, XLNX_PCIE_IMR); status = val & mask; if (!status) return (FILTER_HANDLED); if (status & IMR_LINK_DOWN) device_printf(sc->dev, "Link down"); if (status & IMR_HOT_RESET) device_printf(sc->dev, "Hot reset"); if (status & IMR_CORRECTABLE) xlnx_pcib_clear_err_interrupts(sc); if (status & IMR_FATAL) xlnx_pcib_clear_err_interrupts(sc); if (status & IMR_NON_FATAL) xlnx_pcib_clear_err_interrupts(sc); if (status & IMR_MSI) { device_printf(sc->dev, "MSI interrupt"); /* FIFO mode MSI not implemented. */ } if (status & IMR_INTX) { device_printf(sc->dev, "INTx received"); /* Not implemented. */ } if (status & IMR_SLAVE_UNSUPP_REQ) device_printf(sc->dev, "Slave unsupported request"); if (status & IMR_SLAVE_UNEXP_COMPL) device_printf(sc->dev, "Slave unexpected completion"); if (status & IMR_SLAVE_COMPL_TIMOUT) device_printf(sc->dev, "Slave completion timeout"); if (status & IMR_SLAVE_ERROR_POISON) device_printf(sc->dev, "Slave error poison"); if (status & IMR_SLAVE_COMPL_ABORT) device_printf(sc->dev, "Slave completion abort"); if (status & IMR_SLAVE_ILLEG_BURST) device_printf(sc->dev, "Slave illegal burst"); if (status & IMR_MASTER_DECERR) device_printf(sc->dev, "Master decode error"); if (status & IMR_MASTER_SLVERR) device_printf(sc->dev, "Master slave error"); bus_write_4(sc->res, XLNX_PCIE_IDR, val); return (FILTER_HANDLED); } static void xlnx_pcib_handle_msi_intr(void *arg, int msireg) { struct generic_pcie_fdt_softc *fdt_sc; struct generic_pcie_core_softc *sc; struct xlnx_pcib_softc *xlnx_sc; struct xlnx_pcib_irqsrc *xi; struct trapframe *tf; int irq; int reg; int i; xlnx_sc = arg; fdt_sc = &xlnx_sc->fdt_sc; sc = &fdt_sc->base; tf = curthread->td_intr_frame; do { reg = bus_read_4(sc->res, msireg); for (i = 0; i < sizeof(uint32_t) * 8; i++) { if (reg & (1 << i)) { bus_write_4(sc->res, msireg, (1 << i)); irq = i; if (msireg == XLNX_PCIE_RPMSIID2) irq += 32; xi = &xlnx_sc->isrcs[irq]; if (intr_isrc_dispatch(&xi->isrc, tf) != 0) { /* Disable stray. */ xlnx_pcib_msi_mask(sc->dev, &xi->isrc, 1); device_printf(sc->dev, "Stray irq %u disabled\n", irq); } } } } while (reg != 0); } static int xlnx_pcib_msi0_intr(void *arg) { xlnx_pcib_handle_msi_intr(arg, XLNX_PCIE_RPMSIID1); return (FILTER_HANDLED); } static int xlnx_pcib_msi1_intr(void *arg) { xlnx_pcib_handle_msi_intr(arg, XLNX_PCIE_RPMSIID2); return (FILTER_HANDLED); } static int xlnx_pcib_register_msi(struct xlnx_pcib_softc *sc) { const char *name; int error; int irq; sc->isrcs = malloc(sizeof(*sc->isrcs) * XLNX_PCIB_MAX_MSI, M_DEVBUF, M_WAITOK | M_ZERO); name = device_get_nameunit(sc->dev); for (irq = 0; irq < XLNX_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 void xlnx_pcib_init(struct xlnx_pcib_softc *sc) { bus_addr_t addr; int reg; /* Disable interrupts. */ bus_write_4(sc->res[0], XLNX_PCIE_IMR, 0); /* Clear pending interrupts.*/ reg = bus_read_4(sc->res[0], XLNX_PCIE_IDR); bus_write_4(sc->res[0], XLNX_PCIE_IDR, reg); /* Setup an MSI page. */ sc->msi_page = kmem_alloc_contig(PAGE_SIZE, M_WAITOK, 0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0, VM_MEMATTR_DEFAULT); addr = vtophys(sc->msi_page); bus_write_4(sc->res[0], XLNX_PCIE_RPMSIBR1, (addr >> 32)); bus_write_4(sc->res[0], XLNX_PCIE_RPMSIBR2, (addr >> 0)); /* Enable the bridge. */ reg = bus_read_4(sc->res[0], XLNX_PCIE_RPSCR); reg |= RPSCR_BE; bus_write_4(sc->res[0], XLNX_PCIE_RPSCR, reg); /* Enable interrupts. */ reg = IMR_LINK_DOWN | IMR_HOT_RESET | IMR_CFG_COMPL_STATUS_M | IMR_CFG_TIMEOUT | IMR_CORRECTABLE | IMR_NON_FATAL | IMR_FATAL | IMR_INTX | IMR_MSI | IMR_SLAVE_UNSUPP_REQ | IMR_SLAVE_UNEXP_COMPL | IMR_SLAVE_COMPL_TIMOUT | IMR_SLAVE_ERROR_POISON | IMR_SLAVE_COMPL_ABORT | IMR_SLAVE_ILLEG_BURST | IMR_MASTER_DECERR | IMR_MASTER_SLVERR; bus_write_4(sc->res[0], XLNX_PCIE_IMR, reg); } static int xlnx_pcib_fdt_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "xlnx,xdma-host-3.00")) { device_set_desc(dev, "Xilinx XDMA PCIe Controller"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int xlnx_pcib_fdt_attach(device_t dev) { struct xlnx_pcib_softc *sc; int error; sc = device_get_softc(dev); sc->dev = dev; mtx_init(&sc->mtx, "msi_mtx", NULL, MTX_DEF); if (bus_alloc_resources(dev, xlnx_pcib_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Setup MISC interrupt handler. */ error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, xlnx_pcib_intr, NULL, sc, &sc->intr_cookie[0]); if (error != 0) { device_printf(dev, "could not setup interrupt handler.\n"); return (ENXIO); } /* Setup MSI0 interrupt handler. */ error = bus_setup_intr(dev, sc->res[2], INTR_TYPE_MISC | INTR_MPSAFE, xlnx_pcib_msi0_intr, NULL, sc, &sc->intr_cookie[1]); if (error != 0) { device_printf(dev, "could not setup interrupt handler.\n"); return (ENXIO); } /* Setup MSI1 interrupt handler. */ error = bus_setup_intr(dev, sc->res[3], INTR_TYPE_MISC | INTR_MPSAFE, xlnx_pcib_msi1_intr, NULL, sc, &sc->intr_cookie[2]); if (error != 0) { device_printf(dev, "could not setup interrupt handler.\n"); return (ENXIO); } xlnx_pcib_init(sc); /* * Allow the core driver to map registers. * We will be accessing the device memory using core_softc. */ bus_release_resources(dev, xlnx_pcib_spec, sc->res); error = xlnx_pcib_register_msi(sc); if (error) return (error); - return (pci_host_generic_attach(dev)); + return (pci_host_generic_fdt_attach(dev)); } static int xlnx_pcib_fdt_get_id(device_t pci, device_t child, enum pci_id_type type, uintptr_t *id) { phandle_t node; int bsf; if (type != PCI_ID_MSI) return (pcib_get_id(pci, child, type, id)); node = ofw_bus_get_node(pci); if (OF_hasprop(node, "msi-map")) return (generic_pcie_get_id(pci, child, type, id)); bsf = pci_get_rid(child); *id = (pci_get_domain(child) << PCI_RID_DOMAIN_SHIFT) | bsf; return (0); } static int xlnx_pcib_req_valid(struct generic_pcie_core_softc *sc, u_int bus, u_int slot, u_int func, u_int reg) { bus_space_handle_t h; bus_space_tag_t t; uint32_t val; t = sc->bst; h = sc->bsh; if ((bus < sc->bus_start) || (bus > sc->bus_end)) return (0); if ((slot > PCI_SLOTMAX) || (func > PCI_FUNCMAX) || (reg > PCIE_REGMAX)) return (0); if (bus == 0 && slot > 0) return (0); val = bus_space_read_4(t, h, XLNX_PCIE_PHYSCR); if ((val & PHYSCR_LINK_UP) == 0) { /* Link is down */ return (0); } /* Valid */ return (1); } static uint32_t xlnx_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int bytes) { struct generic_pcie_fdt_softc *fdt_sc; struct xlnx_pcib_softc *xlnx_sc; struct generic_pcie_core_softc *sc; bus_space_handle_t h; bus_space_tag_t t; uint64_t offset; uint32_t data; xlnx_sc = device_get_softc(dev); fdt_sc = &xlnx_sc->fdt_sc; sc = &fdt_sc->base; if (!xlnx_pcib_req_valid(sc, bus, slot, func, reg)) return (~0U); offset = PCIE_ADDR_OFFSET(bus - sc->bus_start, slot, func, reg); t = sc->bst; h = sc->bsh; data = bus_space_read_4(t, h, offset & ~3); switch (bytes) { case 1: data >>= (offset & 3) * 8; data &= 0xff; break; case 2: data >>= (offset & 3) * 8; data = le16toh(data); break; case 4: data = le32toh(data); break; default: return (~0U); } return (data); } static void xlnx_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, uint32_t val, int bytes) { struct generic_pcie_fdt_softc *fdt_sc; struct xlnx_pcib_softc *xlnx_sc; struct generic_pcie_core_softc *sc; bus_space_handle_t h; bus_space_tag_t t; uint64_t offset; uint32_t data; xlnx_sc = device_get_softc(dev); fdt_sc = &xlnx_sc->fdt_sc; sc = &fdt_sc->base; if (!xlnx_pcib_req_valid(sc, bus, slot, func, reg)) return; offset = PCIE_ADDR_OFFSET(bus - sc->bus_start, slot, func, reg); t = sc->bst; h = sc->bsh; /* * 32-bit access used due to a bug in the Xilinx bridge that * requires to write primary and secondary buses in one blast. * * TODO: This is probably wrong on big-endian. */ switch (bytes) { case 1: data = bus_space_read_4(t, h, offset & ~3); data &= ~(0xff << ((offset & 3) * 8)); data |= (val & 0xff) << ((offset & 3) * 8); bus_space_write_4(t, h, offset & ~3, htole32(data)); break; case 2: data = bus_space_read_4(t, h, offset & ~3); data &= ~(0xffff << ((offset & 3) * 8)); data |= (val & 0xffff) << ((offset & 3) * 8); bus_space_write_4(t, h, offset & ~3, htole32(data)); break; case 4: bus_space_write_4(t, h, offset, htole32(val)); break; default: return; } } static int xlnx_pcib_alloc_msi(device_t pci, device_t child, int count, int maxcount, int *irqs) { phandle_t msi_parent; 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 xlnx_pcib_release_msi(device_t pci, device_t child, int count, int *irqs) { phandle_t msi_parent; 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 xlnx_pcib_map_msi(device_t pci, device_t child, int irq, uint64_t *addr, uint32_t *data) { phandle_t msi_parent; 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)); } static int xlnx_pcib_msi_alloc_msi(device_t dev, device_t child, int count, int maxcount, device_t *pic, struct intr_irqsrc **srcs) { struct xlnx_pcib_softc *sc; int irq, end_irq, i; bool found; sc = device_get_softc(dev); mtx_lock(&sc->mtx); found = false; for (irq = 0; (irq + count - 1) < XLNX_PCIB_MAX_MSI; irq++) { /* Assume the range is valid. */ found = true; /* Check this range is valid. */ for (end_irq = irq; end_irq < irq + count; end_irq++) { if (sc->isrcs[end_irq].flags & XLNX_IRQ_FLAG_USED) { /* This is already used. */ found = false; break; } } if (found) break; } if (!found || irq == (XLNX_PCIB_MAX_MSI - 1)) { /* Not enough interrupts were found. */ mtx_unlock(&sc->mtx); return (ENXIO); } /* Mark the interrupt as used. */ for (i = 0; i < count; i++) sc->isrcs[irq + i].flags |= XLNX_IRQ_FLAG_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 xlnx_pcib_msi_release_msi(device_t dev, device_t child, int count, struct intr_irqsrc **isrc) { struct xlnx_pcib_softc *sc; struct xlnx_pcib_irqsrc *xi; int i; sc = device_get_softc(dev); mtx_lock(&sc->mtx); for (i = 0; i < count; i++) { xi = (struct xlnx_pcib_irqsrc *)isrc[i]; KASSERT(xi->flags & XLNX_IRQ_FLAG_USED, ("%s: Releasing an unused MSI interrupt", __func__)); xi->flags &= ~XLNX_IRQ_FLAG_USED; } mtx_unlock(&sc->mtx); return (0); } static int xlnx_pcib_msi_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc, uint64_t *addr, uint32_t *data) { struct xlnx_pcib_softc *sc; struct xlnx_pcib_irqsrc *xi; sc = device_get_softc(dev); xi = (struct xlnx_pcib_irqsrc *)isrc; *addr = vtophys(sc->msi_page); *data = xi->irq; return (0); } static void xlnx_pcib_msi_mask(device_t dev, struct intr_irqsrc *isrc, bool mask) { struct generic_pcie_fdt_softc *fdt_sc; struct generic_pcie_core_softc *sc; struct xlnx_pcib_softc *xlnx_sc; struct xlnx_pcib_irqsrc *xi; uint32_t msireg, irq; uint32_t reg; xlnx_sc = device_get_softc(dev); fdt_sc = &xlnx_sc->fdt_sc; sc = &fdt_sc->base; xi = (struct xlnx_pcib_irqsrc *)isrc; irq = xi->irq; if (irq < 32) msireg = XLNX_PCIE_RPMSIID1_MASK; else msireg = XLNX_PCIE_RPMSIID2_MASK; reg = bus_read_4(sc->res, msireg); if (mask) reg &= ~(1 << irq); else reg |= (1 << irq); bus_write_4(sc->res, msireg, reg); } static void xlnx_pcib_msi_disable_intr(device_t dev, struct intr_irqsrc *isrc) { xlnx_pcib_msi_mask(dev, isrc, true); } static void xlnx_pcib_msi_enable_intr(device_t dev, struct intr_irqsrc *isrc) { xlnx_pcib_msi_mask(dev, isrc, false); } static void xlnx_pcib_msi_post_filter(device_t dev, struct intr_irqsrc *isrc) { } static void xlnx_pcib_msi_post_ithread(device_t dev, struct intr_irqsrc *isrc) { xlnx_pcib_msi_mask(dev, isrc, false); } static void xlnx_pcib_msi_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { xlnx_pcib_msi_mask(dev, isrc, true); } static int xlnx_pcib_msi_setup_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { return (0); } static int xlnx_pcib_msi_teardown_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) { return (0); } static device_method_t xlnx_pcib_fdt_methods[] = { /* Device interface */ DEVMETHOD(device_probe, xlnx_pcib_fdt_probe), DEVMETHOD(device_attach, xlnx_pcib_fdt_attach), /* pcib interface */ DEVMETHOD(pcib_get_id, xlnx_pcib_fdt_get_id), DEVMETHOD(pcib_read_config, xlnx_pcib_read_config), DEVMETHOD(pcib_write_config, xlnx_pcib_write_config), DEVMETHOD(pcib_alloc_msi, xlnx_pcib_alloc_msi), DEVMETHOD(pcib_release_msi, xlnx_pcib_release_msi), DEVMETHOD(pcib_map_msi, xlnx_pcib_map_msi), /* MSI interface */ DEVMETHOD(msi_alloc_msi, xlnx_pcib_msi_alloc_msi), DEVMETHOD(msi_release_msi, xlnx_pcib_msi_release_msi), DEVMETHOD(msi_map_msi, xlnx_pcib_msi_map_msi), /* Interrupt controller interface */ DEVMETHOD(pic_disable_intr, xlnx_pcib_msi_disable_intr), DEVMETHOD(pic_enable_intr, xlnx_pcib_msi_enable_intr), DEVMETHOD(pic_setup_intr, xlnx_pcib_msi_setup_intr), DEVMETHOD(pic_teardown_intr, xlnx_pcib_msi_teardown_intr), DEVMETHOD(pic_post_filter, xlnx_pcib_msi_post_filter), DEVMETHOD(pic_post_ithread, xlnx_pcib_msi_post_ithread), DEVMETHOD(pic_pre_ithread, xlnx_pcib_msi_pre_ithread), /* End */ DEVMETHOD_END }; DEFINE_CLASS_1(pcib, xlnx_pcib_fdt_driver, xlnx_pcib_fdt_methods, sizeof(struct xlnx_pcib_softc), generic_pcie_fdt_driver); DRIVER_MODULE(xlnx_pcib, simplebus, xlnx_pcib_fdt_driver, 0, 0); DRIVER_MODULE(xlnx_pcib, ofwbus, xlnx_pcib_fdt_driver, 0, 0);