PCI: rockchip: dw: Fix link fail in s2r

Delaying link training need the irq to help set dly2_done which couldn't
come true in resume due to the noirq phase. If the training is still going
but the EP issues a hot reset request, the LTSSM will be stuck and the
link never be back even if we reset the EP. The only way is to reset the
whole controller, which is unacceptable.

The issue is very difficult to be reproduced but finally we spot the key
point from fifo status of ltssm. From the designe point of view, the only
way to make ltssm from 0x0(DETECT_QUIET) to 0x5(PRE_DETECT_QUIET) is
core_rst_n be active and dly logic taking over client settings.

[816669.085768][ T2707] rk-pcie fe180000.pcie: fifo_status = 0xf0009
[816669.085775][ T2707] rk-pcie fe180000.pcie: fifo_status = 0xe000a
[816669.085783][ T2707] rk-pcie fe180000.pcie: fifo_status = 0xd000b
[816669.085790][ T2707] rk-pcie fe180000.pcie: fifo_status = 0xc000c
[816669.085797][ T2707] rk-pcie fe180000.pcie: fifo_status = 0xb0011
[816669.085804][ T2707] rk-pcie fe180000.pcie: fifo_status = 0xa000d
[816669.085811][ T2707] rk-pcie fe180000.pcie: fifo_status = 0x9000f
[816669.085818][ T2707] rk-pcie fe180000.pcie: fifo_status = 0x8000e
[816669.085826][ T2707] rk-pcie fe180000.pcie: fifo_status = 0x107000d
[816669.085833][ T2707] rk-pcie fe180000.pcie: fifo_status = 0x106000e
[816669.085840][ T2707] rk-pcie fe180000.pcie: fifo_status = 0x5000d
[816669.085847][ T2707] rk-pcie fe180000.pcie: fifo_status = 0x40005
[816669.085854][ T2707] rk-pcie fe180000.pcie: fifo_status = 0x30000
[816669.085861][ T2707] rk-pcie fe180000.pcie: fifo_status = 0x20005
[816669.085868][ T2707] rk-pcie fe180000.pcie: fifo_status = 0x10000

Given dly2_done is slef-clear bit, so we can't set it in advance but have
to disable dly2_en when linking in resume and enable it later.

Fixes: 679557456b ("PCIe: dw: rockchip: Delaying the link training after hot reset")
Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
Change-Id: I85c24c7d7ea4c5f6718bcbdfbd7bf328d9a7f170
This commit is contained in:
Shawn Lin
2025-05-15 15:01:25 +08:00
committed by Tao Huang
parent 98c569358a
commit cd348ae871

View File

@@ -930,14 +930,21 @@ static const struct dw_pcie_ops dw_pcie_ops = {
.link_up = rk_pcie_link_up,
};
static void rk_pcie_fast_link_setup(struct rk_pcie *rk_pcie)
static void rk_pcie_fast_link_setup(struct rk_pcie *rk_pcie, bool enable_dly2_en)
{
u32 val;
/* LTSSM EN ctrl mode */
val = rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_HOT_RESET_CTRL);
val |= (PCIE_LTSSM_ENABLE_ENHANCE | PCIE_LTSSM_APP_DLY2_EN)
| ((PCIE_LTSSM_APP_DLY2_EN | PCIE_LTSSM_ENABLE_ENHANCE) << 16);
val |= PCIE_LTSSM_ENABLE_ENHANCE | (PCIE_LTSSM_ENABLE_ENHANCE << 16);
if (enable_dly2_en) {
val |= PCIE_LTSSM_APP_DLY2_EN | (PCIE_LTSSM_APP_DLY2_EN << 16);
} else {
val &= ~PCIE_LTSSM_APP_DLY2_EN;
val |= PCIE_LTSSM_APP_DLY2_EN << 16;
}
rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_HOT_RESET_CTRL, val);
}
@@ -1339,7 +1346,7 @@ static int rk_pcie_slot_enable(struct gpio_hotplug_slot *slot)
dev_info(rk_pcie->pci->dev, "%s\n", __func__);
rk_pcie->hp_no_link = true;
rk_pcie_enable_power(rk_pcie);
rk_pcie_fast_link_setup(rk_pcie);
rk_pcie_fast_link_setup(rk_pcie, true);
ret = rk_pcie_establish_link(rk_pcie->pci);
if (ret)
dev_err(rk_pcie->pci->dev, "fail to enable slot\n");
@@ -1494,6 +1501,7 @@ static int rk_pcie_hardware_io_config(struct rk_pcie *rk_pcie)
phy_power_on(rk_pcie->phy);
/* Release resets after PHY is working */
reset_control_deassert(rk_pcie->rsts);
ret = phy_calibrate(rk_pcie->phy);
@@ -1585,7 +1593,14 @@ static int rk_pcie_host_config(struct rk_pcie *rk_pcie)
dw_pcie_writel_dbi(rk_pcie->pci, rk_pcie->linkcap_off, val);
}
rk_pcie_fast_link_setup(rk_pcie);
/*
* S2R is in noirq phase which couldn't ack hot reset or link down event.
* But we need to deal with dly2_en enable case, otherwise the ltssm will
* be stuck waiting for dlye_done. We could set dly2_done in advance,
* however, it's slef-clear. So the only option here is to disable dly2_en
* when resuming.
*/
rk_pcie_fast_link_setup(rk_pcie, !rk_pcie->in_suspend);
rk_pcie_set_power_limit(rk_pcie);
@@ -2034,6 +2049,7 @@ static int __maybe_unused rockchip_dw_pcie_resume(struct device *dev)
dw_pcie_dbi_ro_wr_dis(pci);
rk_pcie->in_suspend = false;
rk_pcie_fast_link_setup(rk_pcie, true);
return 0;