The probe brings the controller up from whatever state it was left in.
After a warm reboot, or when an earlier boot stage has used the port,
the link can still be trained; the driver then prints "PCI Link already
up before configuration!" and skips the link configuration altogether.

Linux's pcie-spacemit-k1 driver and FreeBSD's spacemit_pcie driver both
pulse PCIE_SOFT_RESET in the APMU control logic register for 2 ms
before enabling the application clocks and resets. Do the same, and
drop the "already up" shortcut, which can no longer be taken.

Signed-off-by: Dave Klotz <[email protected]>
---
 drivers/pci/pcie_dw_spacemit.c | 23 ++++++++++++++++++-----
 1 file changed, 18 insertions(+), 5 deletions(-)

diff --git a/drivers/pci/pcie_dw_spacemit.c b/drivers/pci/pcie_dw_spacemit.c
index 04b3161e94..b85de73ad3 100644
--- a/drivers/pci/pcie_dw_spacemit.c
+++ b/drivers/pci/pcie_dw_spacemit.c
@@ -106,11 +106,6 @@ static int pcie_dw_spacemit_pcie_link_up(struct 
pcie_dw_spacemit *pci, u32 cap_s
 {
        u32 reg;
 
-       if (is_link_up(pci)) {
-               printf("PCI Link already up before configuration!\n");
-               return 1;
-       }
-
        /* DW pre link configurations */
        pcie_dw_configure(pci, cap_speed);
 
@@ -133,6 +128,22 @@ static int pcie_dw_spacemit_pcie_link_up(struct 
pcie_dw_spacemit *pci, u32 cap_s
        return 1;
 }
 
+/*
+ * Reset the controller, so that nothing left over from an earlier boot
+ * stage or a warm reboot (a trained link, a stale LTSSM state) survives.
+ */
+static void spacemit_pcie_toggle_soft_reset(struct pcie_dw_spacemit *pci)
+{
+       u32 reg;
+
+       reg = spacemit_pcie_readl(pci, PCIE_CTRL_LOGIC);
+       spacemit_pcie_writel(pci, PCIE_CTRL_LOGIC, reg | PCIE_SOFT_RESET);
+       /* Read back so that the write has landed before the delay */
+       spacemit_pcie_readl(pci, PCIE_CTRL_LOGIC);
+       mdelay(2);
+       spacemit_pcie_writel(pci, PCIE_CTRL_LOGIC, reg & ~PCIE_SOFT_RESET);
+}
+
 static int pcie_set_mode(struct pcie_dw_spacemit *pci,
                         enum dw_pcie_device_mode mode)
 {
@@ -216,6 +227,8 @@ static int pcie_dw_spacemit_probe(struct udevice *dev)
        int ret;
        u32 reg;
 
+       spacemit_pcie_toggle_soft_reset(pci);
+
        /* enable pcie clk and deassert resets */
        clk_enable_bulk(&pci->clks);
        reset_deassert_bulk(&pci->rsts);
-- 
2.55.0


Reply via email to