PCI/DPC: Save and restore config state
authorKeith Busch <keith.busch@intel.com>
Thu, 20 Sep 2018 16:27:08 +0000 (10:27 -0600)
committerBjorn Helgaas <bhelgaas@google.com>
Thu, 20 Sep 2018 21:06:27 +0000 (16:06 -0500)
This patch provides DPC save and restore capabilities.  This is necessary
for the driver to observe DPC events in the event the configuration space
needs to be restored after a reset.

Signed-off-by: Keith Busch <keith.busch@intel.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Sinan Kaya <okaya@kernel.org>
drivers/pci/pci.c
drivers/pci/pci.h
drivers/pci/pcie/dpc.c

index 0e54588825cbd611b2234da2eb0909ced2746bf6..d6bb56fbee6dfa4ff12cb6329aafc5daff242cdc 100644 (file)
@@ -1284,6 +1284,7 @@ int pci_save_state(struct pci_dev *dev)
        if (i != 0)
                return i;
 
+       pci_save_dpc_state(dev);
        return pci_save_vc_state(dev);
 }
 EXPORT_SYMBOL(pci_save_state);
@@ -1378,6 +1379,7 @@ void pci_restore_state(struct pci_dev *dev)
        pci_restore_ats_state(dev);
        pci_restore_vc_state(dev);
        pci_restore_rebar_state(dev);
+       pci_restore_dpc_state(dev);
 
        pci_cleanup_aer_error_status_regs(dev);
 
index 6e0d1528d471c7970613680f88333480389b6ef4..b5af5642c6c9d139f5ae728b5a3645632ce94d0b 100644 (file)
@@ -346,6 +346,14 @@ int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info);
 void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
 #endif /* CONFIG_PCIEAER */
 
+#ifdef CONFIG_PCIE_DPC
+void pci_save_dpc_state(struct pci_dev *dev);
+void pci_restore_dpc_state(struct pci_dev *dev);
+#else
+static inline void pci_save_dpc_state(struct pci_dev *dev) {}
+static inline void pci_restore_dpc_state(struct pci_dev *dev) {}
+#endif
+
 #ifdef CONFIG_PCI_ATS
 void pci_restore_ats_state(struct pci_dev *dev);
 #else
index a1fd16bf1cab2db66129de96591f81cae13b55d9..ed815a28512e1fc76ec927341fb90486f992d394 100644 (file)
@@ -44,6 +44,58 @@ static const char * const rp_pio_error_string[] = {
        "Memory Request Completion Timeout",             /* Bit Position 18 */
 };
 
+static struct dpc_dev *to_dpc_dev(struct pci_dev *dev)
+{
+       struct device *device;
+
+       device = pcie_port_find_device(dev, PCIE_PORT_SERVICE_DPC);
+       if (!device)
+               return NULL;
+       return get_service_data(to_pcie_device(device));
+}
+
+void pci_save_dpc_state(struct pci_dev *dev)
+{
+       struct dpc_dev *dpc;
+       struct pci_cap_saved_state *save_state;
+       u16 *cap;
+
+       if (!pci_is_pcie(dev))
+               return;
+
+       dpc = to_dpc_dev(dev);
+       if (!dpc)
+               return;
+
+       save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_DPC);
+       if (!save_state)
+               return;
+
+       cap = (u16 *)&save_state->cap.data[0];
+       pci_read_config_word(dev, dpc->cap_pos + PCI_EXP_DPC_CTL, cap);
+}
+
+void pci_restore_dpc_state(struct pci_dev *dev)
+{
+       struct dpc_dev *dpc;
+       struct pci_cap_saved_state *save_state;
+       u16 *cap;
+
+       if (!pci_is_pcie(dev))
+               return;
+
+       dpc = to_dpc_dev(dev);
+       if (!dpc)
+               return;
+
+       save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_DPC);
+       if (!save_state)
+               return;
+
+       cap = (u16 *)&save_state->cap.data[0];
+       pci_write_config_word(dev, dpc->cap_pos + PCI_EXP_DPC_CTL, *cap);
+}
+
 static int dpc_wait_rp_inactive(struct dpc_dev *dpc)
 {
        unsigned long timeout = jiffies + HZ;
@@ -67,18 +119,13 @@ static int dpc_wait_rp_inactive(struct dpc_dev *dpc)
 static pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)
 {
        struct dpc_dev *dpc;
-       struct pcie_device *pciedev;
-       struct device *devdpc;
-
        u16 cap;
 
        /*
         * DPC disables the Link automatically in hardware, so it has
         * already been reset by the time we get here.
         */
-       devdpc = pcie_port_find_device(pdev, PCIE_PORT_SERVICE_DPC);
-       pciedev = to_pcie_device(devdpc);
-       dpc = get_service_data(pciedev);
+       dpc = to_dpc_dev(pdev);
        cap = dpc->cap_pos;
 
        /*
@@ -259,6 +306,8 @@ static int dpc_probe(struct pcie_device *dev)
                FLAG(cap, PCI_EXP_DPC_CAP_POISONED_TLP),
                FLAG(cap, PCI_EXP_DPC_CAP_SW_TRIGGER), dpc->rp_log_size,
                FLAG(cap, PCI_EXP_DPC_CAP_DL_ACTIVE));
+
+       pci_add_ext_cap_save_buffer(pdev, PCI_EXT_CAP_ID_DPC, sizeof(u16));
        return status;
 }