]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
PCI/ASPM: Update save_state when configuration changes
authorVidya Sagar <vidyas@nvidia.com>
Fri, 23 Feb 2024 19:36:24 +0000 (13:36 -0600)
committerBjorn Helgaas <bhelgaas@google.com>
Tue, 12 Mar 2024 17:09:12 +0000 (12:09 -0500)
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/20240222174436.3565146-1-vidyas@nvidia.com
Signed-off-by: Vidya Sagar <vidyas@nvidia.com>
[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 <bhelgaas@google.com>
Reviewed-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
Reviewed-by: David E. Box <david.e.box@linux.intel.com>
drivers/pci/pcie/aspm.c

index 1379b8decdf1dc0393bfa36b3e586263be6faed9..10160d82c10a6d7036c68f73f95e24e141c98e84 100644 (file)
@@ -301,16 +301,42 @@ static int policy_to_clkpm_state(struct pcie_link_state *link)
        return 0;
 }
 
+static void pci_update_aspm_saved_state(struct pci_dev *dev)
+{
+       struct pci_cap_saved_state *save_state;
+       u16 *cap, lnkctl, aspm_ctl;
+
+       save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP);
+       if (!save_state)
+               return;
+
+       pcie_capability_read_word(dev, PCI_EXP_LNKCTL, &lnkctl);
+
+       /*
+        * Update ASPM and CLKREQ bits of LNKCTL in save_state. We only
+        * write PCI_EXP_LNKCTL_CCC during enumeration, so it shouldn't
+        * change after being captured in save_state.
+        */
+       aspm_ctl = lnkctl & (PCI_EXP_LNKCTL_ASPMC | PCI_EXP_LNKCTL_CLKREQ_EN);
+       lnkctl &= ~(PCI_EXP_LNKCTL_ASPMC | PCI_EXP_LNKCTL_CLKREQ_EN);
+
+       /* Depends on pci_save_pcie_state(): cap[1] is LNKCTL */
+       cap = (u16 *)&save_state->cap.data[0];
+       cap[1] = lnkctl | aspm_ctl;
+}
+
 static void pcie_set_clkpm_nocheck(struct pcie_link_state *link, int enable)
 {
        struct pci_dev *child;
        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_update_aspm_saved_state(child);
+       }
        link->clkpm_enabled = !!enable;
 }
 
@@ -929,6 +955,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_l1ss_state(link->downstream);
+       pci_update_aspm_saved_state(link->downstream);
+       pci_save_aspm_l1ss_state(parent);
+       pci_update_aspm_saved_state(parent);
 }
 
 static void pcie_config_aspm_path(struct pcie_link_state *link)