Module Name: src Committed By: skrll Date: Wed Apr 27 08:03:06 UTC 2022
Modified Files: src/sys/arch/arm/apple: apple_pcie.c Log Message: Sync with OpenBSD - port initialisation and device power up. Latter requires the SMC GPIO controller which is WIP. While here - improve the MSI vector search algorithm; and - spinkle some BITS(3) To generate a diff of this commit: cvs rdiff -u -r1.5 -r1.6 src/sys/arch/arm/apple/apple_pcie.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/apple/apple_pcie.c diff -u src/sys/arch/arm/apple/apple_pcie.c:1.5 src/sys/arch/arm/apple/apple_pcie.c:1.6 --- src/sys/arch/arm/apple/apple_pcie.c:1.5 Tue Sep 14 01:33:19 2021 +++ src/sys/arch/arm/apple/apple_pcie.c Wed Apr 27 08:03:06 2022 @@ -1,4 +1,5 @@ -/* $NetBSD: apple_pcie.c,v 1.5 2021/09/14 01:33:19 jmcneill Exp $ */ +/* $NetBSD: apple_pcie.c,v 1.6 2022/04/27 08:03:06 skrll Exp $ */ +/* $OpenBSD: aplpcie.c,v 1.13 2022/04/06 18:59:26 naddy Exp $ */ /*- * Copyright (c) 2021 Jared McNeill <jmcne...@invisible.ca> @@ -26,8 +27,25 @@ * SUCH DAMAGE. */ +/* + * Copyright (c) 2021 Mark Kettenis <kette...@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: apple_pcie.c,v 1.5 2021/09/14 01:33:19 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: apple_pcie.c,v 1.6 2022/04/27 08:03:06 skrll Exp $"); #include <sys/param.h> #include <sys/device.h> @@ -46,17 +64,44 @@ __KERNEL_RCSID(0, "$NetBSD: apple_pcie.c #include <arm/pci/pci_msi_machdep.h> #include <arm/fdt/pcihost_fdtvar.h> -#define PCIE_MSI_CTRL 0x0124 -#define PCIE_MSI_CTRL_EN (1U << 0) -#define PCIE_MSI_CTRL_32 (5U << 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 __BIT(0) +#define PCIE_CORE_LANE_CONF_REFCLK1REQ __BIT(1) +#define PCIE_CORE_LANE_CONF_REFCLK0ACK __BIT(2) +#define PCIE_CORE_LANE_CONF_REFCLK1ACK __BIT(3) +#define PCIE_CORE_LANE_CONF_REFCLK0EN __BIT(9) +#define PCIE_CORE_LANE_CONF_REFCLK1EN __BIT(10) +#define PCIE_CORE_LANE_CTRL(port) (0x84004 + (port) * 0x4000) +#define PCIE_CORE_LANE_CTRL_CFGACC __BIT(15) + +#define PCIE_PORT_LTSSM_CTRL 0x0080 +#define PCIE_PORT_LTSSM_CTRL_START __BIT(0) +#define PCIE_PORT_MSI_CTRL 0x0124 +#define PCIE_PORT_MSI_CTRL_EN __BIT(0) +#define PCIE_PORT_MSI_CTRL_32 __SHIFTIN(5U, __BITS(7,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 __BIT(0) +#define PCIE_PORT_APPCLK 0x0800 +#define PCIE_PORT_APPCLK_EN __BIT(0) +#define PCIE_PORT_APPCLK_CGDIS __BIT(8) +#define PCIE_PORT_STAT 0x0804 +#define PCIE_PORT_STAT_READY __BIT(0) +#define PCIE_PORT_REFCLK 0x0810 +#define PCIE_PORT_REFCLK_EN __BIT(0) +#define PCIE_PORT_REFCLK_CGDIS __BIT(8) +#define PCIE_PORT_PERST 0x0814 +#define PCIE_PORT_PERST_DIS __BIT(0) extern struct bus_space arm_generic_bs_tag; struct apple_pcie_softc { struct pcihost_softc sc_pcihost; + bus_space_tag_t sc_rc_bst; + bus_space_handle_t sc_rc_bsh; + int sc_phandle; struct arm_pci_msi sc_msi; u_int sc_msi_start; @@ -81,6 +126,176 @@ static const struct device_compatible_en DEVICE_COMPAT_EOL }; +#define RREAD4(sc, reg) \ + (bus_space_read_4((sc)->sc_rc_bst, (sc)->sc_rc_bsh, (reg))) +#define RWRITE4(sc, reg, val) \ + bus_space_write_4((sc)->sc_rc_bst, (sc)->sc_rc_bsh, (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)) + + +static void +apple_pcie_setup_port(struct apple_pcie_softc *sc, int phandle) +{ + const bus_space_tag_t bst = sc->sc_pcihost.sc_bst; + const device_t dev = sc->sc_pcihost.sc_dev; + const int parent = sc->sc_pcihost.sc_phandle; + char regname[sizeof("portX")]; + bus_space_handle_t bsh; + bus_addr_t addr; + bus_size_t size; + int error; + int timo; + int len; + + const u_int *reg = fdtbus_get_prop(phandle, "reg", &len); + if (len != 5 * sizeof(uint32_t)) { + aprint_error(": couldn't get port number\n"); + } + + u_int portno = __SHIFTOUT(be32toh(reg[0]), __BITS(13,11)); + snprintf(regname, sizeof(regname), "port%u", portno); + + if (fdtbus_get_reg_byname(parent, regname, &addr, &size) != 0) { + aprint_error(": couldn't get %s regs\n", regname); + return; + } + error = bus_space_map(bst, addr, size, 0, &bsh); + if (error != 0) { + aprint_error(": couldn't map %s regs\n", regname); + return; + } + +#define PREAD4(bst, bsh, reg) \ + bus_space_read_4((bst), (bsh), (reg)) +#define PWRITE4(bst, bsh, reg, val) \ + bus_space_write_4((bst), (bsh), (reg), (val)) +#define PSET4(bst, bsh, reg, bits) \ + PWRITE4((bst), (bsh), (reg), PREAD4((bst), (bsh), (reg)) | (bits)) +#define PCLR4(bst, bsh, reg, bits) \ + PWRITE4((bst), (bsh), (reg), PREAD4((bst), (bsh), (reg)) & ~(bits)) + + /* Doorbell address must be below 4GB */ + KASSERT((sc->sc_msi_addr & ~0xffffffffUL) == 0); + + int pwren_gpiolen, reset_gpiolen; + + pwren_gpiolen = OF_getproplen(phandle, "pwren-gpios"); + reset_gpiolen = OF_getproplen(phandle, "reset-gpios"); + if (reset_gpiolen <= 0) + return; + + /* + * Set things up such that we can share the 32 available MSIs + * across all ports. + */ + PWRITE4(bst, bsh, PCIE_PORT_MSI_CTRL, + PCIE_PORT_MSI_CTRL_32 | PCIE_PORT_MSI_CTRL_EN); + PWRITE4(bst, bsh, PCIE_PORT_MSI_REMAP, 0); + PWRITE4(bst, bsh, PCIE_PORT_MSI_DOORBELL, + __SHIFTOUT(sc->sc_msi_addr, __BITS(31, 0))); + + /* Check if the link is already up. */ + uint32_t stat = PREAD4(bst, bsh, PCIE_PORT_LINK_STAT); + if (stat & PCIE_PORT_LINK_STAT_UP) { + aprint_debug_dev(dev, "link already up\n"); + return; + } + aprint_debug_dev(dev, "bringing link up\n"); + + PSET4(bst, bsh, PCIE_PORT_APPCLK, PCIE_PORT_APPCLK_EN); + + struct fdtbus_gpio_pin *gpio_reset = fdtbus_gpio_acquire(phandle, + "reset-gpios", GPIO_PIN_OUTPUT); + + if (gpio_reset == NULL) { + aprint_debug_dev(dev, "failed to get reset-gpios\n"); + return; + } + + fdtbus_gpio_write(gpio_reset, 1); + + /* Power up the device if necessary. */ + if (pwren_gpiolen > 0) { + struct fdtbus_gpio_pin *gpio_pwren = fdtbus_gpio_acquire(phandle, + "pwren-gpios", GPIO_PIN_OUTPUT); + + if (gpio_pwren == NULL) { + aprint_debug_dev(dev, "failed to get pwren-gpios\n"); + return; + } + + fdtbus_gpio_write(gpio_pwren, 1); + } + + /* Setup Refclk. */ + RSET4(sc, PCIE_CORE_LANE_CTRL(portno), PCIE_CORE_LANE_CTRL_CFGACC); + RSET4(sc, PCIE_CORE_LANE_CONF(portno), PCIE_CORE_LANE_CONF_REFCLK0REQ); + for (timo = 500; timo > 0; timo--) { + stat = RREAD4(sc, PCIE_CORE_LANE_CONF(portno)); + if (stat & PCIE_CORE_LANE_CONF_REFCLK0ACK) + break; + delay(100); + } + RSET4(sc, PCIE_CORE_LANE_CONF(portno), PCIE_CORE_LANE_CONF_REFCLK1REQ); + for (timo = 500; timo > 0; timo--) { + stat = RREAD4(sc, PCIE_CORE_LANE_CONF(portno)); + if (stat & PCIE_CORE_LANE_CONF_REFCLK1ACK) + break; + delay(100); + } + RCLR4(sc, PCIE_CORE_LANE_CTRL(portno), PCIE_CORE_LANE_CTRL_CFGACC); + RSET4(sc, PCIE_CORE_LANE_CONF(portno), + PCIE_CORE_LANE_CONF_REFCLK0EN | PCIE_CORE_LANE_CONF_REFCLK1EN); + PSET4(bst, bsh, PCIE_PORT_REFCLK, PCIE_PORT_REFCLK_EN); + + /* + * PERST# must remain asserted for at least 100us after the + * reference clock becomes stable. But also has to remain + * active at least 100ms after power up. + */ + if (pwren_gpiolen > 0) + delay(100000); + else + delay(100); + + /* Deassert PERST#. */ + PSET4(bst, bsh, PCIE_PORT_PERST, PCIE_PORT_PERST_DIS); + fdtbus_gpio_write(gpio_reset, 0); + + for (timo = 2500; timo > 0; timo--) { + stat = PREAD4(bst, bsh, PCIE_PORT_STAT); + if (stat & PCIE_PORT_STAT_READY) + break; + delay(100); + } + if ((stat & PCIE_PORT_STAT_READY) == 0) { + aprint_debug_dev(dev, "link up\n"); + return; + } + + PCLR4(bst, bsh, PCIE_PORT_REFCLK, PCIE_PORT_REFCLK_CGDIS); + PCLR4(bst, bsh, PCIE_PORT_APPCLK, PCIE_PORT_APPCLK_CGDIS); + + /* Bring up the link. */ + PWRITE4(bst, bsh, PCIE_PORT_LTSSM_CTRL, PCIE_PORT_LTSSM_CTRL_START); + for (timo = 1000; timo > 0; timo--) { + stat = PREAD4(bst, bsh, PCIE_PORT_LINK_STAT); + if (stat & PCIE_PORT_LINK_STAT_UP) + break; + delay(100); + } + +#undef PREAD4 +#undef PWRITE4 +#undef PCLR4 +#undef PSET4 + + bus_space_unmap(bst, bsh, size); +} + static int apple_pcie_match(device_t parent, cfdata_t cf, void *aux) { @@ -96,18 +311,23 @@ apple_pcie_attach(device_t parent, devic struct pcihost_softc * const sc = &asc->sc_pcihost; struct fdt_attach_args * const faa = aux; const int phandle = faa->faa_phandle; - bus_addr_t cs_addr; - bus_size_t cs_size; + bus_addr_t cs_addr, rc_addr; + bus_size_t cs_size, rc_size; int error; - if (fdtbus_get_reg(phandle, 0, &cs_addr, &cs_size) != 0) { - aprint_error(": couldn't get registers\n"); + if (fdtbus_get_reg_byname(phandle, "config", &cs_addr, &cs_size) != 0) { + aprint_error(": couldn't get registers (%s)\n", "config"); + return; + } + + if (fdtbus_get_reg_byname(phandle, "rc", &rc_addr, &rc_size) != 0) { + aprint_error(": couldn't get registers (%s)\n", "rc"); return; } sc->sc_dev = self; sc->sc_dmat = faa->faa_dmat; - sc->sc_bst = faa->faa_bst; + sc->sc_bst = asc->sc_rc_bst = faa->faa_bst; /* * Create a new bus tag for PCIe devices that does not inherit the * nonposted MMIO flag from the host controller. @@ -116,7 +336,15 @@ apple_pcie_attach(device_t parent, devic sc->sc_phandle = phandle; error = bus_space_map(faa->faa_bst, cs_addr, cs_size, 0, &sc->sc_bsh); if (error) { - aprint_error(": couldn't map registers: %d\n", error); + aprint_error(": couldn't map registers (%s): %d\n", "config", + error); + return; + } + error = bus_space_map(asc->sc_rc_bst, rc_addr, rc_size, 0, + &asc->sc_rc_bsh); + if (error) { + aprint_error(": couldn't map registers (%s): %d\n", "rc", + error); return; } sc->sc_type = PCIHOST_ECAM; @@ -131,44 +359,23 @@ apple_pcie_attach(device_t parent, devic aprint_naive("\n"); aprint_normal(": Apple PCIe host controller\n"); + for (int node = OF_child(phandle); node; node = OF_peer(node)) + apple_pcie_setup_port(asc, node); + + /* + * Must wait at least 100ms after link training completes + * before sending a configuration request to a device + * immediately below a port. + */ + delay(100000); + pcihost_init(&sc->sc_pc, sc); + sc->sc_pc.pc_attach_hook = apple_pcie_attach_hook; pcihost_init2(sc); } -static void -apple_pcie_setup_port(struct apple_pcie_softc *sc, u_int portno) -{ - const int phandle = sc->sc_pcihost.sc_phandle; - bus_space_tag_t bst = sc->sc_pcihost.sc_bst; - char regname[sizeof("portX")]; - bus_space_handle_t bsh; - bus_addr_t addr; - bus_size_t size; - int error; - snprintf(regname, sizeof(regname), "port%u", portno); - if (fdtbus_get_reg_byname(phandle, regname, &addr, &size) != 0) { - aprint_error(": couldn't get %s regs\n", regname); - return; - } - error = bus_space_map(bst, addr, size, 0, &bsh); - if (error != 0) { - aprint_error(": couldn't map %s regs\n", regname); - return; - } - - /* Doorbell address must be below 4GB */ - KASSERT((sc->sc_msi_addr & ~0xffffffffUL) == 0); - - bus_space_write_4(bst, bsh, PCIE_MSI_CTRL, - PCIE_MSI_CTRL_32 | PCIE_MSI_CTRL_EN); - bus_space_write_4(bst, bsh, PCIE_MSI_REMAP, 0); - bus_space_write_4(bst, bsh, PCIE_MSI_DOORBELL, - (uint32_t)sc->sc_msi_addr); - - bus_space_unmap(bst, bsh, size); -} static void apple_pcie_attach_hook(device_t parent, device_t self, @@ -184,6 +391,7 @@ apple_pcie_attach_hook(device_t parent, const uint32_t rid = pba->pba_bus << 8; dmat = fdtbus_iommu_map_pci(phandle, rid, sc->sc_pcihost.sc_dmat); + pba->pba_dmat = pba->pba_dmat64 = dmat; } @@ -194,23 +402,33 @@ apple_pcie_msi_alloc_msi(struct apple_pc struct pci_attach_args *new_pa; int msi, n; - for (msi = 0; msi < sc->sc_nmsi; msi += count) { - if (sc->sc_msi_pa[msi] == NULL) { - for (n = 1; n < count; n++) { - if (msi + n < sc->sc_nmsi && - sc->sc_msi_pa[msi + n] != NULL) { - continue; - } - } + for (msi = 0; msi < sc->sc_nmsi; msi += n) { + /* Look for first empty slot */ + if (sc->sc_msi_pa[msi] != NULL) { + /* skip the used entry */ + n = 1; + continue; + } - for (n = 0; n < count; n++) { - new_pa = kmem_alloc(sizeof(*new_pa), KM_SLEEP); - memcpy(new_pa, pa, sizeof(*new_pa)); - sc->sc_msi_pa[msi + n] = new_pa; + /* Now check that 'count' entries are also empty */ + for (n = 1; n < count && msi + n < sc->sc_nmsi; n++) { + if (sc->sc_msi_pa[msi + n] != NULL) { + break; } - - return msi; } + /* + * If 'count' empty entries weren't found then the search + * continues. + */ + if (n != count) + continue; + for (n = 0; n < count; n++) { + new_pa = kmem_alloc(sizeof(*new_pa), KM_SLEEP); + memcpy(new_pa, pa, sizeof(*new_pa)); + sc->sc_msi_pa[msi + n] = new_pa; + } + + return msi; } return -1; @@ -270,13 +488,13 @@ apple_pcie_msi_msi_enable(struct apple_p ctl = pci_conf_read(pc, tag, off + PCI_MSI_CTL); if (ctl & PCI_MSI_CTL_64BIT_ADDR) { pci_conf_write(pc, tag, off + PCI_MSI_MADDR64_LO, - addr & 0xffffffff); + __SHIFTOUT(addr, __BITS(31, 0))); pci_conf_write(pc, tag, off + PCI_MSI_MADDR64_HI, - (addr >> 32) & 0xffffffff); + __SHIFTOUT(addr, __BITS(63, 32))); pci_conf_write(pc, tag, off + PCI_MSI_MDATA64, data); } else { pci_conf_write(pc, tag, off + PCI_MSI_MADDR, - addr & 0xffffffff); + __SHIFTOUT(addr, __BITS(31, 0))); pci_conf_write(pc, tag, off + PCI_MSI_MDATA, data); } ctl |= PCI_MSI_CTL_MSI_ENABLE; @@ -322,9 +540,9 @@ apple_pcie_msi_msix_enable(struct apple_ const uint32_t data = msi; const uint64_t entry_base = PCI_MSIX_TABLE_ENTRY_SIZE * msix_vec; bus_space_write_4(bst, bsh, entry_base + PCI_MSIX_TABLE_ENTRY_ADDR_LO, - (uint32_t)addr); + __SHIFTOUT(addr, __BITS(31, 0))); bus_space_write_4(bst, bsh, entry_base + PCI_MSIX_TABLE_ENTRY_ADDR_HI, - (uint32_t)(addr >> 32)); + __SHIFTOUT(addr, __BITS(63, 32))); bus_space_write_4(bst, bsh, entry_base + PCI_MSIX_TABLE_ENTRY_DATA, data); val = bus_space_read_4(bst, bsh, @@ -367,12 +585,14 @@ apple_pcie_msi_msi_alloc(struct arm_pci_ return NULL; const int avail = apple_pcie_msi_available_msi(sc); + if (avail == 0) + return NULL; + if (exact && *count > avail) return NULL; while (*count > avail) { - if (avail < *count) - (*count) >>= 1; + (*count) >>= 1; } if (*count == 0) return NULL; @@ -416,8 +636,7 @@ apple_pcie_msi_msix_alloc(struct arm_pci return NULL; while (*count > avail) { - if (avail < *count) - (*count) >>= 1; + (*count) >>= 1; } if (*count == 0) return NULL; @@ -500,7 +719,6 @@ apple_pcie_msi_init(struct apple_pcie_so { struct arm_pci_msi *msi = &sc->sc_msi; const int phandle = sc->sc_pcihost.sc_phandle; - u_int portno; int len; const u_int *data = fdtbus_get_prop(phandle, "msi-ranges", &len); @@ -529,10 +747,6 @@ apple_pcie_msi_init(struct apple_pcie_so sc->sc_msi_addr = 0xffff000ULL; } - for (portno = 0; portno < 3; portno++) { - apple_pcie_setup_port(sc, portno); - } - msi->msi_dev = sc->sc_pcihost.sc_dev; msi->msi_priv = sc; msi->msi_alloc = apple_pcie_msi_msi_alloc;