Many PCIe device drivers save the configuration state of their respective devices during probe and restore the same when their 'slot_reset' hook is called through PCIe Error Recovery Handler. If the system has a change in ASPM policy after the driver's probe is called and before error event occurred, 'slot_reset' hook restores the PCIe configuration state to what it was at the time of probe but not to what it was just before the occurrence of the error event. This effectively leads to a mismatch in the ASPM configuration between the device and its upstream parent device. Update the saved configuration state of the device with the latest info whenever there is a change w.r.t ASPM policy. Signed-off-by: Vidya Sagar <vidyas@xxxxxxxxxx> --- V4: * Rebased on top of pci/aspm branch V3: * Addressed sathyanarayanan.kuppuswamy's review comments V2: * Rebased on top of the tree code * Addressed Bjorn's review comments drivers/pci/pcie/aspm.c | 28 ++++++++++++++++++++++++++-- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index cfc5b84dc9c9..3db606ba9344 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1648,7 +1648,7 @@ static int pci_save_pcie_state(struct pci_dev *dev) pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &cap[i++]); pcie_capability_read_word(dev, PCI_EXP_SLTCTL2, &cap[i++]); - pci_save_aspm_state(dev); + pci_save_aspm_l1ss_state(dev); pci_save_ltr_state(dev); return 0; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index b217e74966eb..9fe78eb8b07d 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -95,7 +95,7 @@ void pci_msix_init(struct pci_dev *dev); bool pci_bridge_d3_possible(struct pci_dev *dev); void pci_bridge_d3_update(struct pci_dev *dev); void pci_aspm_get_l1ss(struct pci_dev *pdev); -void pci_save_aspm_state(struct pci_dev *pdev); +void pci_save_aspm_l1ss_state(struct pci_dev *pdev); void pci_restore_aspm_state(struct pci_dev *pdev); void pci_save_ltr_state(struct pci_dev *dev); void pci_restore_ltr_state(struct pci_dev *dev); diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 7f1d674ff171..a62648dd52bc 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -24,13 +24,29 @@ #include "../pci.h" +static void pci_save_aspm_state(struct pci_dev *dev) +{ + struct pci_cap_saved_state *save_state; + u16 *cap; + + if (!pci_is_pcie(dev)) + return; + + save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP); + if (!save_state) + return; + + cap = (u16 *)&save_state->cap.data[0]; + pcie_capability_read_word(dev, PCI_EXP_LNKCTL, &cap[1]); +} + void pci_aspm_get_l1ss(struct pci_dev *pdev) { /* Read L1 PM substate capabilities */ pdev->l1ss = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS); } -void pci_save_aspm_state(struct pci_dev *pdev) +void pci_save_aspm_l1ss_state(struct pci_dev *pdev) { struct pci_cap_saved_state *save_state; u16 l1ss = pdev->l1ss; @@ -309,10 +325,12 @@ static void pcie_set_clkpm_nocheck(struct pcie_link_state *link, int enable) struct pci_bus *linkbus = link->pdev->subordinate; u32 val = enable ? PCI_EXP_LNKCTL_CLKREQ_EN : 0; - list_for_each_entry(child, &linkbus->devices, bus_list) + list_for_each_entry(child, &linkbus->devices, bus_list) { pcie_capability_clear_and_set_word(child, PCI_EXP_LNKCTL, PCI_EXP_LNKCTL_CLKREQ_EN, val); + pci_save_aspm_state(child); + } link->clkpm_enabled = !!enable; } @@ -931,6 +949,12 @@ static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state) pcie_config_aspm_dev(parent, upstream); link->aspm_enabled = state; + + /* Update latest ASPM configuration in saved context */ + pci_save_aspm_state(link->downstream); + pci_save_aspm_l1ss_state(link->downstream); + pci_save_aspm_state(parent); + pci_save_aspm_l1ss_state(parent); } static void pcie_config_aspm_path(struct pcie_link_state *link) -- 2.25.1