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;

Reply via email to