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
