diff mbox series

[1/9] pci: spacemit: power the slot from vpcie3v3-supply

Message ID 4220c9da3d3a427fcee593c333b856263d9d625c.1790975598.git.DaveWK225@protonmail.com
State New
Delegated to: Tim Ouyang
Headers show
Series pci: spacemit: fixes for the K1 PCIe host driver | expand

Commit Message

Dave Klotz Oct. 2, 2026, 9:30 p.m. UTC
The driver takes the slot power switch from a "k1x,pwr_on" GPIO and a
"power-on-status" polarity flag. Those are vendor properties: neither
the spacemit,k1-pcie binding nor any upstream device tree has them. The
binding describes the 3.3 V slot supply as a regulator, vpcie3v3-supply,
on the root port node, and that is ignored. On boards whose M.2 slot
sits behind a GPIO-switched regulator, such as the OrangePi RV2
(GPIO 116), the slot is never powered and the link cannot train.

Look up vpcie3v3-supply on the root port, falling back to the host
bridge node where some board device trees also put it and where Linux's
pcie-spacemit-k1 driver reads it, and enable it in place of the GPIO.
PERST# is still held for 100 ms after the supply is turned on. Drop the
vendor GPIO properties.

Unlike Linux, only warn when the supply is described but cannot be
found: the Banana Pi F3 and MUSE Pi Pro describe always-on fixed
supplies, and spacemit_k1_defconfig does not build the fixed regulator
driver, so failing the probe would break ports that work today.

Signed-off-by: Dave Klotz <DaveWK225@protonmail.com>
---
 drivers/pci/pcie_dw_spacemit.c | 72 +++++++++++++++++-----------------
 1 file changed, 35 insertions(+), 37 deletions(-)
diff mbox series

Patch

diff --git a/drivers/pci/pcie_dw_spacemit.c b/drivers/pci/pcie_dw_spacemit.c
index 09c56d8a4c..7ca5a5ad1a 100644
--- a/drivers/pci/pcie_dw_spacemit.c
+++ b/drivers/pci/pcie_dw_spacemit.c
@@ -8,7 +8,6 @@ 
  */
 #include <asm/global_data.h>
 #include <asm/io.h>
-#include <asm-generic/gpio.h>
 #include <clk.h>
 #include <dm.h>
 #include <dm/device_compat.h>
@@ -21,6 +20,7 @@ 
 #include <log.h>
 #include <pci.h>
 #include <power-domain.h>
+#include <power/regulator.h>
 #include <reset.h>
 
 #include "pcie_dw_common.h"
@@ -39,8 +39,7 @@  struct pcie_dw_spacemit {
 	/* reset, clock resources */
 	struct clk_bulk		clks;
 	struct reset_ctl_bulk	rsts;
-	struct gpio_desc		pwr_on_gpio;
-	int			power_on_status;
+	struct udevice		*vpcie3v3;	/* slot supply, if described */
 };
 
 static inline u32 spacemit_pcie_readl(struct pcie_dw_spacemit *pcie, u32 offset)
@@ -185,30 +184,26 @@  static int pcie_dw_init_id(struct pcie_dw_spacemit *pci)
 	return 0;
 }
 
-static int k1x_power_on(struct pcie_dw_spacemit *pci, int on)
+/*
+ * The binding describes the 3.3 V slot supply on the root port node. Some
+ * board device trees also, or only, put it on the host bridge node, which
+ * is where Linux's driver looks for it.
+ */
+static int spacemit_pcie_get_vpcie3v3(struct udevice *dev, ofnode port,
+				      struct udevice **supplyp)
 {
-	struct pcie_dw *dwp = &pci->dw;
-	int gpio_val = 0;
-
-	if (on) {
-		if (pci->power_on_status)
-			gpio_val = 1;
-		else
-			gpio_val = 0;
-	} else {
-		if (pci->power_on_status)
-			gpio_val = 0;
-		else
-			gpio_val = 1;
-	}
-
-	if (dm_gpio_is_valid(&pci->pwr_on_gpio)) {
-		dev_info(dwp->dev, "PCIe interface power %s, set gpio %d to %d\n",
-			 on ? "on" : "off", pci->pwr_on_gpio.offset, gpio_val);
-		dm_gpio_set_value(&pci->pwr_on_gpio, gpio_val);
+	ofnode supply;
+
+	supply = ofnode_parse_phandle(port, "vpcie3v3-supply", 0);
+	if (!ofnode_valid(supply))
+		supply = ofnode_parse_phandle(dev_ofnode(dev),
+					      "vpcie3v3-supply", 0);
+	if (!ofnode_valid(supply)) {
+		*supplyp = NULL;
+		return 0;
 	}
 
-	return 0;
+	return uclass_get_device_by_ofnode(UCLASS_REGULATOR, supply, supplyp);
 }
 
 static int pcie_dw_spacemit_probe(struct udevice *dev)
@@ -241,6 +236,13 @@  static int pcie_dw_spacemit_probe(struct udevice *dev)
 		return -ENODEV;
 	}
 
+	ret = spacemit_pcie_get_vpcie3v3(dev, port, &pci->vpcie3v3);
+	if (ret) {
+		/* e.g. an always-on fixed supply with no driver built in */
+		dev_warn(dev, "vpcie3v3 supply not available: %d\n", ret);
+		pci->vpcie3v3 = NULL;
+	}
+
 	ret = generic_phy_get_by_index_nodev(port, 0, &phy0);
 	if (!ret) {
 		reg = spacemit_pcie_readl(pci, PCIECTRL_K1X_CONF_DEVICE_CMD);
@@ -271,8 +273,15 @@  static int pcie_dw_spacemit_probe(struct udevice *dev)
 
 	pcie_set_mode(pci, DW_PCIE_RC_TYPE);
 
-	/* power on the interface */
-	k1x_power_on(pci, 1);
+	/* Power the slot; PERST# stays asserted for 100 ms after this */
+	if (pci->vpcie3v3) {
+		ret = regulator_set_enable_if_allowed(pci->vpcie3v3, true);
+		if (ret) {
+			dev_err(dev, "failed to enable vpcie3v3 supply: %d\n",
+				ret);
+			return ret;
+		}
+	}
 
 	spacemit_pcie_host_init(pci);
 	pcie_dw_setup_host(&pci->dw);
@@ -356,17 +365,6 @@  static int pcie_dw_spacemit_of_to_plat(struct udevice *dev)
 		return ret;
 	}
 
-	gpio_request_by_name(dev, "k1x,pwr_on", 0, &pcie->pwr_on_gpio,
-			     GPIOD_IS_OUT);
-	if (!dm_gpio_is_valid(&pcie->pwr_on_gpio))
-		dev_info(dev, "has no power on gpio.\n");
-
-	ret = dev_read_u32(dev, "power-on-status", &pcie->power_on_status);
-	if (ret) {
-		dev_info(dev, "has no power-on-status flag, use default.\n");
-		pcie->power_on_status = 1;
-	}
-
 	return 0;
 }