Skip to content

Commit a00bba4

Browse files
committed
PCI: dwc: Advertise L1 PM Substates only if driver requests it
L1 PM Substates require the CLKREQ# signal and may also require device-specific support. If CLKREQ# is not supported or driver support is lacking, enabling L1.1 or L1.2 may cause errors when accessing devices, e.g., nvme nvme0: controller is down; will reset: CSTS=0xffffffff, PCI_STATUS=0x10 If the kernel is built with CONFIG_PCIEASPM_POWER_SUPERSAVE=y or users enable L1.x via sysfs, users may trip over these errors even if L1 Substates haven't been enabled by firmware or the driver. To prevent such errors, disable advertising the L1 PM Substates unless the driver sets "dw_pcie.l1ss_support" to indicate that it knows CLKREQ# is present and any device-specific configuration has been done. Set "dw_pcie.l1ss_support" in tegra194 (if DT includes the "supports-clkreq' property) and qcom (for cfg_2_7_0, cfg_1_9_0, cfg_1_34_0, and cfg_sc8280xp controllers) so they can continue to use L1 Substates. Based on Niklas's patch: https://patch.msgid.link/20251017163252.598812-2-cassel@kernel.org [bhelgaas: drop hiding for endpoints] Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Link: https://patch.msgid.link/20251118214312.2598220-2-helgaas@kernel.org
1 parent bcc9a4a commit a00bba4

5 files changed

Lines changed: 33 additions & 0 deletions

File tree

drivers/pci/controller/dwc/pcie-designware-host.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,6 +1060,8 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp)
10601060
PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
10611061
dw_pcie_writel_dbi(pci, PCI_COMMAND, val);
10621062

1063+
dw_pcie_hide_unsupported_l1ss(pci);
1064+
10631065
dw_pcie_config_presets(pp);
10641066
/*
10651067
* If the platform provides its own child bus config accesses, it means

drivers/pci/controller/dwc/pcie-designware.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,30 @@ void dw_pcie_edma_remove(struct dw_pcie *pci)
10811081
dw_edma_remove(&pci->edma);
10821082
}
10831083

1084+
void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci)
1085+
{
1086+
u16 l1ss;
1087+
u32 l1ss_cap;
1088+
1089+
if (pci->l1ss_support)
1090+
return;
1091+
1092+
l1ss = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS);
1093+
if (!l1ss)
1094+
return;
1095+
1096+
/*
1097+
* Unless the driver claims "l1ss_support", don't advertise L1 PM
1098+
* Substates because they require CLKREQ# and possibly other
1099+
* device-specific configuration.
1100+
*/
1101+
l1ss_cap = dw_pcie_readl_dbi(pci, l1ss + PCI_L1SS_CAP);
1102+
l1ss_cap &= ~(PCI_L1SS_CAP_PCIPM_L1_1 | PCI_L1SS_CAP_ASPM_L1_1 |
1103+
PCI_L1SS_CAP_PCIPM_L1_2 | PCI_L1SS_CAP_ASPM_L1_2 |
1104+
PCI_L1SS_CAP_L1_PM_SS);
1105+
dw_pcie_writel_dbi(pci, l1ss + PCI_L1SS_CAP, l1ss_cap);
1106+
}
1107+
10841108
void dw_pcie_setup(struct dw_pcie *pci)
10851109
{
10861110
u32 val;

drivers/pci/controller/dwc/pcie-designware.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,7 @@ struct dw_pcie {
516516
int max_link_speed;
517517
u8 n_fts[2];
518518
struct dw_edma_chip edma;
519+
bool l1ss_support; /* L1 PM Substates support */
519520
struct clk_bulk_data app_clks[DW_PCIE_NUM_APP_CLKS];
520521
struct clk_bulk_data core_clks[DW_PCIE_NUM_CORE_CLKS];
521522
struct reset_control_bulk_data app_rsts[DW_PCIE_NUM_APP_RSTS];
@@ -573,6 +574,7 @@ int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
573574
int type, u64 parent_bus_addr,
574575
u8 bar, size_t size);
575576
void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index);
577+
void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci);
576578
void dw_pcie_setup(struct dw_pcie *pci);
577579
void dw_pcie_iatu_detect(struct dw_pcie *pci);
578580
int dw_pcie_edma_detect(struct dw_pcie *pci);

drivers/pci/controller/dwc/pcie-qcom.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,8 @@ static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie)
10671067
val &= ~REQ_NOT_ENTR_L1;
10681068
writel(val, pcie->parf + PARF_PM_CTRL);
10691069

1070+
pci->l1ss_support = true;
1071+
10701072
val = readl(pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT_V2);
10711073
val |= EN;
10721074
writel(val, pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT_V2);

drivers/pci/controller/dwc/pcie-tegra194.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,9 @@ static void init_host_aspm(struct tegra_pcie_dw *pcie)
703703
val |= (pcie->aspm_pwr_on_t << 19);
704704
dw_pcie_writel_dbi(pci, pcie->cfg_link_cap_l1sub, val);
705705

706+
if (pcie->supports_clkreq)
707+
pci->l1ss_support = true;
708+
706709
/* Program L0s and L1 entrance latencies */
707710
val = dw_pcie_readl_dbi(pci, PCIE_PORT_AFR);
708711
val &= ~PORT_AFR_L0S_ENTRANCE_LAT_MASK;

0 commit comments

Comments
 (0)