Skip to content

Commit 1742336

Browse files
debox1bjorn-helgaas
authored andcommitted
PCI/ASPM: Save L1 PM Substates Capability for suspend/resume
4ff116d ("PCI/ASPM: Save L1 PM Substates Capability for suspend/resume") restored the L1 PM Substates Capability after resume, which reduced power consumption by making the ASPM L1.x states work after resume. a7152be ("Revert "PCI/ASPM: Save L1 PM Substates Capability for suspend/resume"") reverted 4ff116d because resume failed on some systems, so power consumption after resume increased again. a7152be mentioned that we restore L1 PM substate configuration even though ASPM L1 may already be enabled. This is due the fact that the pci_restore_aspm_l1ss_state() was called before pci_restore_pcie_state(). Save and restore the L1 PM Substates Capability, following PCIe r6.1, sec 5.5.4 more closely by: 1) Do not restore ASPM configuration in pci_restore_pcie_state() but do that after PCIe capability is restored in pci_restore_aspm_state() following PCIe r6.1, sec 5.5.4. 2) If BIOS reenables L1SS, particularly L1.2, we need to clear the enables in the right order, downstream before upstream. Defer restoring the L1SS config until we are at the downstream component. Then update the config for both ends of the link in the prescribed order. 3) Program ASPM L1 PM substate configuration before L1 enables. 4) Program ASPM L1 PM substate enables last, after rest of the fields in the capability are programmed. [bhelgaas: commit log, squash L1SS-related patches, do both LNKCTL restores in pci_restore_pcie_state()] Link: https://lore.kernel.org/r/[email protected] Link: https://lore.kernel.org/r/[email protected] Link: https://lore.kernel.org/r/[email protected] Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217321 Link: https://bugzilla.kernel.org/show_bug.cgi?id=216782 Link: https://bugzilla.kernel.org/show_bug.cgi?id=216877 Co-developed-by: Mika Westerberg <[email protected]> Co-developed-by: David E. Box <[email protected]> Reported-by: Koba Ko <[email protected]> Signed-off-by: Mika Westerberg <[email protected]> Signed-off-by: David E. Box <[email protected]> Signed-off-by: Bjorn Helgaas <[email protected]> Tested-by: Tasev Nikola <[email protected]> # Asus UX305FA Cc: Mark Enriquez <[email protected]> Cc: Thomas Witt <[email protected]> Cc: Werner Sembach <[email protected]> Cc: Vidya Sagar <[email protected]>
1 parent 1e11b54 commit 1742336

File tree

5 files changed

+119
-6
lines changed

5 files changed

+119
-6
lines changed

drivers/pci/pci.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1623,14 +1623,16 @@ static int pci_save_pcie_state(struct pci_dev *dev)
16231623
pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &cap[i++]);
16241624
pcie_capability_read_word(dev, PCI_EXP_SLTCTL2, &cap[i++]);
16251625

1626+
pci_save_aspm_l1ss_state(dev);
1627+
16261628
return 0;
16271629
}
16281630

16291631
static void pci_restore_pcie_state(struct pci_dev *dev)
16301632
{
16311633
int i = 0;
16321634
struct pci_cap_saved_state *save_state;
1633-
u16 *cap;
1635+
u16 *cap, lnkctl;
16341636

16351637
save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP);
16361638
if (!save_state)
@@ -1645,12 +1647,23 @@ static void pci_restore_pcie_state(struct pci_dev *dev)
16451647

16461648
cap = (u16 *)&save_state->cap.data[0];
16471649
pcie_capability_write_word(dev, PCI_EXP_DEVCTL, cap[i++]);
1648-
pcie_capability_write_word(dev, PCI_EXP_LNKCTL, cap[i++]);
1650+
1651+
/* Restore LNKCTL register with ASPM control field clear */
1652+
lnkctl = cap[i++];
1653+
pcie_capability_write_word(dev, PCI_EXP_LNKCTL,
1654+
lnkctl & ~PCI_EXP_LNKCTL_ASPMC);
1655+
16491656
pcie_capability_write_word(dev, PCI_EXP_SLTCTL, cap[i++]);
16501657
pcie_capability_write_word(dev, PCI_EXP_RTCTL, cap[i++]);
16511658
pcie_capability_write_word(dev, PCI_EXP_DEVCTL2, cap[i++]);
16521659
pcie_capability_write_word(dev, PCI_EXP_LNKCTL2, cap[i++]);
16531660
pcie_capability_write_word(dev, PCI_EXP_SLTCTL2, cap[i++]);
1661+
1662+
pci_restore_aspm_l1ss_state(dev);
1663+
1664+
/* Restore ASPM control after restoring L1SS state */
1665+
pcie_capability_set_word(dev, PCI_EXP_LNKCTL,
1666+
lnkctl & PCI_EXP_LNKCTL_ASPMC);
16541667
}
16551668

16561669
static int pci_save_pcix_state(struct pci_dev *dev)

drivers/pci/pci.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,9 @@ int pcie_retrain_link(struct pci_dev *pdev, bool use_lt);
571571
/* ASPM-related functionality we need even without CONFIG_PCIEASPM */
572572
void pci_save_ltr_state(struct pci_dev *dev);
573573
void pci_restore_ltr_state(struct pci_dev *dev);
574+
void pci_configure_aspm_l1ss(struct pci_dev *dev);
575+
void pci_save_aspm_l1ss_state(struct pci_dev *dev);
576+
void pci_restore_aspm_l1ss_state(struct pci_dev *dev);
574577

575578
#ifdef CONFIG_PCIEASPM
576579
void pcie_aspm_init_link_state(struct pci_dev *pdev);

drivers/pci/pcie/aspm.c

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,105 @@ void pci_restore_ltr_state(struct pci_dev *dev)
6464
pci_write_config_dword(dev, ltr + PCI_LTR_MAX_SNOOP_LAT, *cap);
6565
}
6666

67+
void pci_configure_aspm_l1ss(struct pci_dev *pdev)
68+
{
69+
int rc;
70+
71+
pdev->l1ss = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS);
72+
73+
rc = pci_add_ext_cap_save_buffer(pdev, PCI_EXT_CAP_ID_L1SS,
74+
2 * sizeof(u32));
75+
if (rc)
76+
pci_err(pdev, "unable to allocate ASPM L1SS save buffer (%pe)\n",
77+
ERR_PTR(rc));
78+
}
79+
80+
void pci_save_aspm_l1ss_state(struct pci_dev *pdev)
81+
{
82+
struct pci_cap_saved_state *save_state;
83+
u16 l1ss = pdev->l1ss;
84+
u32 *cap;
85+
86+
/*
87+
* Save L1 substate configuration. The ASPM L0s/L1 configuration
88+
* in PCI_EXP_LNKCTL_ASPMC is saved by pci_save_pcie_state().
89+
*/
90+
if (!l1ss)
91+
return;
92+
93+
save_state = pci_find_saved_ext_cap(pdev, PCI_EXT_CAP_ID_L1SS);
94+
if (!save_state)
95+
return;
96+
97+
cap = &save_state->cap.data[0];
98+
pci_read_config_dword(pdev, l1ss + PCI_L1SS_CTL2, cap++);
99+
pci_read_config_dword(pdev, l1ss + PCI_L1SS_CTL1, cap++);
100+
}
101+
102+
void pci_restore_aspm_l1ss_state(struct pci_dev *pdev)
103+
{
104+
struct pci_cap_saved_state *pl_save_state, *cl_save_state;
105+
struct pci_dev *parent = pdev->bus->self;
106+
u32 *cap, pl_ctl1, pl_ctl2, pl_l1_2_enable;
107+
u32 cl_ctl1, cl_ctl2, cl_l1_2_enable;
108+
109+
/*
110+
* In case BIOS enabled L1.2 when resuming, we need to disable it first
111+
* on the downstream component before the upstream. So, don't attempt to
112+
* restore either until we are at the downstream component.
113+
*/
114+
if (pcie_downstream_port(pdev) || !parent)
115+
return;
116+
117+
if (!pdev->l1ss || !parent->l1ss)
118+
return;
119+
120+
cl_save_state = pci_find_saved_ext_cap(pdev, PCI_EXT_CAP_ID_L1SS);
121+
pl_save_state = pci_find_saved_ext_cap(parent, PCI_EXT_CAP_ID_L1SS);
122+
if (!cl_save_state || !pl_save_state)
123+
return;
124+
125+
cap = &cl_save_state->cap.data[0];
126+
cl_ctl2 = *cap++;
127+
cl_ctl1 = *cap;
128+
cap = &pl_save_state->cap.data[0];
129+
pl_ctl2 = *cap++;
130+
pl_ctl1 = *cap;
131+
132+
/*
133+
* Disable L1.2 on this downstream endpoint device first, followed
134+
* by the upstream
135+
*/
136+
pci_clear_and_set_config_dword(pdev, pdev->l1ss + PCI_L1SS_CTL1,
137+
PCI_L1SS_CTL1_L1_2_MASK, 0);
138+
pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
139+
PCI_L1SS_CTL1_L1_2_MASK, 0);
140+
141+
/*
142+
* In addition, Common_Mode_Restore_Time and LTR_L1.2_THRESHOLD
143+
* in PCI_L1SS_CTL1 must be programmed *before* setting the L1.2
144+
* enable bits, even though they're all in PCI_L1SS_CTL1.
145+
*/
146+
pl_l1_2_enable = pl_ctl1 & PCI_L1SS_CTL1_L1_2_MASK;
147+
pl_ctl1 &= ~PCI_L1SS_CTL1_L1_2_MASK;
148+
cl_l1_2_enable = cl_ctl1 & PCI_L1SS_CTL1_L1_2_MASK;
149+
cl_ctl1 &= ~PCI_L1SS_CTL1_L1_2_MASK;
150+
151+
/* Write back without enables first (above we cleared them in ctl1) */
152+
pci_write_config_dword(parent, parent->l1ss + PCI_L1SS_CTL2, pl_ctl2);
153+
pci_write_config_dword(pdev, pdev->l1ss + PCI_L1SS_CTL2, cl_ctl2);
154+
pci_write_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, pl_ctl1);
155+
pci_write_config_dword(pdev, pdev->l1ss + PCI_L1SS_CTL1, cl_ctl1);
156+
157+
/* Then write back the enables */
158+
if (pl_l1_2_enable || cl_l1_2_enable) {
159+
pci_write_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
160+
pl_ctl1 | pl_l1_2_enable);
161+
pci_write_config_dword(pdev, pdev->l1ss + PCI_L1SS_CTL1,
162+
cl_ctl1 | cl_l1_2_enable);
163+
}
164+
}
165+
67166
#ifdef CONFIG_PCIEASPM
68167

69168
#ifdef MODULE_PARAM_PREFIX
@@ -1005,9 +1104,6 @@ void pci_configure_ltr(struct pci_dev *pdev)
10051104
if (!pci_is_pcie(pdev))
10061105
return;
10071106

1008-
/* Read L1 PM substate capabilities */
1009-
pdev->l1ss = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS);
1010-
10111107
pcie_capability_read_dword(pdev, PCI_EXP_DEVCAP2, &cap);
10121108
if (!(cap & PCI_EXP_DEVCAP2_LTR))
10131109
return;

drivers/pci/probe.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2259,6 +2259,7 @@ static void pci_configure_device(struct pci_dev *dev)
22592259
pci_configure_extended_tags(dev, NULL);
22602260
pci_configure_relaxed_ordering(dev);
22612261
pci_configure_ltr(dev);
2262+
pci_configure_aspm_l1ss(dev);
22622263
pci_configure_eetlp_prefix(dev);
22632264
pci_configure_serr(dev);
22642265

include/linux/pci.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,9 +390,9 @@ struct pci_dev {
390390
unsigned int d3hot_delay; /* D3hot->D0 transition time in ms */
391391
unsigned int d3cold_delay; /* D3cold->D0 transition time in ms */
392392

393+
u16 l1ss; /* L1SS Capability pointer */
393394
#ifdef CONFIG_PCIEASPM
394395
struct pcie_link_state *link_state; /* ASPM link state */
395-
u16 l1ss; /* L1SS Capability pointer */
396396
unsigned int ltr_path:1; /* Latency Tolerance Reporting
397397
supported from root to here */
398398
#endif

0 commit comments

Comments
 (0)