Skip to content

Commit 6d42666

Browse files
Vidya Sagarbjorn-helgaas
authored andcommitted
PCI/ASPM: Update save_state when configuration changes
Many PCIe device drivers save the configuration state of their device during probe and restore it when their .slot_reset() hook is called during PCIe error recovery. If the ASPM configuration is changed after the driver's probe is called and before an error event occurs, .slot_reset() restores the ASPM configuration to what it was at the time of probe, not to what it was just before the occurrence of the error event. This leads to a mismatch in ASPM configuration between the device and its upstream device. Update the saved configuration of the device when the ASPM configuration changes. Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Vidya Sagar <[email protected]> [bhelgaas: commit log, rebase to pci/aspm, rename to pci_update_aspm_saved_state() since it updates only LNKCTL, update only ASPMC and CLKREQ_EN in LNKCTL] Signed-off-by: Bjorn Helgaas <[email protected]> Reviewed-by: Kuppuswamy Sathyanarayanan <[email protected]> Reviewed-by: David E. Box <[email protected]>
1 parent 64dbb2d commit 6d42666

File tree

1 file changed

+33
-1
lines changed

1 file changed

+33
-1
lines changed

drivers/pci/pcie/aspm.c

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,16 +301,42 @@ static int policy_to_clkpm_state(struct pcie_link_state *link)
301301
return 0;
302302
}
303303

304+
static void pci_update_aspm_saved_state(struct pci_dev *dev)
305+
{
306+
struct pci_cap_saved_state *save_state;
307+
u16 *cap, lnkctl, aspm_ctl;
308+
309+
save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP);
310+
if (!save_state)
311+
return;
312+
313+
pcie_capability_read_word(dev, PCI_EXP_LNKCTL, &lnkctl);
314+
315+
/*
316+
* Update ASPM and CLKREQ bits of LNKCTL in save_state. We only
317+
* write PCI_EXP_LNKCTL_CCC during enumeration, so it shouldn't
318+
* change after being captured in save_state.
319+
*/
320+
aspm_ctl = lnkctl & (PCI_EXP_LNKCTL_ASPMC | PCI_EXP_LNKCTL_CLKREQ_EN);
321+
lnkctl &= ~(PCI_EXP_LNKCTL_ASPMC | PCI_EXP_LNKCTL_CLKREQ_EN);
322+
323+
/* Depends on pci_save_pcie_state(): cap[1] is LNKCTL */
324+
cap = (u16 *)&save_state->cap.data[0];
325+
cap[1] = lnkctl | aspm_ctl;
326+
}
327+
304328
static void pcie_set_clkpm_nocheck(struct pcie_link_state *link, int enable)
305329
{
306330
struct pci_dev *child;
307331
struct pci_bus *linkbus = link->pdev->subordinate;
308332
u32 val = enable ? PCI_EXP_LNKCTL_CLKREQ_EN : 0;
309333

310-
list_for_each_entry(child, &linkbus->devices, bus_list)
334+
list_for_each_entry(child, &linkbus->devices, bus_list) {
311335
pcie_capability_clear_and_set_word(child, PCI_EXP_LNKCTL,
312336
PCI_EXP_LNKCTL_CLKREQ_EN,
313337
val);
338+
pci_update_aspm_saved_state(child);
339+
}
314340
link->clkpm_enabled = !!enable;
315341
}
316342

@@ -929,6 +955,12 @@ static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state)
929955
pcie_config_aspm_dev(parent, upstream);
930956

931957
link->aspm_enabled = state;
958+
959+
/* Update latest ASPM configuration in saved context */
960+
pci_save_aspm_l1ss_state(link->downstream);
961+
pci_update_aspm_saved_state(link->downstream);
962+
pci_save_aspm_l1ss_state(parent);
963+
pci_update_aspm_saved_state(parent);
932964
}
933965

934966
static void pcie_config_aspm_path(struct pcie_link_state *link)

0 commit comments

Comments
 (0)