From 86f8752502e0116646bda03947d437a18b970e2f Mon Sep 17 00:00:00 2001 From: kettenis Date: Fri, 7 Jan 2022 19:03:57 +0000 Subject: [PATCH] Add code to initialize the PCIe host bridge hardware. We currently rely on U-Boot to initialize the hardware for us, but it is better if we can cope with this ourselves. ok patrick@ --- sys/arch/arm64/dev/aplpcie.c | 213 +++++++++++++++++++++++++++++------ 1 file changed, 177 insertions(+), 36 deletions(-) diff --git a/sys/arch/arm64/dev/aplpcie.c b/sys/arch/arm64/dev/aplpcie.c index 7480307646b..026fcce4f32 100644 --- a/sys/arch/arm64/dev/aplpcie.c +++ b/sys/arch/arm64/dev/aplpcie.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aplpcie.c,v 1.9 2021/12/09 11:38:27 kettenis Exp $ */ +/* $OpenBSD: aplpcie.c,v 1.10 2022/01/07 19:03:57 kettenis Exp $ */ /* * Copyright (c) 2021 Mark Kettenis * @@ -30,32 +30,64 @@ #include #include +#include #include #include #include #include -/* - * This driver is based on preliminary device tree bindings and will - * almost certainly need changes once the official bindings land in - * mainline Linux. Support for these preliminary bindings will be - * dropped as soon as official bindings are available. - * - * The driver assumes that the hardware has been (almost) completely - * initialized by U-Boot. More code will be needed to support - * alternate boot paths. - */ - -#define PCIE_MSI_CTRL 0x0124 -#define PCIE_MSI_CTRL_ENABLE (1 << 0) -#define PCIE_MSI_CTRL_32 (5 << 4) -#define PCIE_MSI_REMAP 0x0128 -#define PCIE_MSI_DOORBELL 0x0168 +#define PCIE_CORE_LANE_CONF(port) (0x84000 + (port) * 0x4000) +#define PCIE_CORE_LANE_CONF_REFCLK0REQ (1 << 0) +#define PCIE_CORE_LANE_CONF_REFCLK1REQ (1 << 1) +#define PCIE_CORE_LANE_CONF_REFCLK0ACK (1 << 2) +#define PCIE_CORE_LANE_CONF_REFCLK1ACK (1 << 3) +#define PCIE_CORE_LANE_CONF_REFCLK0EN (1 << 9) +#define PCIE_CORE_LANE_CONF_REFCLK1EN (1 << 10) +#define PCIE_CORE_LANE_CTRL(port) (0x84004 + (port) * 0x4000) +#define PCIE_CORE_LANE_CTRL_CFGACC (1 << 15) + +#define PCIE_PORT_LTSSM_CTRL 0x0080 +#define PCIE_PORT_LTSSM_CTRL_START (1 << 0) +#define PCIE_PORT_MSI_CTRL 0x0124 +#define PCIE_PORT_MSI_CTRL_ENABLE (1 << 0) +#define PCIE_PORT_MSI_CTRL_32 (5 << 4) +#define PCIE_PORT_MSI_REMAP 0x0128 +#define PCIE_PORT_MSI_DOORBELL 0x0168 +#define PCIE_PORT_LINK_STAT 0x0208 +#define PCIE_PORT_LINK_STAT_UP (1 << 0) +#define PCIE_PORT_APPCLK 0x0800 +#define PCIE_PORT_APPCLK_EN (1 << 0) +#define PCIE_PORT_APPCLK_CGDIS (1 << 8) +#define PCIE_PORT_STAT 0x0804 +#define PCIE_PORT_STAT_READY (1 << 0) +#define PCIE_PORT_REFCLK 0x0810 +#define PCIE_PORT_REFCLK_EN (1 << 0) +#define PCIE_PORT_REFCLK_CGDIS (1 << 8) +#define PCIE_PORT_PERST 0x0814 +#define PCIE_PORT_PERST_DIS (1 << 0) #define HREAD4(sc, reg) \ - (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) + (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) #define HWRITE4(sc, reg, val) \ - bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) + bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) + +#define RREAD4(sc, reg) \ + (bus_space_read_4((sc)->sc_iot, (sc)->sc_rc_ioh, (reg))) +#define RWRITE4(sc, reg, val) \ + bus_space_write_4((sc)->sc_iot, (sc)->sc_rc_ioh, (reg), (val)) +#define RSET4(sc, reg, bits) \ + RWRITE4((sc), (reg), RREAD4((sc), (reg)) | (bits)) +#define RCLR4(sc, reg, bits) \ + RWRITE4((sc), (reg), RREAD4((sc), (reg)) & ~(bits)) + +#define PREAD4(sc, port, reg) \ + (bus_space_read_4((sc)->sc_iot, (sc)->sc_port_ioh[(port)], (reg))) +#define PWRITE4(sc, port, reg, val) \ + bus_space_write_4((sc)->sc_iot, (sc)->sc_port_ioh[(port)], (reg), (val)) +#define PSET4(sc, port, reg, bits) \ + PWRITE4((sc), (port), (reg), PREAD4((sc), (port), (reg)) | (bits)) +#define PCLR4(sc, port, reg, bits) \ + PWRITE4((sc), (port), (reg), PREAD4((sc), (port), (reg)) & ~(bits)) struct aplpcie_range { uint32_t flags; @@ -64,11 +96,15 @@ struct aplpcie_range { uint64_t size; }; +#define APLPCIE_MAX_PORTS 4 + struct aplpcie_softc { struct device sc_dev; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; - bus_space_handle_t sc_port_ioh[3]; + bus_space_handle_t sc_rc_ioh; + bus_space_handle_t sc_port_ioh[APLPCIE_MAX_PORTS]; + bus_size_t sc_port_ios[APLPCIE_MAX_PORTS]; bus_dma_tag_t sc_dmat; int sc_node; @@ -114,6 +150,8 @@ aplpcie_match(struct device *parent, void *match, void *aux) return OF_is_compatible(faa->fa_node, "apple,pcie"); } +void aplpcie_init_port(struct aplpcie_softc *, int); + void aplpcie_attach_hook(struct device *, struct device *, struct pcibus_attach_args *); int aplpcie_bus_maxdevs(void *, int); @@ -149,7 +187,7 @@ aplpcie_attach(struct device *parent, struct device *self, void *aux) int i, j, nranges, rangeslen; uint32_t bus_range[2]; char name[32]; - int idx; + int idx, node, port; sc->sc_iot = faa->fa_iot; @@ -161,15 +199,26 @@ aplpcie_attach(struct device *parent, struct device *self, void *aux) return; } - for (i = 0; i < 3; i++) { - snprintf(name, sizeof(name), "port%d", i); + idx = OF_getindex(faa->fa_node, "rc", "reg-names"); + if (idx < 0 || idx >= faa->fa_nreg || + bus_space_map(sc->sc_iot, faa->fa_reg[idx].addr, + faa->fa_reg[idx].size, 0, &sc->sc_rc_ioh)) { + printf(": can't map registers\n"); + return; + } + + for (port = 0; port < APLPCIE_MAX_PORTS; port++) { + snprintf(name, sizeof(name), "port%d", port); idx = OF_getindex(faa->fa_node, name, "reg-names"); - if (idx < 0 || idx > faa->fa_nreg || + if (idx < 0) + continue; + if (idx > faa->fa_nreg || bus_space_map(sc->sc_iot, faa->fa_reg[idx].addr, - faa->fa_reg[idx].size, 0, &sc->sc_port_ioh[i])) { + faa->fa_reg[idx].size, 0, &sc->sc_port_ioh[port])) { printf(": can't map registers\n"); return; } + sc->sc_port_ios[port] = faa->fa_reg[idx].size; } sc->sc_dmat = faa->fa_dmat; @@ -186,19 +235,15 @@ aplpcie_attach(struct device *parent, struct device *self, void *aux) return; } + for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) + aplpcie_init_port(sc, node); + /* - * Set things up such that we can share the 32 available MSIs - * across all ports. + * Must wait at least 100ms after link training completes + * before sending a configuration request to a device + * immediately below a port. */ - for (i = 0; i < 3; i++) { - bus_space_write_4(sc->sc_iot, sc->sc_port_ioh[i], - PCIE_MSI_CTRL, PCIE_MSI_CTRL_32 | PCIE_MSI_CTRL_ENABLE); - bus_space_write_4(sc->sc_iot, sc->sc_port_ioh[i], - PCIE_MSI_REMAP, 0); - bus_space_write_4(sc->sc_iot, sc->sc_port_ioh[i], - PCIE_MSI_DOORBELL, sc->sc_msi_doorbell); - } - sc->sc_msi = 0; + delay(100000); sc->sc_acells = OF_getpropint(sc->sc_node, "#address-cells", faa->fa_acells); @@ -332,6 +377,102 @@ aplpcie_attach(struct device *parent, struct device *self, void *aux) config_found(self, &pba, NULL); } +void +aplpcie_init_port(struct aplpcie_softc *sc, int node) +{ + uint32_t reg[5]; + uint32_t *reset_gpio; + int reset_gpiolen; + uint32_t stat; + int port, timo; + + if (OF_getpropintarray(node, "reg", reg, sizeof(reg)) != sizeof(reg)) + return; + + port = reg[0] >> 11; + if (port >= APLPCIE_MAX_PORTS || sc->sc_port_ios[port] == 0) + return; + + reset_gpiolen = OF_getproplen(node, "reset-gpios"); + if (reset_gpiolen <= 0) + return; + + /* + * Set things up such that we can share the 32 available MSIs + * across all ports. + */ + PWRITE4(sc, port, PCIE_PORT_MSI_CTRL, + PCIE_PORT_MSI_CTRL_32 | PCIE_PORT_MSI_CTRL_ENABLE); + PWRITE4(sc, port, PCIE_PORT_MSI_REMAP, 0); + PWRITE4(sc, port, PCIE_PORT_MSI_DOORBELL, sc->sc_msi_doorbell); + + /* Check if the link is already up. */ + stat = PREAD4(sc, port, PCIE_PORT_LINK_STAT); + if (stat & PCIE_PORT_LINK_STAT_UP) + return; + + PSET4(sc, port, PCIE_PORT_APPCLK, PCIE_PORT_APPCLK_EN); + + /* Assert PERST#. */ + reset_gpio = malloc(reset_gpiolen, M_TEMP, M_WAITOK); + OF_getpropintarray(node, "reset-gpios", reset_gpio, reset_gpiolen); + gpio_controller_config_pin(reset_gpio, GPIO_CONFIG_OUTPUT); + gpio_controller_set_pin(reset_gpio, 1); + + /* Setup Refclk. */ + RSET4(sc, PCIE_CORE_LANE_CTRL(port), PCIE_CORE_LANE_CTRL_CFGACC); + RSET4(sc, PCIE_CORE_LANE_CONF(port), PCIE_CORE_LANE_CONF_REFCLK0REQ); + for (timo = 500; timo > 0; timo--) { + stat = RREAD4(sc, PCIE_CORE_LANE_CONF(port)); + if (stat & PCIE_CORE_LANE_CONF_REFCLK0ACK) + break; + delay(100); + } + RSET4(sc, PCIE_CORE_LANE_CONF(port), PCIE_CORE_LANE_CONF_REFCLK1REQ); + for (timo = 500; timo > 0; timo--) { + stat = RREAD4(sc, PCIE_CORE_LANE_CONF(port)); + if (stat & PCIE_CORE_LANE_CONF_REFCLK1ACK) + break; + delay(100); + } + RCLR4(sc, PCIE_CORE_LANE_CTRL(port), PCIE_CORE_LANE_CTRL_CFGACC); + RSET4(sc, PCIE_CORE_LANE_CONF(port), + PCIE_CORE_LANE_CONF_REFCLK0EN | PCIE_CORE_LANE_CONF_REFCLK1EN); + PSET4(sc, port, PCIE_PORT_REFCLK, PCIE_PORT_REFCLK_EN); + + /* + * PERST# must remain asserted for at least 100us after the + * reference clock becomes stable. + */ + delay(100); + + /* Deassert PERST#. */ + PSET4(sc, port, PCIE_PORT_PERST, PCIE_PORT_PERST_DIS); + gpio_controller_set_pin(reset_gpio, 0); + free(reset_gpio, M_TEMP, reset_gpiolen); + + for (timo = 2500; timo > 0; timo--) { + stat = PREAD4(sc, port, PCIE_PORT_STAT); + if (stat & PCIE_PORT_STAT_READY) + break; + delay(100); + } + if ((stat & PCIE_PORT_STAT_READY) == 0) + return; + + PCLR4(sc, port, PCIE_PORT_REFCLK, PCIE_PORT_REFCLK_CGDIS); + PCLR4(sc, port, PCIE_PORT_APPCLK, PCIE_PORT_APPCLK_CGDIS); + + /* Bring up the link. */ + PWRITE4(sc, port, PCIE_PORT_LTSSM_CTRL, PCIE_PORT_LTSSM_CTRL_START); + for (timo = 1000; timo > 0; timo--) { + stat = PREAD4(sc, port, PCIE_PORT_LINK_STAT); + if (stat & PCIE_PORT_LINK_STAT_UP) + break; + delay(100); + } +} + void aplpcie_attach_hook(struct device *parent, struct device *self, struct pcibus_attach_args *pba) -- 2.20.1