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 <[email protected]>
@@ -26,8 +27,25 @@
* SUCH DAMAGE.
*/
+/*
+ * Copyright (c) 2021 Mark Kettenis <[email protected]>
+ *
+ * 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;