summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2018-04-05 21:41:50 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2018-04-05 21:41:50 +0000
commitb3eaca2c32ea41870d4c38c13633520da318e8b3 (patch)
treed9c28555287e4511f6721b1a101eca9692f1adc9 /sys/dev
parent7832d512a8d02ba7e4443d3a7104d78467e3fe0c (diff)
Add more initialization code such that things work with the EDK2-based
UEFI firmware as well.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/fdt/dwpcie.c227
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);
}