The L1 substates support requires additional steps to work, see e.g. section '11.6.6.4 L1 Substate' in the RK3588 TRM V1.0.
These steps are currently missing from the driver.
While this has always been a problem when using e.g. CONFIG_PCIEASPM_POWER_SUPERSAVE=y, the problem became more apparent after commit f3ac2ff14834 ("PCI/ASPM: Enable all ClockPM and ASPM states for devicetree platforms"), which enabled ASPM also for CONFIG_PCIEASPM_DEFAULT=y.
Disable L1 substates until proper support is added.
Cc: stable@vger.kernel.org Fixes: 0e898eb8df4e ("PCI: rockchip-dwc: Add Rockchip RK356X host controller driver") Fixes: f3ac2ff14834 ("PCI/ASPM: Enable all ClockPM and ASPM states for devicetree platforms") Signed-off-by: Niklas Cassel cassel@kernel.org --- drivers/pci/controller/dwc/pcie-dw-rockchip.c | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+)
diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 3e2752c7dd09..28e0fffe2542 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -200,6 +200,26 @@ static bool rockchip_pcie_link_up(struct dw_pcie *pci) return FIELD_GET(PCIE_LINKUP_MASK, val) == PCIE_LINKUP; }
+/* + * See e.g. section '11.6.6.4 L1 Substate' in the RK3588 TRM V1.0 for the steps + * needed to support L1 substates. Currently, not a single rockchip platform + * performs these steps, so disable L1 substates until there is proper support. + */ +static void rockchip_pcie_disable_l1sub(struct dw_pcie *pci) +{ + u32 cap, l1subcap; + + cap = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS); + if (cap) { + l1subcap = dw_pcie_readl_dbi(pci, cap + PCI_L1SS_CAP); + l1subcap &= ~(PCI_L1SS_CAP_L1_PM_SS | PCI_L1SS_CAP_ASPM_L1_1 | + PCI_L1SS_CAP_ASPM_L1_2 | PCI_L1SS_CAP_PCIPM_L1_1 | + PCI_L1SS_CAP_PCIPM_L1_2); + dw_pcie_writel_dbi(pci, cap + PCI_L1SS_CAP, l1subcap); + l1subcap = dw_pcie_readl_dbi(pci, cap + PCI_L1SS_CAP); + } +} + static void rockchip_pcie_enable_l0s(struct dw_pcie *pci) { u32 cap, lnkcap; @@ -264,6 +284,7 @@ static int rockchip_pcie_host_init(struct dw_pcie_rp *pp) irq_set_chained_handler_and_data(irq, rockchip_pcie_intx_handler, rockchip);
+ rockchip_pcie_disable_l1sub(pci); rockchip_pcie_enable_l0s(pci);
return 0; @@ -301,6 +322,7 @@ static void rockchip_pcie_ep_init(struct dw_pcie_ep *ep) struct dw_pcie *pci = to_dw_pcie_from_ep(ep); enum pci_barno bar;
+ rockchip_pcie_disable_l1sub(pci); rockchip_pcie_enable_l0s(pci); rockchip_pcie_ep_hide_broken_ats_cap_rk3588(ep);
On Wed, Oct 15, 2025 at 02:31:43PM +0200, Niklas Cassel wrote:
The L1 substates support requires additional steps to work, see e.g. section '11.6.6.4 L1 Substate' in the RK3588 TRM V1.0.
These steps are currently missing from the driver.
While this has always been a problem when using e.g. CONFIG_PCIEASPM_POWER_SUPERSAVE=y, the problem became more apparent after commit f3ac2ff14834 ("PCI/ASPM: Enable all ClockPM and ASPM states for devicetree platforms"), which enabled ASPM also for CONFIG_PCIEASPM_DEFAULT=y.
Disable L1 substates until proper support is added.
Cc: stable@vger.kernel.org Fixes: 0e898eb8df4e ("PCI: rockchip-dwc: Add Rockchip RK356X host controller driver") Fixes: f3ac2ff14834 ("PCI/ASPM: Enable all ClockPM and ASPM states for devicetree platforms") Signed-off-by: Niklas Cassel cassel@kernel.org
drivers/pci/controller/dwc/pcie-dw-rockchip.c | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+)
diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 3e2752c7dd09..28e0fffe2542 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -200,6 +200,26 @@ static bool rockchip_pcie_link_up(struct dw_pcie *pci) return FIELD_GET(PCIE_LINKUP_MASK, val) == PCIE_LINKUP; } +/*
- See e.g. section '11.6.6.4 L1 Substate' in the RK3588 TRM V1.0 for the steps
- needed to support L1 substates. Currently, not a single rockchip platform
- performs these steps, so disable L1 substates until there is proper support.
- */
+static void rockchip_pcie_disable_l1sub(struct dw_pcie *pci) +{
- u32 cap, l1subcap;
- cap = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS);
- if (cap) {
l1subcap = dw_pcie_readl_dbi(pci, cap + PCI_L1SS_CAP);
l1subcap &= ~(PCI_L1SS_CAP_L1_PM_SS | PCI_L1SS_CAP_ASPM_L1_1 |
PCI_L1SS_CAP_ASPM_L1_2 | PCI_L1SS_CAP_PCIPM_L1_1 |
PCI_L1SS_CAP_PCIPM_L1_2);
dw_pcie_writel_dbi(pci, cap + PCI_L1SS_CAP, l1subcap);
l1subcap = dw_pcie_readl_dbi(pci, cap + PCI_L1SS_CAP);
Sorry, this extra dw_pcie_readl_dbi() was left over from debugging. Tell me if I should respin or if you can fix up when applying.
I've verified that the patch works using an NVMe drive on v6.18-rc1, which is working after this patch, but was not working before this patch.
Kind regards, Niklas
On Wed Oct 15, 2025 at 2:31 PM CEST, Niklas Cassel wrote:
The L1 substates support requires additional steps to work, see e.g. section '11.6.6.4 L1 Substate' in the RK3588 TRM V1.0.
I visually compared '18.6.6 PCIe Power Management' of Part 2 V1.1 (20210301) of the RK3568 TRM with '11.6.6 PCIe Power Management' of Part 2 V1.0 (20220309) of the RK3588 TRM. AFAICT they are word for word the same ... until I got to 'Table 18-14 PCIe Interrupt Table' (RK3568) and 'Table 11-22 ...' (RK3588) where there are differences. I don't understand enough of this material so I would appreciate if you could take a look to see if that difference is or could be relevant.
TIA, Diederik
These steps are currently missing from the driver.
While this has always been a problem when using e.g. CONFIG_PCIEASPM_POWER_SUPERSAVE=y, the problem became more apparent after commit f3ac2ff14834 ("PCI/ASPM: Enable all ClockPM and ASPM states for devicetree platforms"), which enabled ASPM also for CONFIG_PCIEASPM_DEFAULT=y.
Disable L1 substates until proper support is added.
Cc: stable@vger.kernel.org Fixes: 0e898eb8df4e ("PCI: rockchip-dwc: Add Rockchip RK356X host controller driver") Fixes: f3ac2ff14834 ("PCI/ASPM: Enable all ClockPM and ASPM states for devicetree platforms") Signed-off-by: Niklas Cassel cassel@kernel.org
drivers/pci/controller/dwc/pcie-dw-rockchip.c | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+)
diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 3e2752c7dd09..28e0fffe2542 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -200,6 +200,26 @@ static bool rockchip_pcie_link_up(struct dw_pcie *pci) return FIELD_GET(PCIE_LINKUP_MASK, val) == PCIE_LINKUP; } +/*
- See e.g. section '11.6.6.4 L1 Substate' in the RK3588 TRM V1.0 for the steps
- needed to support L1 substates. Currently, not a single rockchip platform
- performs these steps, so disable L1 substates until there is proper support.
- */
+static void rockchip_pcie_disable_l1sub(struct dw_pcie *pci) +{
- u32 cap, l1subcap;
- cap = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS);
- if (cap) {
l1subcap = dw_pcie_readl_dbi(pci, cap + PCI_L1SS_CAP);
l1subcap &= ~(PCI_L1SS_CAP_L1_PM_SS | PCI_L1SS_CAP_ASPM_L1_1 |
PCI_L1SS_CAP_ASPM_L1_2 | PCI_L1SS_CAP_PCIPM_L1_1 |
PCI_L1SS_CAP_PCIPM_L1_2);
dw_pcie_writel_dbi(pci, cap + PCI_L1SS_CAP, l1subcap);
l1subcap = dw_pcie_readl_dbi(pci, cap + PCI_L1SS_CAP);
- }
+}
static void rockchip_pcie_enable_l0s(struct dw_pcie *pci) { u32 cap, lnkcap; @@ -264,6 +284,7 @@ static int rockchip_pcie_host_init(struct dw_pcie_rp *pp) irq_set_chained_handler_and_data(irq, rockchip_pcie_intx_handler, rockchip);
- rockchip_pcie_disable_l1sub(pci); rockchip_pcie_enable_l0s(pci);
return 0; @@ -301,6 +322,7 @@ static void rockchip_pcie_ep_init(struct dw_pcie_ep *ep) struct dw_pcie *pci = to_dw_pcie_from_ep(ep); enum pci_barno bar;
- rockchip_pcie_disable_l1sub(pci); rockchip_pcie_enable_l0s(pci); rockchip_pcie_ep_hide_broken_ats_cap_rk3588(ep);
Hello Diederik,
On Wed, Oct 15, 2025 at 03:21:48PM +0200, Diederik de Haas wrote:
On Wed Oct 15, 2025 at 2:31 PM CEST, Niklas Cassel wrote:
The L1 substates support requires additional steps to work, see e.g. section '11.6.6.4 L1 Substate' in the RK3588 TRM V1.0.
I visually compared '18.6.6 PCIe Power Management' of Part 2 V1.1 (20210301) of the RK3568 TRM with '11.6.6 PCIe Power Management' of Part 2 V1.0 (20220309) of the RK3588 TRM. AFAICT they are word for word the same ... until I got to 'Table 18-14 PCIe Interrupt Table' (RK3568) and 'Table 11-22 ...' (RK3588) where there are differences. I don't understand enough of this material so I would appreciate if you could take a look to see if that difference is or could be relevant.
What you should compare is "18.6.6.4 L1 Substate" of RK3658 TRM Part2 V1.1, vs "11.6.6.4 L1 Substate" of RK3588 TRM Part2 V1.0.
But I have just done that, and I can tell you that they are identical.
Shawn also replied here: https://lore.kernel.org/linux-pci/7df0bf91-8ab1-4e76-83fa-841a4059c634@rock-...
that there is indeed a lot of things missing for L1 substates to work, including proper pinmuxing.
Kind regards, Niklas
linux-stable-mirror@lists.linaro.org