126abae3fSMichal Meloun /*- 226abae3fSMichal Meloun * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 326abae3fSMichal Meloun * 426abae3fSMichal Meloun * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org> 526abae3fSMichal Meloun * 626abae3fSMichal Meloun * Redistribution and use in source and binary forms, with or without 726abae3fSMichal Meloun * modification, are permitted provided that the following conditions 826abae3fSMichal Meloun * are met: 926abae3fSMichal Meloun * 1. Redistributions of source code must retain the above copyright 1026abae3fSMichal Meloun * notice, this list of conditions and the following disclaimer. 1126abae3fSMichal Meloun * 2. Redistributions in binary form must reproduce the above copyright 1226abae3fSMichal Meloun * notice, this list of conditions and the following disclaimer in the 1326abae3fSMichal Meloun * documentation and/or other materials provided with the distribution. 1426abae3fSMichal Meloun * 1526abae3fSMichal Meloun * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1626abae3fSMichal Meloun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1726abae3fSMichal Meloun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1826abae3fSMichal Meloun * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1926abae3fSMichal Meloun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2026abae3fSMichal Meloun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2126abae3fSMichal Meloun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2226abae3fSMichal Meloun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2326abae3fSMichal Meloun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2426abae3fSMichal Meloun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2526abae3fSMichal Meloun * SUCH DAMAGE. 2626abae3fSMichal Meloun * 2726abae3fSMichal Meloun */ 2826abae3fSMichal Meloun 2926abae3fSMichal Meloun /* Base class for all Synopsys DesignWare PCI/PCIe drivers */ 3026abae3fSMichal Meloun 3126abae3fSMichal Meloun #include <sys/cdefs.h> 3226abae3fSMichal Meloun __FBSDID("$FreeBSD$"); 3326abae3fSMichal Meloun 3426abae3fSMichal Meloun #include <sys/param.h> 3526abae3fSMichal Meloun #include <sys/systm.h> 3626abae3fSMichal Meloun #include <sys/bus.h> 3726abae3fSMichal Meloun #include <sys/devmap.h> 3826abae3fSMichal Meloun #include <sys/proc.h> 3926abae3fSMichal Meloun #include <sys/kernel.h> 4026abae3fSMichal Meloun #include <sys/lock.h> 4126abae3fSMichal Meloun #include <sys/malloc.h> 4226abae3fSMichal Meloun #include <sys/module.h> 4326abae3fSMichal Meloun #include <sys/mutex.h> 4426abae3fSMichal Meloun #include <sys/rman.h> 4526abae3fSMichal Meloun 4626abae3fSMichal Meloun #include <machine/bus.h> 4726abae3fSMichal Meloun #include <machine/intr.h> 4826abae3fSMichal Meloun #include <machine/resource.h> 4926abae3fSMichal Meloun 5026abae3fSMichal Meloun #include <dev/ofw/ofw_bus.h> 5126abae3fSMichal Meloun #include <dev/ofw/ofw_bus_subr.h> 5226abae3fSMichal Meloun #include <dev/ofw/ofw_pci.h> 5326abae3fSMichal Meloun #include <dev/ofw/ofwpci.h> 5426abae3fSMichal Meloun #include <dev/pci/pcivar.h> 5526abae3fSMichal Meloun #include <dev/pci/pcireg.h> 5626abae3fSMichal Meloun #include <dev/pci/pcib_private.h> 5726abae3fSMichal Meloun #include <dev/pci/pci_dw.h> 5826abae3fSMichal Meloun 5926abae3fSMichal Meloun #include "pcib_if.h" 6026abae3fSMichal Meloun #include "pci_dw_if.h" 6126abae3fSMichal Meloun 6226abae3fSMichal Meloun #define DEBUG 6326abae3fSMichal Meloun #ifdef DEBUG 6426abae3fSMichal Meloun #define debugf(fmt, args...) do { printf(fmt,##args); } while (0) 6526abae3fSMichal Meloun #else 6626abae3fSMichal Meloun #define debugf(fmt, args...) 6726abae3fSMichal Meloun #endif 6826abae3fSMichal Meloun 6926abae3fSMichal Meloun #define DBI_WR1(sc, reg, val) pci_dw_dbi_wr1((sc)->dev, reg, val) 7026abae3fSMichal Meloun #define DBI_WR2(sc, reg, val) pci_dw_dbi_wr2((sc)->dev, reg, val) 7126abae3fSMichal Meloun #define DBI_WR4(sc, reg, val) pci_dw_dbi_wr4((sc)->dev, reg, val) 7226abae3fSMichal Meloun #define DBI_RD1(sc, reg) pci_dw_dbi_rd1((sc)->dev, reg) 7326abae3fSMichal Meloun #define DBI_RD2(sc, reg) pci_dw_dbi_rd2((sc)->dev, reg) 7426abae3fSMichal Meloun #define DBI_RD4(sc, reg) pci_dw_dbi_rd4((sc)->dev, reg) 7526abae3fSMichal Meloun 7626abae3fSMichal Meloun #define PCI_BUS_SHIFT 20 7726abae3fSMichal Meloun #define PCI_SLOT_SHIFT 15 7826abae3fSMichal Meloun #define PCI_FUNC_SHIFT 12 7926abae3fSMichal Meloun #define PCI_BUS_MASK 0xFF 8026abae3fSMichal Meloun #define PCI_SLOT_MASK 0x1F 8126abae3fSMichal Meloun #define PCI_FUNC_MASK 0x07 8226abae3fSMichal Meloun #define PCI_REG_MASK 0xFFF 8326abae3fSMichal Meloun 8426abae3fSMichal Meloun #define IATU_CFG_BUS(bus) ((uint64_t)((bus) & 0xff) << 24) 8526abae3fSMichal Meloun #define IATU_CFG_SLOT(slot) ((uint64_t)((slot) & 0x1f) << 19) 8626abae3fSMichal Meloun #define IATU_CFG_FUNC(func) ((uint64_t)((func) & 0x07) << 16) 8726abae3fSMichal Meloun 8826abae3fSMichal Meloun static uint32_t 8926abae3fSMichal Meloun pci_dw_dbi_read(device_t dev, u_int reg, int width) 9026abae3fSMichal Meloun { 9126abae3fSMichal Meloun struct pci_dw_softc *sc; 9226abae3fSMichal Meloun 9326abae3fSMichal Meloun sc = device_get_softc(dev); 9426abae3fSMichal Meloun MPASS(sc->dbi_res != NULL); 9526abae3fSMichal Meloun 9626abae3fSMichal Meloun switch (width) { 9726abae3fSMichal Meloun case 4: 9826abae3fSMichal Meloun return (bus_read_4(sc->dbi_res, reg)); 9926abae3fSMichal Meloun case 2: 10026abae3fSMichal Meloun return (bus_read_2(sc->dbi_res, reg)); 10126abae3fSMichal Meloun case 1: 10226abae3fSMichal Meloun return (bus_read_1(sc->dbi_res, reg)); 10326abae3fSMichal Meloun default: 10426abae3fSMichal Meloun device_printf(sc->dev, "Unsupported width: %d\n", width); 10526abae3fSMichal Meloun return (0xFFFFFFFF); 10626abae3fSMichal Meloun } 10726abae3fSMichal Meloun } 10826abae3fSMichal Meloun 10926abae3fSMichal Meloun static void 11026abae3fSMichal Meloun pci_dw_dbi_write(device_t dev, u_int reg, uint32_t val, int width) 11126abae3fSMichal Meloun { 11226abae3fSMichal Meloun struct pci_dw_softc *sc; 11326abae3fSMichal Meloun 11426abae3fSMichal Meloun sc = device_get_softc(dev); 11526abae3fSMichal Meloun MPASS(sc->dbi_res != NULL); 11626abae3fSMichal Meloun 11726abae3fSMichal Meloun switch (width) { 11826abae3fSMichal Meloun case 4: 11926abae3fSMichal Meloun bus_write_4(sc->dbi_res, reg, val); 12026abae3fSMichal Meloun break; 12126abae3fSMichal Meloun case 2: 12226abae3fSMichal Meloun bus_write_2(sc->dbi_res, reg, val); 12326abae3fSMichal Meloun break; 12426abae3fSMichal Meloun case 1: 12526abae3fSMichal Meloun bus_write_1(sc->dbi_res, reg, val); 12626abae3fSMichal Meloun break; 12726abae3fSMichal Meloun default: 12826abae3fSMichal Meloun device_printf(sc->dev, "Unsupported width: %d\n", width); 12926abae3fSMichal Meloun break; 13026abae3fSMichal Meloun } 13126abae3fSMichal Meloun } 13226abae3fSMichal Meloun 13326abae3fSMichal Meloun static void 13426abae3fSMichal Meloun pci_dw_dbi_protect(struct pci_dw_softc *sc, bool protect) 13526abae3fSMichal Meloun { 13626abae3fSMichal Meloun uint32_t reg; 13726abae3fSMichal Meloun 13826abae3fSMichal Meloun reg = DBI_RD4(sc, DW_MISC_CONTROL_1); 13926abae3fSMichal Meloun if (protect) 14026abae3fSMichal Meloun reg &= ~DBI_RO_WR_EN; 14126abae3fSMichal Meloun else 14226abae3fSMichal Meloun reg |= DBI_RO_WR_EN; 14326abae3fSMichal Meloun DBI_WR4(sc, DW_MISC_CONTROL_1, reg); 14426abae3fSMichal Meloun } 14526abae3fSMichal Meloun 14626abae3fSMichal Meloun static bool 14726abae3fSMichal Meloun pci_dw_check_dev(struct pci_dw_softc *sc, u_int bus, u_int slot, u_int func, 14826abae3fSMichal Meloun u_int reg) 14926abae3fSMichal Meloun { 15026abae3fSMichal Meloun bool status; 15126abae3fSMichal Meloun int rv; 15226abae3fSMichal Meloun 15326abae3fSMichal Meloun if (bus < sc->bus_start || bus > sc->bus_end || slot > PCI_SLOTMAX || 1541f446a11SMichal Meloun func > PCI_FUNCMAX || reg > PCIE_REGMAX) 15526abae3fSMichal Meloun return (false); 15626abae3fSMichal Meloun 15726abae3fSMichal Meloun /* link is needed for access to all non-root busses */ 15826abae3fSMichal Meloun if (bus != sc->root_bus) { 15926abae3fSMichal Meloun rv = PCI_DW_GET_LINK(sc->dev, &status); 16026abae3fSMichal Meloun if (rv != 0 || !status) 16126abae3fSMichal Meloun return (false); 16226abae3fSMichal Meloun return (true); 16326abae3fSMichal Meloun } 16426abae3fSMichal Meloun 16526abae3fSMichal Meloun /* we have only 1 device with 1 function root port */ 16626abae3fSMichal Meloun if (slot > 0 || func > 0) 16726abae3fSMichal Meloun return (false); 16826abae3fSMichal Meloun return (true); 16926abae3fSMichal Meloun } 17026abae3fSMichal Meloun 17126abae3fSMichal Meloun /* Map one uoutbound ATU region */ 17226abae3fSMichal Meloun static int 17326abae3fSMichal Meloun pci_dw_map_out_atu(struct pci_dw_softc *sc, int idx, int type, 17426abae3fSMichal Meloun uint64_t pa, uint64_t pci_addr, uint32_t size) 17526abae3fSMichal Meloun { 17626abae3fSMichal Meloun uint32_t reg; 17726abae3fSMichal Meloun int i; 17826abae3fSMichal Meloun 17926abae3fSMichal Meloun if (size == 0) 18026abae3fSMichal Meloun return (0); 18126abae3fSMichal Meloun 18226abae3fSMichal Meloun DBI_WR4(sc, DW_IATU_VIEWPORT, IATU_REGION_INDEX(idx)); 18326abae3fSMichal Meloun DBI_WR4(sc, DW_IATU_LWR_BASE_ADDR, pa & 0xFFFFFFFF); 18426abae3fSMichal Meloun DBI_WR4(sc, DW_IATU_UPPER_BASE_ADDR, (pa >> 32) & 0xFFFFFFFF); 18526abae3fSMichal Meloun DBI_WR4(sc, DW_IATU_LIMIT_ADDR, (pa + size - 1) & 0xFFFFFFFF); 18626abae3fSMichal Meloun DBI_WR4(sc, DW_IATU_LWR_TARGET_ADDR, pci_addr & 0xFFFFFFFF); 18726abae3fSMichal Meloun DBI_WR4(sc, DW_IATU_UPPER_TARGET_ADDR, (pci_addr >> 32) & 0xFFFFFFFF); 18826abae3fSMichal Meloun DBI_WR4(sc, DW_IATU_CTRL1, IATU_CTRL1_TYPE(type)); 18926abae3fSMichal Meloun DBI_WR4(sc, DW_IATU_CTRL2, IATU_CTRL2_REGION_EN); 19026abae3fSMichal Meloun 19126abae3fSMichal Meloun /* Wait until setup becomes valid */ 19226abae3fSMichal Meloun for (i = 10; i > 0; i--) { 19326abae3fSMichal Meloun reg = DBI_RD4(sc, DW_IATU_CTRL2); 19426abae3fSMichal Meloun if (reg & IATU_CTRL2_REGION_EN) 19526abae3fSMichal Meloun return (0); 19626abae3fSMichal Meloun DELAY(5); 19726abae3fSMichal Meloun } 19826abae3fSMichal Meloun device_printf(sc->dev, 19926abae3fSMichal Meloun "Cannot map outbound region(%d) in iATU\n", idx); 20026abae3fSMichal Meloun return (ETIMEDOUT); 20126abae3fSMichal Meloun } 20226abae3fSMichal Meloun 20326abae3fSMichal Meloun static int 20426abae3fSMichal Meloun pci_dw_setup_hw(struct pci_dw_softc *sc) 20526abae3fSMichal Meloun { 20626abae3fSMichal Meloun uint32_t reg; 20726abae3fSMichal Meloun int rv; 20826abae3fSMichal Meloun 20926abae3fSMichal Meloun pci_dw_dbi_protect(sc, false); 21026abae3fSMichal Meloun 21126abae3fSMichal Meloun /* Setup config registers */ 21226abae3fSMichal Meloun DBI_WR1(sc, PCIR_CLASS, PCIC_BRIDGE); 21326abae3fSMichal Meloun DBI_WR1(sc, PCIR_SUBCLASS, PCIS_BRIDGE_PCI); 21426abae3fSMichal Meloun DBI_WR4(sc, PCIR_BAR(0), 4); 21526abae3fSMichal Meloun DBI_WR4(sc, PCIR_BAR(1), 0); 21626abae3fSMichal Meloun DBI_WR1(sc, PCIR_INTPIN, 1); 21726abae3fSMichal Meloun DBI_WR1(sc, PCIR_PRIBUS_1, sc->root_bus); 21826abae3fSMichal Meloun DBI_WR1(sc, PCIR_SECBUS_1, sc->sub_bus); 21926abae3fSMichal Meloun DBI_WR1(sc, PCIR_SUBBUS_1, sc->bus_end); 22026abae3fSMichal Meloun DBI_WR2(sc, PCIR_COMMAND, 22126abae3fSMichal Meloun PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | 22226abae3fSMichal Meloun PCIM_CMD_BUSMASTEREN | PCIM_CMD_SERRESPEN); 22326abae3fSMichal Meloun pci_dw_dbi_protect(sc, true); 22426abae3fSMichal Meloun 22526abae3fSMichal Meloun /* Setup outbound memory window */ 22626abae3fSMichal Meloun rv = pci_dw_map_out_atu(sc, 0, IATU_CTRL1_TYPE_MEM, 22726abae3fSMichal Meloun sc->mem_range.host, sc->mem_range.pci, sc->mem_range.size); 22826abae3fSMichal Meloun if (rv != 0) 22926abae3fSMichal Meloun return (rv); 23026abae3fSMichal Meloun 23126abae3fSMichal Meloun /* If we have enouht viewports ..*/ 2321f446a11SMichal Meloun if (sc->num_viewport >= 3 && sc->io_range.size != 0) { 23326abae3fSMichal Meloun /* Setup outbound I/O window */ 23426abae3fSMichal Meloun rv = pci_dw_map_out_atu(sc, 0, IATU_CTRL1_TYPE_MEM, 23526abae3fSMichal Meloun sc->io_range.host, sc->io_range.pci, sc->io_range.size); 23626abae3fSMichal Meloun if (rv != 0) 23726abae3fSMichal Meloun return (rv); 23826abae3fSMichal Meloun } 23926abae3fSMichal Meloun /* XXX Should we handle also prefetch memory? */ 24026abae3fSMichal Meloun 24126abae3fSMichal Meloun /* Adjust number of lanes */ 24226abae3fSMichal Meloun reg = DBI_RD4(sc, DW_PORT_LINK_CTRL); 24326abae3fSMichal Meloun reg &= ~PORT_LINK_CAPABLE(~0); 24426abae3fSMichal Meloun switch (sc->num_lanes) { 24526abae3fSMichal Meloun case 1: 24626abae3fSMichal Meloun reg |= PORT_LINK_CAPABLE(PORT_LINK_CAPABLE_1); 24726abae3fSMichal Meloun break; 24826abae3fSMichal Meloun case 2: 24926abae3fSMichal Meloun reg |= PORT_LINK_CAPABLE(PORT_LINK_CAPABLE_2); 25026abae3fSMichal Meloun break; 25126abae3fSMichal Meloun case 4: 25226abae3fSMichal Meloun reg |= PORT_LINK_CAPABLE(PORT_LINK_CAPABLE_4); 25326abae3fSMichal Meloun break; 25426abae3fSMichal Meloun case 8: 25526abae3fSMichal Meloun reg |= PORT_LINK_CAPABLE(PORT_LINK_CAPABLE_8); 25626abae3fSMichal Meloun break; 25726abae3fSMichal Meloun case 16: 25826abae3fSMichal Meloun reg |= PORT_LINK_CAPABLE(PORT_LINK_CAPABLE_16); 25926abae3fSMichal Meloun break; 26026abae3fSMichal Meloun case 32: 26126abae3fSMichal Meloun reg |= PORT_LINK_CAPABLE(PORT_LINK_CAPABLE_32); 26226abae3fSMichal Meloun break; 26326abae3fSMichal Meloun default: 26426abae3fSMichal Meloun device_printf(sc->dev, 26526abae3fSMichal Meloun "'num-lanes' property have invalid value: %d\n", 26626abae3fSMichal Meloun sc->num_lanes); 26726abae3fSMichal Meloun return (EINVAL); 26826abae3fSMichal Meloun } 26926abae3fSMichal Meloun DBI_WR4(sc, DW_PORT_LINK_CTRL, reg); 27026abae3fSMichal Meloun 27126abae3fSMichal Meloun /* And link width */ 27226abae3fSMichal Meloun reg = DBI_RD4(sc, DW_GEN2_CTRL); 27326abae3fSMichal Meloun reg &= ~GEN2_CTRL_NUM_OF_LANES(~0); 27426abae3fSMichal Meloun switch (sc->num_lanes) { 27526abae3fSMichal Meloun case 1: 27626abae3fSMichal Meloun reg |= GEN2_CTRL_NUM_OF_LANES(GEN2_CTRL_NUM_OF_LANES_1); 27726abae3fSMichal Meloun break; 27826abae3fSMichal Meloun case 2: 27926abae3fSMichal Meloun reg |= GEN2_CTRL_NUM_OF_LANES(GEN2_CTRL_NUM_OF_LANES_2); 28026abae3fSMichal Meloun break; 28126abae3fSMichal Meloun case 4: 28226abae3fSMichal Meloun reg |= GEN2_CTRL_NUM_OF_LANES(GEN2_CTRL_NUM_OF_LANES_4); 28326abae3fSMichal Meloun break; 28426abae3fSMichal Meloun case 8: 28526abae3fSMichal Meloun reg |= GEN2_CTRL_NUM_OF_LANES(GEN2_CTRL_NUM_OF_LANES_8); 28626abae3fSMichal Meloun break; 28726abae3fSMichal Meloun case 16: 28826abae3fSMichal Meloun reg |= GEN2_CTRL_NUM_OF_LANES(GEN2_CTRL_NUM_OF_LANES_16); 28926abae3fSMichal Meloun break; 29026abae3fSMichal Meloun case 32: 29126abae3fSMichal Meloun reg |= GEN2_CTRL_NUM_OF_LANES(GEN2_CTRL_NUM_OF_LANES_32); 29226abae3fSMichal Meloun break; 29326abae3fSMichal Meloun } 29426abae3fSMichal Meloun DBI_WR4(sc, DW_GEN2_CTRL, reg); 29526abae3fSMichal Meloun 29626abae3fSMichal Meloun reg = DBI_RD4(sc, DW_GEN2_CTRL); 29726abae3fSMichal Meloun reg |= DIRECT_SPEED_CHANGE; 29826abae3fSMichal Meloun DBI_WR4(sc, DW_GEN2_CTRL, reg); 29926abae3fSMichal Meloun 30026abae3fSMichal Meloun return (0); 30126abae3fSMichal Meloun } 30226abae3fSMichal Meloun 30326abae3fSMichal Meloun static int 30426abae3fSMichal Meloun pci_dw_decode_ranges(struct pci_dw_softc *sc, struct ofw_pci_range *ranges, 30526abae3fSMichal Meloun int nranges) 30626abae3fSMichal Meloun { 30726abae3fSMichal Meloun int i; 30826abae3fSMichal Meloun 30926abae3fSMichal Meloun for (i = 0; i < nranges; i++) { 31026abae3fSMichal Meloun if ((ranges[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) == 31126abae3fSMichal Meloun OFW_PCI_PHYS_HI_SPACE_IO) { 31226abae3fSMichal Meloun if (sc->io_range.size != 0) { 31326abae3fSMichal Meloun device_printf(sc->dev, 31426abae3fSMichal Meloun "Duplicated IO range found in DT\n"); 31526abae3fSMichal Meloun return (ENXIO); 31626abae3fSMichal Meloun } 31726abae3fSMichal Meloun sc->io_range = ranges[i]; 31826abae3fSMichal Meloun } 31926abae3fSMichal Meloun if (((ranges[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) == 32026abae3fSMichal Meloun OFW_PCI_PHYS_HI_SPACE_MEM32)) { 32126abae3fSMichal Meloun if (ranges[i].pci_hi & OFW_PCI_PHYS_HI_PREFETCHABLE) { 32226abae3fSMichal Meloun if (sc->pref_mem_range.size != 0) { 32326abae3fSMichal Meloun device_printf(sc->dev, 32426abae3fSMichal Meloun "Duplicated memory range found " 32526abae3fSMichal Meloun "in DT\n"); 32626abae3fSMichal Meloun return (ENXIO); 32726abae3fSMichal Meloun } 32826abae3fSMichal Meloun sc->pref_mem_range = ranges[i]; 32926abae3fSMichal Meloun } else { 33026abae3fSMichal Meloun if (sc->mem_range.size != 0) { 33126abae3fSMichal Meloun device_printf(sc->dev, 33226abae3fSMichal Meloun "Duplicated memory range found " 33326abae3fSMichal Meloun "in DT\n"); 33426abae3fSMichal Meloun return (ENXIO); 33526abae3fSMichal Meloun } 33626abae3fSMichal Meloun sc->mem_range = ranges[i]; 33726abae3fSMichal Meloun } 33826abae3fSMichal Meloun } 33926abae3fSMichal Meloun } 3401f446a11SMichal Meloun if (sc->mem_range.size == 0) { 34126abae3fSMichal Meloun device_printf(sc->dev, 34226abae3fSMichal Meloun " Not all required ranges are found in DT\n"); 34326abae3fSMichal Meloun return (ENXIO); 34426abae3fSMichal Meloun } 34526abae3fSMichal Meloun return (0); 34626abae3fSMichal Meloun } 34726abae3fSMichal Meloun 34826abae3fSMichal Meloun /*----------------------------------------------------------------------------- 34926abae3fSMichal Meloun * 35026abae3fSMichal Meloun * P C I B I N T E R F A C E 35126abae3fSMichal Meloun */ 35226abae3fSMichal Meloun 35326abae3fSMichal Meloun static uint32_t 35426abae3fSMichal Meloun pci_dw_read_config(device_t dev, u_int bus, u_int slot, 35526abae3fSMichal Meloun u_int func, u_int reg, int bytes) 35626abae3fSMichal Meloun { 35726abae3fSMichal Meloun struct pci_dw_softc *sc; 35826abae3fSMichal Meloun struct resource *res; 35926abae3fSMichal Meloun uint32_t data; 36026abae3fSMichal Meloun uint64_t addr; 36126abae3fSMichal Meloun int type, rv; 36226abae3fSMichal Meloun 36326abae3fSMichal Meloun sc = device_get_softc(dev); 36426abae3fSMichal Meloun 36526abae3fSMichal Meloun if (!pci_dw_check_dev(sc, bus, slot, func, reg)) 36626abae3fSMichal Meloun return (0xFFFFFFFFU); 36726abae3fSMichal Meloun 36826abae3fSMichal Meloun if (bus == sc->root_bus) { 36926abae3fSMichal Meloun res = (sc->dbi_res); 37026abae3fSMichal Meloun } else { 37126abae3fSMichal Meloun addr = IATU_CFG_BUS(bus) | IATU_CFG_SLOT(slot) | 37226abae3fSMichal Meloun IATU_CFG_FUNC(func); 37326abae3fSMichal Meloun if (bus == sc->sub_bus) 37426abae3fSMichal Meloun type = IATU_CTRL1_TYPE_CFG0; 37526abae3fSMichal Meloun else 37626abae3fSMichal Meloun type = IATU_CTRL1_TYPE_CFG1; 37726abae3fSMichal Meloun rv = pci_dw_map_out_atu(sc, 1, type, 37826abae3fSMichal Meloun sc->cfg_pa, addr, sc->cfg_size); 37926abae3fSMichal Meloun if (rv != 0) 38026abae3fSMichal Meloun return (0xFFFFFFFFU); 38126abae3fSMichal Meloun res = sc->cfg_res; 38226abae3fSMichal Meloun } 38326abae3fSMichal Meloun 38426abae3fSMichal Meloun switch (bytes) { 38526abae3fSMichal Meloun case 1: 38626abae3fSMichal Meloun data = bus_read_1(res, reg); 38726abae3fSMichal Meloun break; 38826abae3fSMichal Meloun case 2: 38926abae3fSMichal Meloun data = bus_read_2(res, reg); 39026abae3fSMichal Meloun break; 39126abae3fSMichal Meloun case 4: 39226abae3fSMichal Meloun data = bus_read_4(res, reg); 39326abae3fSMichal Meloun break; 39426abae3fSMichal Meloun default: 39526abae3fSMichal Meloun data = 0xFFFFFFFFU; 39626abae3fSMichal Meloun } 39726abae3fSMichal Meloun 39826abae3fSMichal Meloun return (data); 39926abae3fSMichal Meloun 40026abae3fSMichal Meloun } 40126abae3fSMichal Meloun 40226abae3fSMichal Meloun static void 40326abae3fSMichal Meloun pci_dw_write_config(device_t dev, u_int bus, u_int slot, 40426abae3fSMichal Meloun u_int func, u_int reg, uint32_t val, int bytes) 40526abae3fSMichal Meloun { 40626abae3fSMichal Meloun struct pci_dw_softc *sc; 40726abae3fSMichal Meloun struct resource *res; 40826abae3fSMichal Meloun uint64_t addr; 40926abae3fSMichal Meloun int type, rv; 41026abae3fSMichal Meloun 41126abae3fSMichal Meloun sc = device_get_softc(dev); 41226abae3fSMichal Meloun if (!pci_dw_check_dev(sc, bus, slot, func, reg)) 41326abae3fSMichal Meloun return; 41426abae3fSMichal Meloun 41526abae3fSMichal Meloun if (bus == sc->root_bus) { 41626abae3fSMichal Meloun res = (sc->dbi_res); 41726abae3fSMichal Meloun } else { 41826abae3fSMichal Meloun addr = IATU_CFG_BUS(bus) | IATU_CFG_SLOT(slot) | 41926abae3fSMichal Meloun IATU_CFG_FUNC(func); 42026abae3fSMichal Meloun if (bus == sc->sub_bus) 42126abae3fSMichal Meloun type = IATU_CTRL1_TYPE_CFG0; 42226abae3fSMichal Meloun else 42326abae3fSMichal Meloun type = IATU_CTRL1_TYPE_CFG1; 42426abae3fSMichal Meloun rv = pci_dw_map_out_atu(sc, 1, type, 42526abae3fSMichal Meloun sc->cfg_pa, addr, sc->cfg_size); 42626abae3fSMichal Meloun if (rv != 0) 42726abae3fSMichal Meloun return ; 42826abae3fSMichal Meloun res = sc->cfg_res; 42926abae3fSMichal Meloun } 43026abae3fSMichal Meloun 43126abae3fSMichal Meloun switch (bytes) { 43226abae3fSMichal Meloun case 1: 43326abae3fSMichal Meloun bus_write_1(res, reg, val); 43426abae3fSMichal Meloun break; 43526abae3fSMichal Meloun case 2: 43626abae3fSMichal Meloun bus_write_2(res, reg, val); 43726abae3fSMichal Meloun break; 43826abae3fSMichal Meloun case 4: 43926abae3fSMichal Meloun bus_write_4(res, reg, val); 44026abae3fSMichal Meloun break; 44126abae3fSMichal Meloun default: 44226abae3fSMichal Meloun break; 44326abae3fSMichal Meloun } 44426abae3fSMichal Meloun } 44526abae3fSMichal Meloun 44626abae3fSMichal Meloun static int 44726abae3fSMichal Meloun pci_dw_alloc_msi(device_t pci, device_t child, int count, 44826abae3fSMichal Meloun int maxcount, int *irqs) 44926abae3fSMichal Meloun { 45026abae3fSMichal Meloun phandle_t msi_parent; 45126abae3fSMichal Meloun int rv; 45226abae3fSMichal Meloun 45326abae3fSMichal Meloun rv = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), 45426abae3fSMichal Meloun &msi_parent, NULL); 45526abae3fSMichal Meloun if (rv != 0) 45626abae3fSMichal Meloun return (rv); 45726abae3fSMichal Meloun 45826abae3fSMichal Meloun return (intr_alloc_msi(pci, child, msi_parent, count, maxcount, 45926abae3fSMichal Meloun irqs)); 46026abae3fSMichal Meloun } 46126abae3fSMichal Meloun 46226abae3fSMichal Meloun static int 46326abae3fSMichal Meloun pci_dw_release_msi(device_t pci, device_t child, int count, int *irqs) 46426abae3fSMichal Meloun { 46526abae3fSMichal Meloun phandle_t msi_parent; 46626abae3fSMichal Meloun int rv; 46726abae3fSMichal Meloun 46826abae3fSMichal Meloun rv = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), 46926abae3fSMichal Meloun &msi_parent, NULL); 47026abae3fSMichal Meloun if (rv != 0) 47126abae3fSMichal Meloun return (rv); 47226abae3fSMichal Meloun return (intr_release_msi(pci, child, msi_parent, count, irqs)); 47326abae3fSMichal Meloun } 47426abae3fSMichal Meloun 47526abae3fSMichal Meloun static int 47626abae3fSMichal Meloun pci_dw_map_msi(device_t pci, device_t child, int irq, uint64_t *addr, 47726abae3fSMichal Meloun uint32_t *data) 47826abae3fSMichal Meloun { 47926abae3fSMichal Meloun phandle_t msi_parent; 48026abae3fSMichal Meloun int rv; 48126abae3fSMichal Meloun 48226abae3fSMichal Meloun rv = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), 48326abae3fSMichal Meloun &msi_parent, NULL); 48426abae3fSMichal Meloun if (rv != 0) 48526abae3fSMichal Meloun return (rv); 48626abae3fSMichal Meloun 48726abae3fSMichal Meloun return (intr_map_msi(pci, child, msi_parent, irq, addr, data)); 48826abae3fSMichal Meloun } 48926abae3fSMichal Meloun 49026abae3fSMichal Meloun static int 49126abae3fSMichal Meloun pci_dw_alloc_msix(device_t pci, device_t child, int *irq) 49226abae3fSMichal Meloun { 49326abae3fSMichal Meloun phandle_t msi_parent; 49426abae3fSMichal Meloun int rv; 49526abae3fSMichal Meloun 49626abae3fSMichal Meloun rv = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), 49726abae3fSMichal Meloun &msi_parent, NULL); 49826abae3fSMichal Meloun if (rv != 0) 49926abae3fSMichal Meloun return (rv); 50026abae3fSMichal Meloun return (intr_alloc_msix(pci, child, msi_parent, irq)); 50126abae3fSMichal Meloun } 50226abae3fSMichal Meloun 50326abae3fSMichal Meloun static int 50426abae3fSMichal Meloun pci_dw_release_msix(device_t pci, device_t child, int irq) 50526abae3fSMichal Meloun { 50626abae3fSMichal Meloun phandle_t msi_parent; 50726abae3fSMichal Meloun int rv; 50826abae3fSMichal Meloun 50926abae3fSMichal Meloun rv = ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), 51026abae3fSMichal Meloun &msi_parent, NULL); 51126abae3fSMichal Meloun if (rv != 0) 51226abae3fSMichal Meloun return (rv); 51326abae3fSMichal Meloun return (intr_release_msix(pci, child, msi_parent, irq)); 51426abae3fSMichal Meloun } 51526abae3fSMichal Meloun 51626abae3fSMichal Meloun static int 51726abae3fSMichal Meloun pci_dw_get_id(device_t pci, device_t child, enum pci_id_type type, 51826abae3fSMichal Meloun uintptr_t *id) 51926abae3fSMichal Meloun { 52026abae3fSMichal Meloun phandle_t node; 52126abae3fSMichal Meloun int rv; 52226abae3fSMichal Meloun uint32_t rid; 52326abae3fSMichal Meloun uint16_t pci_rid; 52426abae3fSMichal Meloun 52526abae3fSMichal Meloun if (type != PCI_ID_MSI) 52626abae3fSMichal Meloun return (pcib_get_id(pci, child, type, id)); 52726abae3fSMichal Meloun 52826abae3fSMichal Meloun node = ofw_bus_get_node(pci); 52926abae3fSMichal Meloun pci_rid = pci_get_rid(child); 53026abae3fSMichal Meloun 53126abae3fSMichal Meloun rv = ofw_bus_msimap(node, pci_rid, NULL, &rid); 53226abae3fSMichal Meloun if (rv != 0) 53326abae3fSMichal Meloun return (rv); 53426abae3fSMichal Meloun *id = rid; 53526abae3fSMichal Meloun 53626abae3fSMichal Meloun return (0); 53726abae3fSMichal Meloun } 53826abae3fSMichal Meloun 53926abae3fSMichal Meloun /*----------------------------------------------------------------------------- 54026abae3fSMichal Meloun * 54126abae3fSMichal Meloun * B U S / D E V I C E I N T E R F A C E 54226abae3fSMichal Meloun */ 54326abae3fSMichal Meloun static bus_dma_tag_t 54426abae3fSMichal Meloun pci_dw_get_dma_tag(device_t dev, device_t child) 54526abae3fSMichal Meloun { 54626abae3fSMichal Meloun struct pci_dw_softc *sc; 54726abae3fSMichal Meloun 54826abae3fSMichal Meloun sc = device_get_softc(dev); 54926abae3fSMichal Meloun return (sc->dmat); 55026abae3fSMichal Meloun } 55126abae3fSMichal Meloun 55226abae3fSMichal Meloun int 55326abae3fSMichal Meloun pci_dw_init(device_t dev) 55426abae3fSMichal Meloun { 55526abae3fSMichal Meloun struct pci_dw_softc *sc; 55626abae3fSMichal Meloun int rv, rid; 55726abae3fSMichal Meloun 55826abae3fSMichal Meloun sc = device_get_softc(dev); 55926abae3fSMichal Meloun sc->dev = dev; 56026abae3fSMichal Meloun sc->node = ofw_bus_get_node(dev); 56126abae3fSMichal Meloun 56226abae3fSMichal Meloun mtx_init(&sc->mtx, "pci_dw_mtx", NULL, MTX_DEF); 56326abae3fSMichal Meloun 56426abae3fSMichal Meloun /* XXXn Should not be this configurable ? */ 56526abae3fSMichal Meloun sc->bus_start = 0; 56626abae3fSMichal Meloun sc->bus_end = 255; 56726abae3fSMichal Meloun sc->root_bus = 0; 56826abae3fSMichal Meloun sc->sub_bus = 1; 56926abae3fSMichal Meloun 57026abae3fSMichal Meloun /* Read FDT properties */ 57126abae3fSMichal Meloun if (!sc->coherent) 57226abae3fSMichal Meloun sc->coherent = OF_hasprop(sc->node, "dma-coherent"); 57326abae3fSMichal Meloun 57426abae3fSMichal Meloun rv = OF_getencprop(sc->node, "num-viewport", &sc->num_viewport, 57526abae3fSMichal Meloun sizeof(sc->num_viewport)); 57626abae3fSMichal Meloun if (rv != sizeof(sc->num_viewport)) 57726abae3fSMichal Meloun sc->num_viewport = 2; 57826abae3fSMichal Meloun 57926abae3fSMichal Meloun rv = OF_getencprop(sc->node, "num-lanes", &sc->num_lanes, 58026abae3fSMichal Meloun sizeof(sc->num_viewport)); 58126abae3fSMichal Meloun if (rv != sizeof(sc->num_lanes)) 58226abae3fSMichal Meloun sc->num_lanes = 1; 58326abae3fSMichal Meloun if (sc->num_lanes != 1 && sc->num_lanes != 2 && 58426abae3fSMichal Meloun sc->num_lanes != 4 && sc->num_lanes != 8) { 58526abae3fSMichal Meloun device_printf(dev, 58626abae3fSMichal Meloun "invalid number of lanes: %d\n",sc->num_lanes); 58726abae3fSMichal Meloun sc->num_lanes = 0; 58826abae3fSMichal Meloun rv = ENXIO; 58926abae3fSMichal Meloun goto out; 59026abae3fSMichal Meloun } 59126abae3fSMichal Meloun 59226abae3fSMichal Meloun rid = 0; 59326abae3fSMichal Meloun rv = ofw_bus_find_string_index(sc->node, "reg-names", "config", &rid); 59426abae3fSMichal Meloun if (rv != 0) { 59526abae3fSMichal Meloun device_printf(dev, "Cannot get config space memory\n"); 59626abae3fSMichal Meloun rv = ENXIO; 59726abae3fSMichal Meloun goto out; 59826abae3fSMichal Meloun } 59926abae3fSMichal Meloun sc->cfg_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 60026abae3fSMichal Meloun RF_ACTIVE); 60126abae3fSMichal Meloun if (sc->cfg_res == NULL) { 60226abae3fSMichal Meloun device_printf(dev, "Cannot allocate config space(rid: %d)\n", 60326abae3fSMichal Meloun rid); 60426abae3fSMichal Meloun rv = ENXIO; 60526abae3fSMichal Meloun goto out; 60626abae3fSMichal Meloun } 60726abae3fSMichal Meloun 60826abae3fSMichal Meloun /* Fill up config region related variables */ 60926abae3fSMichal Meloun sc->cfg_size = rman_get_size(sc->cfg_res); 61026abae3fSMichal Meloun sc->cfg_pa = rman_get_start(sc->cfg_res) ; 61126abae3fSMichal Meloun 61226abae3fSMichal Meloun if (bootverbose) 61326abae3fSMichal Meloun device_printf(dev, "Bus is%s cache-coherent\n", 61426abae3fSMichal Meloun sc->coherent ? "" : " not"); 61526abae3fSMichal Meloun rv = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ 61626abae3fSMichal Meloun 1, 0, /* alignment, bounds */ 61726abae3fSMichal Meloun BUS_SPACE_MAXADDR, /* lowaddr */ 61826abae3fSMichal Meloun BUS_SPACE_MAXADDR, /* highaddr */ 61926abae3fSMichal Meloun NULL, NULL, /* filter, filterarg */ 62026abae3fSMichal Meloun BUS_SPACE_MAXSIZE, /* maxsize */ 62126abae3fSMichal Meloun BUS_SPACE_UNRESTRICTED, /* nsegments */ 62226abae3fSMichal Meloun BUS_SPACE_MAXSIZE, /* maxsegsize */ 62326abae3fSMichal Meloun sc->coherent ? BUS_DMA_COHERENT : 0, /* flags */ 62426abae3fSMichal Meloun NULL, NULL, /* lockfunc, lockarg */ 62526abae3fSMichal Meloun &sc->dmat); 62626abae3fSMichal Meloun if (rv != 0) 62726abae3fSMichal Meloun goto out; 62826abae3fSMichal Meloun 62926abae3fSMichal Meloun rv = ofw_pci_init(dev); 63026abae3fSMichal Meloun if (rv != 0) 63126abae3fSMichal Meloun goto out; 63226abae3fSMichal Meloun rv = pci_dw_decode_ranges(sc, sc->ofw_pci.sc_range, 63326abae3fSMichal Meloun sc->ofw_pci.sc_nrange); 63426abae3fSMichal Meloun if (rv != 0) 63526abae3fSMichal Meloun goto out; 63626abae3fSMichal Meloun 63726abae3fSMichal Meloun rv = pci_dw_setup_hw(sc); 63826abae3fSMichal Meloun if (rv != 0) 63926abae3fSMichal Meloun goto out; 64026abae3fSMichal Meloun 64126abae3fSMichal Meloun device_add_child(dev, "pci", -1); 64226abae3fSMichal Meloun 643*cc2eaa04SMichal Meloun return (0); 64426abae3fSMichal Meloun out: 64526abae3fSMichal Meloun /* XXX Cleanup */ 64626abae3fSMichal Meloun return (rv); 64726abae3fSMichal Meloun } 64826abae3fSMichal Meloun 64926abae3fSMichal Meloun static device_method_t pci_dw_methods[] = { 65026abae3fSMichal Meloun /* Bus interface */ 65126abae3fSMichal Meloun DEVMETHOD(bus_get_dma_tag, pci_dw_get_dma_tag), 65226abae3fSMichal Meloun 65326abae3fSMichal Meloun /* pcib interface */ 65426abae3fSMichal Meloun DEVMETHOD(pcib_read_config, pci_dw_read_config), 65526abae3fSMichal Meloun DEVMETHOD(pcib_write_config, pci_dw_write_config), 65626abae3fSMichal Meloun DEVMETHOD(pcib_alloc_msi, pci_dw_alloc_msi), 65726abae3fSMichal Meloun DEVMETHOD(pcib_release_msi, pci_dw_release_msi), 65826abae3fSMichal Meloun DEVMETHOD(pcib_alloc_msix, pci_dw_alloc_msix), 65926abae3fSMichal Meloun DEVMETHOD(pcib_release_msix, pci_dw_release_msix), 66026abae3fSMichal Meloun DEVMETHOD(pcib_map_msi, pci_dw_map_msi), 66126abae3fSMichal Meloun DEVMETHOD(pcib_get_id, pci_dw_get_id), 66226abae3fSMichal Meloun 66326abae3fSMichal Meloun /* OFW bus interface */ 66426abae3fSMichal Meloun DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 66526abae3fSMichal Meloun DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 66626abae3fSMichal Meloun DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 66726abae3fSMichal Meloun DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 66826abae3fSMichal Meloun DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 66926abae3fSMichal Meloun 67026abae3fSMichal Meloun /* PCI DW interface */ 67126abae3fSMichal Meloun DEVMETHOD(pci_dw_dbi_read, pci_dw_dbi_read), 67226abae3fSMichal Meloun DEVMETHOD(pci_dw_dbi_write, pci_dw_dbi_write), 67326abae3fSMichal Meloun DEVMETHOD_END 67426abae3fSMichal Meloun }; 67526abae3fSMichal Meloun 67626abae3fSMichal Meloun DEFINE_CLASS_1(pcib, pci_dw_driver, pci_dw_methods, 67726abae3fSMichal Meloun sizeof(struct pci_dw_softc), ofw_pci_driver); 678