Skip to content

Commit 1db806e

Browse files
starnightbjorn-helgaas
authored andcommitted
PCI/ASPM: Save parent L1SS config in pci_save_aspm_l1ss_state()
After 1742336 ("PCI/ASPM: Save L1 PM Substates Capability for suspend/resume"), pci_save_aspm_l1ss_state(dev) saves the L1SS state for "dev", and pci_restore_aspm_l1ss_state(dev) restores the state for both "dev" and its parent. The problem is that unless pci_save_state() has been used in some other path and has already saved the parent L1SS state, we will restore junk to the parent, which means the L1 Substates likely won't work correctly. Save the L1SS config for both the device and its parent in pci_save_aspm_l1ss_state(). When restoring, we need both because L1SS must be enabled at the parent (the Downstream Port) before being enabled at the child (the Upstream Port). Link: https://lore.kernel.org/r/[email protected] Fixes: 1742336 ("PCI/ASPM: Save L1 PM Substates Capability for suspend/resume") Closes: https://bugzilla.kernel.org/show_bug.cgi?id=218394 Suggested-by: Ilpo Järvinen <[email protected]> Signed-off-by: Jian-Hong Pan <[email protected]> [bhelgaas: parallel save/restore structure, simplify commit log, patch at https://lore.kernel.org/r/20241212230340.GA3267194@bhelgaas] Signed-off-by: Bjorn Helgaas <[email protected]> Tested-by: Jian-Hong Pan <[email protected]> # Asus B1400CEAE
1 parent 40384c8 commit 1db806e

File tree

1 file changed

+28
-5
lines changed

1 file changed

+28
-5
lines changed

drivers/pci/pcie/aspm.c

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,24 +81,47 @@ void pci_configure_aspm_l1ss(struct pci_dev *pdev)
8181

8282
void pci_save_aspm_l1ss_state(struct pci_dev *pdev)
8383
{
84+
struct pci_dev *parent = pdev->bus->self;
8485
struct pci_cap_saved_state *save_state;
85-
u16 l1ss = pdev->l1ss;
8686
u32 *cap;
8787

88+
/*
89+
* If this is a Downstream Port, we never restore the L1SS state
90+
* directly; we only restore it when we restore the state of the
91+
* Upstream Port below it.
92+
*/
93+
if (pcie_downstream_port(pdev) || !parent)
94+
return;
95+
96+
if (!pdev->l1ss || !parent->l1ss)
97+
return;
98+
8899
/*
89100
* Save L1 substate configuration. The ASPM L0s/L1 configuration
90101
* in PCI_EXP_LNKCTL_ASPMC is saved by pci_save_pcie_state().
91102
*/
92-
if (!l1ss)
103+
save_state = pci_find_saved_ext_cap(pdev, PCI_EXT_CAP_ID_L1SS);
104+
if (!save_state)
93105
return;
94106

95-
save_state = pci_find_saved_ext_cap(pdev, PCI_EXT_CAP_ID_L1SS);
107+
cap = &save_state->cap.data[0];
108+
pci_read_config_dword(pdev, pdev->l1ss + PCI_L1SS_CTL2, cap++);
109+
pci_read_config_dword(pdev, pdev->l1ss + PCI_L1SS_CTL1, cap++);
110+
111+
if (parent->state_saved)
112+
return;
113+
114+
/*
115+
* Save parent's L1 substate configuration so we have it for
116+
* pci_restore_aspm_l1ss_state(pdev) to restore.
117+
*/
118+
save_state = pci_find_saved_ext_cap(parent, PCI_EXT_CAP_ID_L1SS);
96119
if (!save_state)
97120
return;
98121

99122
cap = &save_state->cap.data[0];
100-
pci_read_config_dword(pdev, l1ss + PCI_L1SS_CTL2, cap++);
101-
pci_read_config_dword(pdev, l1ss + PCI_L1SS_CTL1, cap++);
123+
pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CTL2, cap++);
124+
pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, cap++);
102125
}
103126

104127
void pci_restore_aspm_l1ss_state(struct pci_dev *pdev)

0 commit comments

Comments
 (0)