[PATCH v2 02/15] PCI: rcar-gen4: Check live link status in link_up()
From: Koichiro Den
Date: Mon Sep 28 2026 - 13:09:40 EST
rcar_gen4_pcie_link_up() checks link state using SMLH_LINK_UP and
RDLH_LINK_UP in PCIEINTSTS0. However, these bits do not reflect the live
link state. On an R-Car S4, after taking down the endpoint, a link-down
interrupt saw PCIEINTSTS0 = 0x20a000c5 with both bits still set. Even
after resetting the controller with the LTSSM back in Polling, they read
0xa000c5, still set.
As a result, dw_pcie_link_up() keeps reporting the link as up after it
has gone down. That defeats the check in dw_pcie_other_conf_map_bus(),
which is supposed to stop config accesses to downstream devices while
the link is down, so such accesses go out on the dead link and stall the
host. It also makes the callback useless for the link-down recovery
added later, which has to wait for the link to actually come back after
resetting the controller.
Keep the APP link-up event check from Figure 104.5 of the R-Car S4
reference manual, but also require PORT_DEBUG1 to report the link up and
not in training. The callback then rejects a downed link even if the APP
link-up events remain latched.
Clear the APP latches before enabling LTSSM to discard events from a
previous start, and only read them in .link_up(). RC startup uses
dw_pcie_wait_for_link() to poll the combined condition.
Fixes: 0d0c551011df ("PCI: rcar-gen4: Add R-Car Gen4 PCIe controller support for host mode")
Suggested-by: Marek Vasut <marek.vasut+renesas@xxxxxxxxxxx>
Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
---
Changes in v2:
- Keep the APP link-up event check in .link_up() and combine it with
the PORT_DEBUG1 check, reusing the DWC core's polling. (Marek)
- Clear the APP link-up latches before enabling LTSSM and leave them
latched across .link_up() calls. (Marek)
drivers/pci/controller/dwc/pcie-rcar-gen4.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index 21c3dd9b8891..4dabcda051a5 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -53,6 +53,9 @@
#define PCIEDMAINTSTSEN 0x0314
#define PCIEDMAINTSTSEN_INIT GENMASK(15, 0)
+/* PCIe Interrupt Status 0 Clear */
+#define PCIEINTSTS0CLR 0x0340
+
/* Port Logic Registers 89 */
#define PRTLGC89 0x0b70
@@ -120,8 +123,13 @@ static bool rcar_gen4_pcie_link_up(struct dw_pcie *dw)
val = readl(rcar->base + PCIEINTSTS0);
mask = RDLH_LINK_UP | SMLH_LINK_UP;
+ if ((val & mask) != mask)
+ return false;
- return (val & mask) == mask;
+ /* The APP link-up events remain latched after the link goes down. */
+ val = dw_pcie_readl_dbi(dw, PCIE_PORT_DEBUG1);
+ return (val & PCIE_PORT_DEBUG1_LINK_UP) &&
+ !(val & PCIE_PORT_DEBUG1_LINK_IN_TRAINING);
}
/*
@@ -208,6 +216,9 @@ static int rcar_gen4_pcie_start_link(struct dw_pcie *dw)
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
int ret;
+ /* Clear link-up events from the previous start. */
+ writel(RDLH_LINK_UP | SMLH_LINK_UP, rcar->base + PCIEINTSTS0CLR);
+
if (rcar->drvdata->ltssm_control) {
ret = rcar->drvdata->ltssm_control(rcar, true);
if (ret)
--
2.51.0