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 <[email protected]>
---
 drivers/pci/pcie_dw_spacemit.c | 72 +++++++++++++++++-----------------
 1 file changed, 35 insertions(+), 37 deletions(-)

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;
 }
 
-- 
2.55.0


Reply via email to