diff options
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/fdt/dwpcie.c | 227 |
1 files changed, 196 insertions, 31 deletions
diff --git a/sys/dev/fdt/dwpcie.c b/sys/dev/fdt/dwpcie.c index 29e35de5fa3..a19ef86f149 100644 --- a/sys/dev/fdt/dwpcie.c +++ b/sys/dev/fdt/dwpcie.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dwpcie.c,v 1.2 2018/04/03 09:31:00 kettenis Exp $ */ +/* $OpenBSD: dwpcie.c,v 1.3 2018/04/05 21:41:49 kettenis Exp $ */ /* * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org> * @@ -31,17 +31,41 @@ #include <dev/ofw/openfirm.h> #include <dev/ofw/ofw_clock.h> -#include <dev/ofw/ofw_misc.h> +#include <dev/ofw/ofw_pinctrl.h> #include <dev/ofw/fdt.h> -#define DWPCIE_MISC_CONTROL_1 0x8bc -#define DWPCIE_MISC_CONTROL_1_DBI_RO_WR_EN (1 << 0) -#define DWPCIE_ATU_VIEWPORT 0x900 -#define DWPCIE_ATU_RC1 0x904 -#define DWPCIE_ATU_RC1_TYPE_CFG0 4 -#define DWPCIE_ATU_RC1_TYPE_CFG1 5 -#define DWPCIE_ATU_RLTA 0x918 -#define DWPCIE_ATU_RUTA 0x91c +/* Registers */ +#define MISC_CONTROL_1 0x8bc +#define MISC_CONTROL_1_DBI_RO_WR_EN (1 << 0) +#define IATU_VIEWPORT 0x900 +#define IATU_REGION_CTRL_1 0x904 +#define IATU_REGION_CTRL_1_TYPE_CFG0 4 +#define IATU_REGION_CTRL_1_TYPE_CFG1 5 +#define IATU_REGION_CTRL_2 0x908 +#define IATU_REGION_CTRL_2_REGION_EN (1U << 31) +#define IATU_LWR_BASE_ADDR 0x90c +#define IATU_UPPER_BASE_ADDR 0x910 +#define IATU_LIMIT_ADDR 0x914 +#define IATU_LWR_TARGET_ADDR 0x918 +#define IATU_UPPER_TARGET_ADDR 0x91c + +#define PCIE_GLOBAL_CTRL 0x8000 +#define PCIE_GLOBAL_CTRL_APP_LTSSM_EN (1 << 2) +#define PCIE_GLOBAL_CTRL_DEVICE_TYPE_MASK (0xf << 4) +#define PCIE_GLOBAL_CTRL_DEVICE_TYPE_RC (0x4 << 4) +#define PCIE_GLOBAL_STATUS 0x8008 +#define PCIE_GLOBAL_STATUS_RDLH_LINK_UP (1 << 1) +#define PCIE_GLOBAL_STATUS_PHY_LINK_UP (1 << 9) +#define PCIE_PM_STATUS 0x8014 +#define PCIE_ARCACHE_TRC 0x8050 +#define PCIE_ARCACHE_TRC_DEFAULT 0x3511 +#define PCIE_AWCACHE_TRC 0x8054 +#define PCIE_AWCACHE_TRC_DEFAULT 0x5311 +#define PCIE_ARUSER 0x805c +#define PCIE_AWUSER 0x8060 +#define PCIE_AXUSER_DOMAIN_MASK (0x3 << 4) +#define PCIE_AXUSER_DOMAIN_INNER_SHARABLE (0x1 << 4) +#define PCIE_AXUSER_DOMAIN_OUTER_SHARABLE (0x2 << 4) #define HREAD4(sc, reg) \ (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) @@ -52,22 +76,36 @@ #define HCLR4(sc, reg, bits) \ HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) +struct dwpcie_range { + uint32_t flags; + uint64_t pci_base; + uint64_t phys_base; + uint64_t size; +}; + struct dwpcie_softc { struct device sc_dev; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; bus_space_handle_t sc_cfg_ioh; - int sc_node; + bus_addr_t sc_cfg_addr; + bus_size_t sc_cfg_size; + int sc_node; int sc_acells; int sc_scells; int sc_pacells; int sc_pscells; + struct dwpcie_range *sc_ranges; + int sc_rangeslen; struct arm64_pci_chipset sc_pc; struct extent *sc_busex; struct extent *sc_memex; struct extent *sc_ioex; + char sc_busex_name[32]; + char sc_ioex_name[32]; + char sc_memex_name[32]; int sc_bus; }; @@ -90,9 +128,8 @@ dwpcie_match(struct device *parent, void *match, void *aux) return OF_is_compatible(faa->fa_node, "marvell,armada8k-pcie"); } -void dwpcie_atr_init(struct dwpcie_softc *); -void dwpcie_phy_init(struct dwpcie_softc *); -void dwpcie_phy_poweron(struct dwpcie_softc *); +void dwpcie_armada8k_init(struct dwpcie_softc *); +int dwpcie_armada8k_link_up(struct dwpcie_softc *); void dwpcie_attach_hook(struct device *, struct device *, struct pcibus_attach_args *); @@ -119,6 +156,8 @@ dwpcie_attach(struct device *parent, struct device *self, void *aux) struct fdt_attach_args *faa = aux; struct pcibus_attach_args pba; uint32_t bus_range[2]; + uint32_t *ranges; + int i, j, nranges, rangeslen; if (faa->fa_nreg < 2) { printf(": no registers\n"); @@ -126,6 +165,7 @@ dwpcie_attach(struct device *parent, struct device *self, void *aux) } sc->sc_iot = faa->fa_iot; + sc->sc_node = faa->fa_node; sc->sc_acells = OF_getpropint(sc->sc_node, "#address-cells", faa->fa_acells); @@ -134,6 +174,45 @@ dwpcie_attach(struct device *parent, struct device *self, void *aux) sc->sc_pacells = faa->fa_acells; sc->sc_pscells = faa->fa_scells; + rangeslen = OF_getproplen(sc->sc_node, "ranges"); + if (rangeslen <= 0 || (rangeslen % sizeof(uint32_t)) || + (rangeslen / sizeof(uint32_t)) % (sc->sc_acells + + sc->sc_pacells + sc->sc_scells)) { + printf(": invalid ranges property\n"); + return; + } + + ranges = malloc(rangeslen, M_TEMP, M_WAITOK); + OF_getpropintarray(sc->sc_node, "ranges", ranges, + rangeslen); + + nranges = (rangeslen / sizeof(uint32_t)) / + (sc->sc_acells + sc->sc_pacells + sc->sc_scells); + sc->sc_ranges = mallocarray(nranges, + sizeof(struct dwpcie_range), M_TEMP, M_WAITOK); + sc->sc_rangeslen = nranges; + + for (i = 0, j = 0; i < nranges; i++) { + sc->sc_ranges[i].flags = ranges[j++]; + sc->sc_ranges[i].pci_base = ranges[j++]; + if (sc->sc_acells - 1 == 2) { + sc->sc_ranges[i].pci_base <<= 32; + sc->sc_ranges[i].pci_base |= ranges[j++]; + } + sc->sc_ranges[i].phys_base = ranges[j++]; + if (sc->sc_pacells == 2) { + sc->sc_ranges[i].phys_base <<= 32; + sc->sc_ranges[i].phys_base |= ranges[j++]; + } + sc->sc_ranges[i].size = ranges[j++]; + if (sc->sc_scells == 2) { + sc->sc_ranges[i].size <<= 32; + sc->sc_ranges[i].size |= ranges[j++]; + } + } + + free(ranges, M_TEMP, rangeslen); + if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size, 0, &sc->sc_ioh)) { printf(": can't map ctrl registers\n"); @@ -146,29 +225,46 @@ dwpcie_attach(struct device *parent, struct device *self, void *aux) printf(": can't map config registers\n"); return; } + sc->sc_cfg_addr = faa->fa_reg[1].addr; + sc->sc_cfg_size = faa->fa_reg[1].size; - sc->sc_node = faa->fa_node; printf("\n"); + pinctrl_byname(sc->sc_node, "default"); + clock_enable_all(sc->sc_node); - /* Clear BAR and make sure read-only bits are write-protected. */ - HSET4(sc, DWPCIE_MISC_CONTROL_1, - DWPCIE_MISC_CONTROL_1_DBI_RO_WR_EN); + if (OF_is_compatible(sc->sc_node, "marvell,armada8k-pcie")) + dwpcie_armada8k_init(sc); + + /* Enable modification of read-only bits. */ + HSET4(sc, MISC_CONTROL_1, MISC_CONTROL_1_DBI_RO_WR_EN); + + /* A Root Port is a PCI-PCI Bridge. */ + HWRITE4(sc, PCI_CLASS_REG, + PCI_CLASS_BRIDGE << PCI_CLASS_SHIFT | + PCI_SUBCLASS_BRIDGE_PCI << PCI_SUBCLASS_SHIFT); + + /* Clear BAR as U-Boot seems to leave garbage in it. */ HWRITE4(sc, PCI_MAPREG_START, PCI_MAPREG_MEM_TYPE_64BIT); HWRITE4(sc, PCI_MAPREG_START + 4, 0); - HCLR4(sc, DWPCIE_MISC_CONTROL_1, - DWPCIE_MISC_CONTROL_1_DBI_RO_WR_EN); + + /* Make sure read-only bits are write-protected. */ + HCLR4(sc, MISC_CONTROL_1, MISC_CONTROL_1_DBI_RO_WR_EN); /* Create extents for our address spaces. */ + snprintf(sc->sc_busex_name, sizeof(sc->sc_busex_name), + "%s pcibus", sc->sc_dev.dv_xname); + snprintf(sc->sc_ioex_name, sizeof(sc->sc_ioex_name), + "%s pciio", sc->sc_dev.dv_xname); + snprintf(sc->sc_memex_name, sizeof(sc->sc_memex_name), + "%s pcimem", sc->sc_dev.dv_xname); sc->sc_busex = extent_create("pcibus", 0, 255, M_DEVBUF, NULL, 0, EX_WAITOK | EX_FILLED); -#ifdef notyet - sc->sc_memex = extent_create("pcimem", 0, (u_long)-1, + sc->sc_ioex = extent_create(sc->sc_ioex_name, 0, 0xffffffff, M_DEVBUF, NULL, 0, EX_WAITOK | EX_FILLED); - sc->sc_ioex = extent_create("pciio", 0, 0xffffffff, + sc->sc_memex = extent_create(sc->sc_memex_name, 0, (u_long)-1, M_DEVBUF, NULL, 0, EX_WAITOK | EX_FILLED); -#endif /* Set up bus range. */ if (OF_getpropintarray(sc->sc_node, "bus-range", bus_range, @@ -181,6 +277,13 @@ dwpcie_attach(struct device *parent, struct device *self, void *aux) extent_free(sc->sc_busex, bus_range[0], bus_range[1] - bus_range[0] + 1, EX_WAITOK); + /* Set up memory mapped I/O range. */ + for (i = 0; i < nranges; i++) { + if ((sc->sc_ranges[i].flags & 0x03000000) == 0x02000000) + extent_free(sc->sc_memex, sc->sc_ranges[i].pci_base, + sc->sc_ranges[i].size, EX_NOWAIT); + } + sc->sc_pc.pc_conf_v = sc; sc->sc_pc.pc_attach_hook = dwpcie_attach_hook; sc->sc_pc.pc_bus_maxdevs = dwpcie_bus_maxdevs; @@ -214,11 +317,73 @@ dwpcie_attach(struct device *parent, struct device *self, void *aux) } void +dwpcie_armada8k_init(struct dwpcie_softc *sc) +{ + uint32_t reg; + int timo; + + if (!dwpcie_armada8k_link_up(sc)) { + reg = HREAD4(sc, PCIE_GLOBAL_CTRL); + reg &= ~PCIE_GLOBAL_CTRL_APP_LTSSM_EN; + HWRITE4(sc, PCIE_GLOBAL_CTRL, reg); + } + + /* Enable Root Complex mode. */ + reg = HREAD4(sc, PCIE_GLOBAL_CTRL); + reg &= ~PCIE_GLOBAL_CTRL_DEVICE_TYPE_MASK; + reg |= PCIE_GLOBAL_CTRL_DEVICE_TYPE_RC; + HWRITE4(sc, PCIE_GLOBAL_CTRL, reg); + + HWRITE4(sc, PCIE_ARCACHE_TRC, PCIE_ARCACHE_TRC_DEFAULT); + HWRITE4(sc, PCIE_AWCACHE_TRC, PCIE_AWCACHE_TRC_DEFAULT); + reg = HREAD4(sc, PCIE_ARUSER); + reg &= ~PCIE_AXUSER_DOMAIN_MASK; + reg |= PCIE_AXUSER_DOMAIN_OUTER_SHARABLE; + HWRITE4(sc, PCIE_ARUSER, reg); + reg = HREAD4(sc, PCIE_AWUSER); + reg &= ~PCIE_AXUSER_DOMAIN_MASK; + reg |= PCIE_AXUSER_DOMAIN_OUTER_SHARABLE; + HWRITE4(sc, PCIE_AWUSER, reg); + + /* Set up addres translation for PCI confg space. */ + HWRITE4(sc, IATU_VIEWPORT, 0); + HWRITE4(sc, IATU_LWR_BASE_ADDR, sc->sc_cfg_addr); + HWRITE4(sc, IATU_UPPER_BASE_ADDR, sc->sc_cfg_addr >> 32); + HWRITE4(sc, IATU_LIMIT_ADDR, sc->sc_cfg_addr + sc->sc_cfg_size - 1); + HWRITE4(sc, IATU_LWR_TARGET_ADDR, 0); + HWRITE4(sc, IATU_UPPER_TARGET_ADDR, 0); + HWRITE4(sc, IATU_REGION_CTRL_2, IATU_REGION_CTRL_2_REGION_EN); + + if (!dwpcie_armada8k_link_up(sc)) { + reg = HREAD4(sc, PCIE_GLOBAL_CTRL); + reg |= PCIE_GLOBAL_CTRL_APP_LTSSM_EN; + HWRITE4(sc, PCIE_GLOBAL_CTRL, reg); + } + + for (timo = 40; timo > 0; timo--) { + if (dwpcie_armada8k_link_up(sc)) + break; + delay(1000); + } +} + +int +dwpcie_armada8k_link_up(struct dwpcie_softc *sc) +{ + uint32_t reg, mask; + + mask = PCIE_GLOBAL_STATUS_RDLH_LINK_UP; + mask |= PCIE_GLOBAL_STATUS_PHY_LINK_UP; + reg = HREAD4(sc, PCIE_GLOBAL_STATUS); + return ((reg & mask) == mask); +} + +void dwpcie_atu_config(struct dwpcie_softc *sc, pcitag_t tag, int type) { - HWRITE4(sc, DWPCIE_ATU_VIEWPORT, 0); - HWRITE4(sc, DWPCIE_ATU_RC1, type); - HWRITE4(sc, DWPCIE_ATU_RLTA, tag); + HWRITE4(sc, IATU_VIEWPORT, 0); + HWRITE4(sc, IATU_REGION_CTRL_1, type); + HWRITE4(sc, IATU_LWR_TARGET_ADDR, tag); } void @@ -273,9 +438,9 @@ dwpcie_conf_read(void *v, pcitag_t tag, int reg) } if (bus == sc->sc_bus + 1) - dwpcie_atu_config(sc, tag, DWPCIE_ATU_RC1_TYPE_CFG0); + dwpcie_atu_config(sc, tag, IATU_REGION_CTRL_1_TYPE_CFG0); else - dwpcie_atu_config(sc, tag, DWPCIE_ATU_RC1_TYPE_CFG1); + dwpcie_atu_config(sc, tag, IATU_REGION_CTRL_1_TYPE_CFG1); return bus_space_read_4(sc->sc_iot, sc->sc_cfg_ioh, reg); } @@ -293,9 +458,9 @@ dwpcie_conf_write(void *v, pcitag_t tag, int reg, pcireg_t data) } if (bus == sc->sc_bus + 1) - dwpcie_atu_config(sc, tag, DWPCIE_ATU_RC1_TYPE_CFG0); + dwpcie_atu_config(sc, tag, IATU_REGION_CTRL_1_TYPE_CFG0); else - dwpcie_atu_config(sc, tag, DWPCIE_ATU_RC1_TYPE_CFG1); + dwpcie_atu_config(sc, tag, IATU_REGION_CTRL_1_TYPE_CFG1); bus_space_write_4(sc->sc_iot, sc->sc_cfg_ioh, reg, data); } |