Repo: rockchip-linux/kernel (branch develop-6.6)
Labels: bug, PCIe, rk3588
Environment
|
|
| SoC / board |
RK3588 — Radxa ROCK 5B+ |
| Kernel |
6.6.89-rockchip-standard (develop-6.6, SRCREV 1ba51b059f25) |
| Driver |
drivers/pci/controller/dwc/pcie-dw-rockchip.c |
| PCIe devices |
NVMe (fe150000, Gen3 x2), DEEPX DX-M1 accelerator (fe160000, Gen3 x2), RTL8852BE (fe170000), RTL8125 (fe190000) |
| PHY |
pcie30phy bifurcated x2+x2, data-lanes = <1 1 2 2> |
Summary
After a warm reboot (reboot / systemctl reboot), the next boot intermittently hangs: the async rk-pcie kthread stalls forever inside reset_control_deassert() → dw_pcie_read() and the board never reaches userspace. A cold power-cycle always boots cleanly.
The stalled read never faults or times out — it hangs indefinitely, so the RCU stall detector fires but the machine cannot recover. rockchip_panic_notify() then hangs on the same dead read, so pstore never flushes and there is no crash record.
Steps to reproduce
- Boot the board normally (cold power-on) — PCIe enumerates fine.
reboot (warm reboot, no power cycle).
- Repeat. Within a handful of cycles the board fails to come up.
- Cold power-cycle → boots clean again.
Actual behaviour
Boot stops after the PCIe probe begins; the last useful console output is the RCU stall:
rcu: INFO: rcu_sched detected stalls on CPUs/tasks:
rcu: Tasks blocked on level-0 rcu_node (CPUs 0-7):
task:rk-pcie state:R running task
Call trace:
dw_pcie_read
...
rk_pcie_establish_link
reset_control_deassert
rk-pcie has ppid=kthreadd (async probe), and the read never returns.
Expected behaviour
A warm reboot should leave the PCIe controller and PHY in the same state a cold power-on does, so the next boot's first DBI/config read succeeds.
Root cause
The RK3588 DBI/config read stalls forever (it never faults) when the PCIe PHY / PIPE refclk / PLL is left un-reset. A warm reboot does not power-cycle the block, and rk_pcie_shutdown() only disables LTSSM and masks interrupts — it never asserts the controller reset or powers down the PHY:
static void rk_pcie_shutdown(struct platform_device *pdev)
{
struct rk_pcie *rk_pcie = platform_get_drvdata(pdev);
rk_pcie_disable_ltssm(rk_pcie);
rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_INTR_MASK, 0xffffffff);
}
So the next boot calls reset_control_deassert() on a reset that was never asserted, against a block whose PLL is still in a stale state → the first DBI read hangs.
Cold boot works because power-on starts every reset asserted.
This is weaker than develop-6.1
develop-6.1 additionally marks the bus PCI_D3cold in shutdown to block further config access:
/* Prevent any configure space access by claiming we're in D3cold */
rk_pcie_bus_set_current_state(rk_pcie->pci->pp.bridge->bus, PCI_D3cold);
rk_pcie_disable_ltssm(rk_pcie);
rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_INTR_MASK, 0xffffffff);
develop-6.6 removed that guard, making the warm-reboot teardown strictly weaker than 6.1. (The probe/reset paths themselves are otherwise byte-identical between the two branches, so this is a regression-by-removed-mitigation, not a new hardware issue.)
Note that even 6.1's PCI_D3cold line is only a software guard against config access — neither branch actually resets the hardware on the reboot path.
Why rk_pcie_remove() doesn't help
The only code that fully de-initialises the block —
phy_power_off(...);
phy_exit(...);
reset_control_assert(rk_pcie->rsts);
rk_pcie_disable_power(rk_pcie);
— lives in rk_pcie_remove(), which is not invoked on reboot. Only .shutdown runs.
Proposed fix
Call the existing rk_pcie_hardware_io_unconfig() from .shutdown so a warm reboot ends in a cold-boot-equivalent state. It already asserts reset_control_assert(rk_pcie->rsts) and powers off / exits the PHY:
static void rk_pcie_shutdown(struct platform_device *pdev)
{
struct rk_pcie *rk_pcie = platform_get_drvdata(pdev);
rk_pcie_disable_ltssm(rk_pcie);
rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_INTR_MASK, 0xffffffff);
+
+ /*
+ * A warm reboot does not power-cycle PCIe. Without asserting the
+ * controller reset and powering down the PHY here, the next boot
+ * deasserts a reset that was never asserted, onto a stale-PLL block,
+ * and the first DBI read hangs forever (never faults).
+ */
+ rk_pcie_hardware_io_unconfig(rk_pcie);
}
This runs once per rk-pcie instance, so it covers all bridges (fe150000 / fe160000 / fe170000 / fe190000). It only affects the reboot path, so normal boot is unchanged.
Testing
Applied against develop-6.6 @ 1ba51b059f25. With the patch in place a warm reboot returned the board in ~70 s with all PCIe devices enumerated (/dev/nvme0n1 present, DX-M1 present) and zero dw_pcie_read stalls in dmesg.
Possibly related
- Rockchip commit
47fd882 ("reboot doesn't fully reset hardware")
- Turing-RK1 reports of DBI stalls / PLL-lock polling on RK3588
- Independent RK3588 reports of
Failed to initialize host / PCIe Link Fail, LTSSM is 0x3
Questions for maintainers
- Was dropping the
PCI_D3cold guard in develop-6.6 intentional?
- Is
rk_pcie_hardware_io_unconfig() the preferred hook here, or would you rather see the full rk_pcie_remove()-style teardown (including rk_pcie_disable_power()) on the shutdown path?
Repo:
rockchip-linux/kernel(branchdevelop-6.6)Labels: bug, PCIe, rk3588
Environment
6.6.89-rockchip-standard(develop-6.6, SRCREV1ba51b059f25)drivers/pci/controller/dwc/pcie-dw-rockchip.cfe150000, Gen3 x2), DEEPX DX-M1 accelerator (fe160000, Gen3 x2), RTL8852BE (fe170000), RTL8125 (fe190000)pcie30phybifurcated x2+x2,data-lanes = <1 1 2 2>Summary
After a warm reboot (
reboot/systemctl reboot), the next boot intermittently hangs: the asyncrk-pciekthread stalls forever insidereset_control_deassert()→dw_pcie_read()and the board never reaches userspace. A cold power-cycle always boots cleanly.The stalled read never faults or times out — it hangs indefinitely, so the RCU stall detector fires but the machine cannot recover.
rockchip_panic_notify()then hangs on the same dead read, so pstore never flushes and there is no crash record.Steps to reproduce
reboot(warm reboot, no power cycle).Actual behaviour
Boot stops after the PCIe probe begins; the last useful console output is the RCU stall:
rk-pciehasppid=kthreadd(async probe), and the read never returns.Expected behaviour
A warm reboot should leave the PCIe controller and PHY in the same state a cold power-on does, so the next boot's first DBI/config read succeeds.
Root cause
The RK3588 DBI/config read stalls forever (it never faults) when the PCIe PHY / PIPE refclk / PLL is left un-reset. A warm reboot does not power-cycle the block, and
rk_pcie_shutdown()only disables LTSSM and masks interrupts — it never asserts the controller reset or powers down the PHY:So the next boot calls
reset_control_deassert()on a reset that was never asserted, against a block whose PLL is still in a stale state → the first DBI read hangs.Cold boot works because power-on starts every reset asserted.
This is weaker than
develop-6.1develop-6.1additionally marks the busPCI_D3coldin shutdown to block further config access:develop-6.6removed that guard, making the warm-reboot teardown strictly weaker than 6.1. (The probe/reset paths themselves are otherwise byte-identical between the two branches, so this is a regression-by-removed-mitigation, not a new hardware issue.)Note that even 6.1's
PCI_D3coldline is only a software guard against config access — neither branch actually resets the hardware on the reboot path.Why
rk_pcie_remove()doesn't helpThe only code that fully de-initialises the block —
— lives in
rk_pcie_remove(), which is not invoked on reboot. Only.shutdownruns.Proposed fix
Call the existing
rk_pcie_hardware_io_unconfig()from.shutdownso a warm reboot ends in a cold-boot-equivalent state. It already assertsreset_control_assert(rk_pcie->rsts)and powers off / exits the PHY:static void rk_pcie_shutdown(struct platform_device *pdev) { struct rk_pcie *rk_pcie = platform_get_drvdata(pdev); rk_pcie_disable_ltssm(rk_pcie); rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_INTR_MASK, 0xffffffff); + + /* + * A warm reboot does not power-cycle PCIe. Without asserting the + * controller reset and powering down the PHY here, the next boot + * deasserts a reset that was never asserted, onto a stale-PLL block, + * and the first DBI read hangs forever (never faults). + */ + rk_pcie_hardware_io_unconfig(rk_pcie); }This runs once per
rk-pcieinstance, so it covers all bridges (fe150000/fe160000/fe170000/fe190000). It only affects the reboot path, so normal boot is unchanged.Testing
Applied against
develop-6.6@1ba51b059f25. With the patch in place a warmrebootreturned the board in ~70 s with all PCIe devices enumerated (/dev/nvme0n1present, DX-M1 present) and zerodw_pcie_readstalls indmesg.Possibly related
47fd882("reboot doesn't fully reset hardware")Failed to initialize host/PCIe Link Fail, LTSSM is 0x3Questions for maintainers
PCI_D3coldguard indevelop-6.6intentional?rk_pcie_hardware_io_unconfig()the preferred hook here, or would you rather see the fullrk_pcie_remove()-style teardown (includingrk_pcie_disable_power()) on the shutdown path?