diff --git a/sys/dev/pci/pci_dw.c b/sys/dev/pci/pci_dw.c index f0aae5bf8418..e4211a70a80b 100644 --- a/sys/dev/pci/pci_dw.c +++ b/sys/dev/pci/pci_dw.c @@ -1,689 +1,703 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2019 Michal Meloun * * 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. * */ /* Base class for all Synopsys DesignWare PCI/PCIe drivers */ #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 "pcib_if.h" #include "pci_dw_if.h" #define DEBUG #ifdef DEBUG #define debugf(fmt, args...) do { printf(fmt,##args); } while (0) #else #define debugf(fmt, args...) #endif #define DBI_WR1(sc, reg, val) pci_dw_dbi_wr1((sc)->dev, reg, val) #define DBI_WR2(sc, reg, val) pci_dw_dbi_wr2((sc)->dev, reg, val) #define DBI_WR4(sc, reg, val) pci_dw_dbi_wr4((sc)->dev, reg, val) #define DBI_RD1(sc, reg) pci_dw_dbi_rd1((sc)->dev, reg) #define DBI_RD2(sc, reg) pci_dw_dbi_rd2((sc)->dev, reg) #define DBI_RD4(sc, reg) pci_dw_dbi_rd4((sc)->dev, reg) #define PCI_BUS_SHIFT 20 #define PCI_SLOT_SHIFT 15 #define PCI_FUNC_SHIFT 12 #define PCI_BUS_MASK 0xFF #define PCI_SLOT_MASK 0x1F #define PCI_FUNC_MASK 0x07 #define PCI_REG_MASK 0xFFF #define IATU_CFG_BUS(bus) ((uint64_t)((bus) & 0xff) << 24) #define IATU_CFG_SLOT(slot) ((uint64_t)((slot) & 0x1f) << 19) #define IATU_CFG_FUNC(func) ((uint64_t)((func) & 0x07) << 16) static uint32_t pci_dw_dbi_read(device_t dev, u_int reg, int width) { struct pci_dw_softc *sc; sc = device_get_softc(dev); MPASS(sc->dbi_res != NULL); switch (width) { case 4: return (bus_read_4(sc->dbi_res, reg)); case 2: return (bus_read_2(sc->dbi_res, reg)); case 1: return (bus_read_1(sc->dbi_res, reg)); default: device_printf(sc->dev, "Unsupported width: %d\n", width); return (0xFFFFFFFF); } } static void pci_dw_dbi_write(device_t dev, u_int reg, uint32_t val, int width) { struct pci_dw_softc *sc; sc = device_get_softc(dev); MPASS(sc->dbi_res != NULL); switch (width) { case 4: bus_write_4(sc->dbi_res, reg, val); break; case 2: bus_write_2(sc->dbi_res, reg, val); break; case 1: bus_write_1(sc->dbi_res, reg, val); break; default: device_printf(sc->dev, "Unsupported width: %d\n", width); break; } } static void pci_dw_dbi_protect(struct pci_dw_softc *sc, bool protect) { uint32_t reg; reg = DBI_RD4(sc, DW_MISC_CONTROL_1); if (protect) reg &= ~DBI_RO_WR_EN; else reg |= DBI_RO_WR_EN; DBI_WR4(sc, DW_MISC_CONTROL_1, reg); } static bool pci_dw_check_dev(struct pci_dw_softc *sc, u_int bus, u_int slot, u_int func, u_int reg) { bool status; int rv; if (bus < sc->bus_start || bus > sc->bus_end || slot > PCI_SLOTMAX || func > PCI_FUNCMAX || reg > PCIE_REGMAX) return (false); /* link is needed for access to all non-root busses */ if (bus != sc->root_bus) { rv = PCI_DW_GET_LINK(sc->dev, &status); if (rv != 0 || !status) return (false); return (true); } /* we have only 1 device with 1 function root port */ if (slot > 0 || func > 0) return (false); return (true); } /* Map one uoutbound ATU region */ static int pci_dw_map_out_atu(struct pci_dw_softc *sc, int idx, int type, uint64_t pa, uint64_t pci_addr, uint32_t size) { uint32_t reg; int i; if (size == 0) return (0); DBI_WR4(sc, DW_IATU_VIEWPORT, IATU_REGION_INDEX(idx)); DBI_WR4(sc, DW_IATU_LWR_BASE_ADDR, pa & 0xFFFFFFFF); DBI_WR4(sc, DW_IATU_UPPER_BASE_ADDR, (pa >> 32) & 0xFFFFFFFF); DBI_WR4(sc, DW_IATU_LIMIT_ADDR, (pa + size - 1) & 0xFFFFFFFF); DBI_WR4(sc, DW_IATU_LWR_TARGET_ADDR, pci_addr & 0xFFFFFFFF); DBI_WR4(sc, DW_IATU_UPPER_TARGET_ADDR, (pci_addr >> 32) & 0xFFFFFFFF); DBI_WR4(sc, DW_IATU_CTRL1, IATU_CTRL1_TYPE(type)); DBI_WR4(sc, DW_IATU_CTRL2, IATU_CTRL2_REGION_EN); /* Wait until setup becomes valid */ for (i = 10; i > 0; i--) { reg = DBI_RD4(sc, DW_IATU_CTRL2); if (reg & IATU_CTRL2_REGION_EN) return (0); DELAY(5); } device_printf(sc->dev, "Cannot map outbound region(%d) in iATU\n", idx); return (ETIMEDOUT); } static int pci_dw_setup_hw(struct pci_dw_softc *sc) { uint32_t reg; - int rv; + int rv, i; pci_dw_dbi_protect(sc, false); /* Setup config registers */ DBI_WR1(sc, PCIR_CLASS, PCIC_BRIDGE); DBI_WR1(sc, PCIR_SUBCLASS, PCIS_BRIDGE_PCI); DBI_WR4(sc, PCIR_BAR(0), 4); DBI_WR4(sc, PCIR_BAR(1), 0); DBI_WR1(sc, PCIR_INTPIN, 1); DBI_WR1(sc, PCIR_PRIBUS_1, sc->root_bus); DBI_WR1(sc, PCIR_SECBUS_1, sc->sub_bus); DBI_WR1(sc, PCIR_SUBBUS_1, sc->bus_end); DBI_WR2(sc, PCIR_COMMAND, PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_SERRESPEN); pci_dw_dbi_protect(sc, true); - /* Setup outbound memory window */ - rv = pci_dw_map_out_atu(sc, 0, IATU_CTRL1_TYPE_MEM, - sc->mem_range.host, sc->mem_range.pci, sc->mem_range.size); - if (rv != 0) - return (rv); + /* Setup outbound memory windows */ + for (i = 0; i < min(sc->num_mem_ranges, sc->num_viewport - 1); ++i) { + rv = pci_dw_map_out_atu(sc, i + 1, IATU_CTRL1_TYPE_MEM, + sc->mem_ranges[i].host, sc->mem_ranges[i].pci, + sc->mem_ranges[i].size); + if (rv != 0) + return (rv); + } - /* If we have enouht viewports ..*/ - if (sc->num_viewport >= 3 && sc->io_range.size != 0) { + /* If we have enough viewports ..*/ + if (sc->num_mem_ranges + 1 < sc->num_viewport && + sc->io_range.size != 0) { /* Setup outbound I/O window */ - rv = pci_dw_map_out_atu(sc, 2, IATU_CTRL1_TYPE_IO, - sc->io_range.host, sc->io_range.pci, sc->io_range.size); + rv = pci_dw_map_out_atu(sc, sc->num_mem_ranges + 1, + IATU_CTRL1_TYPE_IO, sc->io_range.host, sc->io_range.pci, + sc->io_range.size); if (rv != 0) return (rv); } - /* XXX Should we handle also prefetch memory? */ /* Adjust number of lanes */ reg = DBI_RD4(sc, DW_PORT_LINK_CTRL); reg &= ~PORT_LINK_CAPABLE(~0); switch (sc->num_lanes) { case 1: reg |= PORT_LINK_CAPABLE(PORT_LINK_CAPABLE_1); break; case 2: reg |= PORT_LINK_CAPABLE(PORT_LINK_CAPABLE_2); break; case 4: reg |= PORT_LINK_CAPABLE(PORT_LINK_CAPABLE_4); break; case 8: reg |= PORT_LINK_CAPABLE(PORT_LINK_CAPABLE_8); break; case 16: reg |= PORT_LINK_CAPABLE(PORT_LINK_CAPABLE_16); break; case 32: reg |= PORT_LINK_CAPABLE(PORT_LINK_CAPABLE_32); break; default: device_printf(sc->dev, "'num-lanes' property have invalid value: %d\n", sc->num_lanes); return (EINVAL); } DBI_WR4(sc, DW_PORT_LINK_CTRL, reg); /* And link width */ reg = DBI_RD4(sc, DW_GEN2_CTRL); reg &= ~GEN2_CTRL_NUM_OF_LANES(~0); switch (sc->num_lanes) { case 1: reg |= GEN2_CTRL_NUM_OF_LANES(GEN2_CTRL_NUM_OF_LANES_1); break; case 2: reg |= GEN2_CTRL_NUM_OF_LANES(GEN2_CTRL_NUM_OF_LANES_2); break; case 4: reg |= GEN2_CTRL_NUM_OF_LANES(GEN2_CTRL_NUM_OF_LANES_4); break; case 8: reg |= GEN2_CTRL_NUM_OF_LANES(GEN2_CTRL_NUM_OF_LANES_8); break; case 16: reg |= GEN2_CTRL_NUM_OF_LANES(GEN2_CTRL_NUM_OF_LANES_16); break; case 32: reg |= GEN2_CTRL_NUM_OF_LANES(GEN2_CTRL_NUM_OF_LANES_32); break; } DBI_WR4(sc, DW_GEN2_CTRL, reg); reg = DBI_RD4(sc, DW_GEN2_CTRL); reg |= DIRECT_SPEED_CHANGE; DBI_WR4(sc, DW_GEN2_CTRL, reg); return (0); } static int pci_dw_decode_ranges(struct pci_dw_softc *sc, struct ofw_pci_range *ranges, int nranges) { - int i; + int i, nmem, rv; + nmem = 0; + for (i = 0; i < nranges; i++) { + if ((ranges[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) == + OFW_PCI_PHYS_HI_SPACE_MEM32) + ++nmem; + } + + sc->mem_ranges = malloc(nmem * sizeof(*sc->mem_ranges), M_DEVBUF, + M_WAITOK); + sc->num_mem_ranges = nmem; + + nmem = 0; for (i = 0; i < nranges; i++) { 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"); - return (ENXIO); + rv = ENXIO; + goto out; } + sc->io_range = ranges[i]; + if (sc->io_range.size > UINT32_MAX) { + device_printf(sc->dev, + "ATU IO window size is too large. " + "Up to 4GB windows are supported, " + "trimming window size to 4GB\n"); + sc->io_range.size = UINT32_MAX; + } } - 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]; + if ((ranges[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) == + OFW_PCI_PHYS_HI_SPACE_MEM32) { + MPASS(nmem < sc->num_mem_ranges); + sc->mem_ranges[nmem] = ranges[i]; + if (sc->mem_ranges[nmem].size > UINT32_MAX) { + device_printf(sc->dev, + "ATU MEM window size is too large. " + "Up to 4GB windows are supported, " + "trimming window size to 4GB\n"); + sc->mem_ranges[nmem].size = UINT32_MAX; } + ++nmem; } } - if (sc->mem_range.size == 0) { + + MPASS(nmem == sc->num_mem_ranges); + + if (nmem == 0) { device_printf(sc->dev, - " Not all required ranges are found in DT\n"); + "Missing required memory range in DT\n"); return (ENXIO); } - if (sc->io_range.size > UINT32_MAX) { - device_printf(sc->dev, - "ATU IO window size is too large. Up to 4GB windows " - "are supported, trimming window size to 4GB\n"); - sc->io_range.size = UINT32_MAX; - } - if (sc->mem_range.size > UINT32_MAX) { - device_printf(sc->dev, - "ATU MEM window size is too large. Up to 4GB windows " - "are supported, trimming window size to 4GB\n"); - sc->mem_range.size = UINT32_MAX; - } + return (0); + +out: + free(sc->mem_ranges, M_DEVBUF); + return (rv); } /*----------------------------------------------------------------------------- * * P C I B I N T E R F A C E */ static uint32_t pci_dw_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int bytes) { struct pci_dw_softc *sc; struct resource *res; uint32_t data; uint64_t addr; int type, rv; sc = device_get_softc(dev); if (!pci_dw_check_dev(sc, bus, slot, func, reg)) return (0xFFFFFFFFU); if (bus == sc->root_bus) { res = (sc->dbi_res); } else { addr = IATU_CFG_BUS(bus) | IATU_CFG_SLOT(slot) | IATU_CFG_FUNC(func); if (bus == sc->sub_bus) type = IATU_CTRL1_TYPE_CFG0; else type = IATU_CTRL1_TYPE_CFG1; - rv = pci_dw_map_out_atu(sc, 1, type, + rv = pci_dw_map_out_atu(sc, 0, type, sc->cfg_pa, addr, sc->cfg_size); if (rv != 0) return (0xFFFFFFFFU); res = sc->cfg_res; } switch (bytes) { case 1: data = bus_read_1(res, reg); break; case 2: data = bus_read_2(res, reg); break; case 4: data = bus_read_4(res, reg); break; default: data = 0xFFFFFFFFU; } return (data); } static void pci_dw_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, uint32_t val, int bytes) { struct pci_dw_softc *sc; struct resource *res; uint64_t addr; int type, rv; sc = device_get_softc(dev); if (!pci_dw_check_dev(sc, bus, slot, func, reg)) return; if (bus == sc->root_bus) { res = (sc->dbi_res); } else { addr = IATU_CFG_BUS(bus) | IATU_CFG_SLOT(slot) | IATU_CFG_FUNC(func); if (bus == sc->sub_bus) type = IATU_CTRL1_TYPE_CFG0; else type = IATU_CTRL1_TYPE_CFG1; - rv = pci_dw_map_out_atu(sc, 1, type, + rv = pci_dw_map_out_atu(sc, 0, type, sc->cfg_pa, addr, sc->cfg_size); if (rv != 0) return ; res = sc->cfg_res; } switch (bytes) { case 1: bus_write_1(res, reg, val); break; case 2: bus_write_2(res, reg, val); break; case 4: bus_write_4(res, reg, val); break; default: break; } } static int pci_dw_alloc_msi(device_t pci, device_t child, int count, int maxcount, int *irqs) { phandle_t msi_parent; int rv; rv = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, NULL); if (rv != 0) return (rv); return (intr_alloc_msi(pci, child, msi_parent, count, maxcount, irqs)); } static int pci_dw_release_msi(device_t pci, device_t child, int count, int *irqs) { phandle_t msi_parent; int rv; rv = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, NULL); if (rv != 0) return (rv); return (intr_release_msi(pci, child, msi_parent, count, irqs)); } static int pci_dw_map_msi(device_t pci, device_t child, int irq, uint64_t *addr, uint32_t *data) { phandle_t msi_parent; int rv; rv = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, NULL); if (rv != 0) return (rv); return (intr_map_msi(pci, child, msi_parent, irq, addr, data)); } static int pci_dw_alloc_msix(device_t pci, device_t child, int *irq) { phandle_t msi_parent; int rv; rv = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, NULL); if (rv != 0) return (rv); return (intr_alloc_msix(pci, child, msi_parent, irq)); } static int pci_dw_release_msix(device_t pci, device_t child, int irq) { phandle_t msi_parent; int rv; rv = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent, NULL); if (rv != 0) return (rv); return (intr_release_msix(pci, child, msi_parent, irq)); } static int pci_dw_get_id(device_t pci, device_t child, enum pci_id_type type, uintptr_t *id) { phandle_t node; int rv; uint32_t rid; uint16_t pci_rid; 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); rv = ofw_bus_msimap(node, pci_rid, NULL, &rid); if (rv != 0) return (rv); *id = rid; return (0); } /*----------------------------------------------------------------------------- * * B U S / D E V I C E I N T E R F A C E */ static bus_dma_tag_t pci_dw_get_dma_tag(device_t dev, device_t child) { struct pci_dw_softc *sc; sc = device_get_softc(dev); return (sc->dmat); } int pci_dw_init(device_t dev) { struct pci_dw_softc *sc; int rv, rid; sc = device_get_softc(dev); sc->dev = dev; sc->node = ofw_bus_get_node(dev); mtx_init(&sc->mtx, "pci_dw_mtx", NULL, MTX_DEF); /* XXXn Should not be this configurable ? */ sc->bus_start = 0; sc->bus_end = 255; sc->root_bus = 0; sc->sub_bus = 1; /* Read FDT properties */ if (!sc->coherent) sc->coherent = OF_hasprop(sc->node, "dma-coherent"); rv = OF_getencprop(sc->node, "num-viewport", &sc->num_viewport, sizeof(sc->num_viewport)); if (rv != sizeof(sc->num_viewport)) sc->num_viewport = 2; rv = OF_getencprop(sc->node, "num-lanes", &sc->num_lanes, sizeof(sc->num_viewport)); if (rv != sizeof(sc->num_lanes)) sc->num_lanes = 1; if (sc->num_lanes != 1 && sc->num_lanes != 2 && sc->num_lanes != 4 && sc->num_lanes != 8) { device_printf(dev, "invalid number of lanes: %d\n",sc->num_lanes); sc->num_lanes = 0; rv = ENXIO; goto out; } rid = 0; rv = ofw_bus_find_string_index(sc->node, "reg-names", "config", &rid); if (rv != 0) { device_printf(dev, "Cannot get config space memory\n"); rv = ENXIO; goto out; } sc->cfg_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->cfg_res == NULL) { device_printf(dev, "Cannot allocate config space(rid: %d)\n", rid); rv = ENXIO; goto out; } /* Fill up config region related variables */ sc->cfg_size = rman_get_size(sc->cfg_res); sc->cfg_pa = rman_get_start(sc->cfg_res) ; if (bootverbose) device_printf(dev, "Bus is%s cache-coherent\n", sc->coherent ? "" : " not"); rv = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ 1, 0, /* alignment, bounds */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE, /* maxsize */ BUS_SPACE_UNRESTRICTED, /* nsegments */ BUS_SPACE_MAXSIZE, /* maxsegsize */ sc->coherent ? BUS_DMA_COHERENT : 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->dmat); if (rv != 0) goto out; rv = ofw_pci_init(dev); if (rv != 0) goto out; rv = pci_dw_decode_ranges(sc, sc->ofw_pci.sc_range, sc->ofw_pci.sc_nrange); if (rv != 0) goto out; rv = pci_dw_setup_hw(sc); if (rv != 0) goto out; device_add_child(dev, "pci", -1); return (0); out: /* XXX Cleanup */ return (rv); } static device_method_t pci_dw_methods[] = { /* Bus interface */ DEVMETHOD(bus_get_dma_tag, pci_dw_get_dma_tag), /* pcib interface */ DEVMETHOD(pcib_read_config, pci_dw_read_config), DEVMETHOD(pcib_write_config, pci_dw_write_config), DEVMETHOD(pcib_alloc_msi, pci_dw_alloc_msi), DEVMETHOD(pcib_release_msi, pci_dw_release_msi), DEVMETHOD(pcib_alloc_msix, pci_dw_alloc_msix), DEVMETHOD(pcib_release_msix, pci_dw_release_msix), DEVMETHOD(pcib_map_msi, pci_dw_map_msi), DEVMETHOD(pcib_get_id, pci_dw_get_id), /* OFW bus interface */ 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), /* PCI DW interface */ DEVMETHOD(pci_dw_dbi_read, pci_dw_dbi_read), DEVMETHOD(pci_dw_dbi_write, pci_dw_dbi_write), DEVMETHOD_END }; DEFINE_CLASS_1(pcib, pci_dw_driver, pci_dw_methods, sizeof(struct pci_dw_softc), ofw_pci_driver); diff --git a/sys/dev/pci/pci_dw.h b/sys/dev/pci/pci_dw.h index d6248cf87298..c2c9249449bb 100644 --- a/sys/dev/pci/pci_dw.h +++ b/sys/dev/pci/pci_dw.h @@ -1,153 +1,153 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2019 Michal Meloun * * 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_DW_H_ #define _PCI_DW_H_ #include "pci_dw_if.h" /* DesignWare CIe configuration registers */ #define DW_PORT_LINK_CTRL 0x710 #define PORT_LINK_CAPABLE(n) (((n) & 0x3F) << 16) #define PORT_LINK_CAPABLE_1 0x01 #define PORT_LINK_CAPABLE_2 0x03 #define PORT_LINK_CAPABLE_4 0x07 #define PORT_LINK_CAPABLE_8 0x0F #define PORT_LINK_CAPABLE_16 0x1F #define PORT_LINK_CAPABLE_32 0x3F #define DW_GEN2_CTRL 0x80C #define DIRECT_SPEED_CHANGE (1 << 17) #define GEN2_CTRL_NUM_OF_LANES(n) (((n) & 0x3F) << 8) #define GEN2_CTRL_NUM_OF_LANES_1 0x01 #define GEN2_CTRL_NUM_OF_LANES_2 0x03 #define GEN2_CTRL_NUM_OF_LANES_4 0x07 #define GEN2_CTRL_NUM_OF_LANES_8 0x0F #define GEN2_CTRL_NUM_OF_LANES_16 0x1F #define GEN2_CTRL_NUM_OF_LANES_32 0x3F #define DW_MSI_ADDR_LO 0x820 #define DW_MSI_ADDR_HI 0x824 #define DW_MSI_INTR0_ENABLE 0x828 #define DW_MSI_INTR0_MASK 0x82C #define DW_MSI_INTR0_STATUS 0x830 #define DW_MISC_CONTROL_1 0x8BC #define DBI_RO_WR_EN (1 << 0) #define DW_IATU_VIEWPORT 0x900 #define IATU_REGION_INBOUND (1U << 31) #define IATU_REGION_INDEX(x) ((x) & 0x7) #define DW_IATU_CTRL1 0x904 #define IATU_CTRL1_TYPE(x) ((x) & 0x1F) #define IATU_CTRL1_TYPE_MEM 0x0 #define IATU_CTRL1_TYPE_IO 0x2 #define IATU_CTRL1_TYPE_CFG0 0x4 #define IATU_CTRL1_TYPE_CFG1 0x5 #define DW_IATU_CTRL2 0x908 #define IATU_CTRL2_REGION_EN (1U << 31) #define DW_IATU_LWR_BASE_ADDR 0x90C #define DW_IATU_UPPER_BASE_ADDR 0x910 #define DW_IATU_LIMIT_ADDR 0x914 #define DW_IATU_LWR_TARGET_ADDR 0x918 #define DW_IATU_UPPER_TARGET_ADDR 0x91C struct pci_dw_softc { struct ofw_pci_softc ofw_pci; /* Must be first */ /* Filled by attachement stub */ struct resource *dbi_res; /* pci_dw variables */ device_t dev; phandle_t node; struct mtx mtx; struct resource *cfg_res; - struct ofw_pci_range mem_range; - struct ofw_pci_range pref_mem_range; struct ofw_pci_range io_range; + struct ofw_pci_range *mem_ranges; + int num_mem_ranges; bool coherent; bus_dma_tag_t dmat; int num_lanes; int num_viewport; bus_addr_t cfg_pa; /* PA of config memoty */ bus_size_t cfg_size; /* size of config region */ u_int bus_start; u_int bus_end; u_int root_bus; u_int sub_bus; }; DECLARE_CLASS(pci_dw_driver); static inline void pci_dw_dbi_wr4(device_t dev, u_int reg, uint32_t val) { PCI_DW_DBI_WRITE(dev, reg, val, 4); } static inline void pci_dw_dbi_wr2(device_t dev, u_int reg, uint16_t val) { PCI_DW_DBI_WRITE(dev, reg, val, 2); } static inline void pci_dw_dbi_wr1(device_t dev, u_int reg, uint8_t val) { PCI_DW_DBI_WRITE(dev, reg, val, 1); } static inline uint32_t pci_dw_dbi_rd4(device_t dev, u_int reg) { return (PCI_DW_DBI_READ(dev, reg, 4)); } static inline uint16_t pci_dw_dbi_rd2(device_t dev, u_int reg) { return ((uint16_t)PCI_DW_DBI_READ(dev, reg, 2)); } static inline uint8_t pci_dw_dbi_rd1(device_t dev, u_int reg) { return ((uint8_t)PCI_DW_DBI_READ(dev, reg, 1)); } int pci_dw_init(device_t); #endif /* __PCI_HOST_GENERIC_H_ */