Merge tag 'pci-v5.1-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
[sfrench/cifs-2.6.git] / drivers / pci / pci.c
index db55acf32a7eb8c802600f358718ce14c73a6ede..7c1b362f599aebc4bbf66acb09fe67fb5d76ea39 100644 (file)
@@ -861,7 +861,7 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
                if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot
                 && !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET))
                        need_restore = true;
-               /* Fall-through: force to D0 */
+               /* Fall-through - force to D0 */
        default:
                pmcsr = 0;
                break;
@@ -1233,7 +1233,6 @@ static void pci_restore_pcie_state(struct pci_dev *dev)
        pcie_capability_write_word(dev, PCI_EXP_SLTCTL2, cap[i++]);
 }
 
-
 static int pci_save_pcix_state(struct pci_dev *dev)
 {
        int pos;
@@ -1270,6 +1269,45 @@ static void pci_restore_pcix_state(struct pci_dev *dev)
        pci_write_config_word(dev, pos + PCI_X_CMD, cap[i++]);
 }
 
+static void pci_save_ltr_state(struct pci_dev *dev)
+{
+       int ltr;
+       struct pci_cap_saved_state *save_state;
+       u16 *cap;
+
+       if (!pci_is_pcie(dev))
+               return;
+
+       ltr = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_LTR);
+       if (!ltr)
+               return;
+
+       save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_LTR);
+       if (!save_state) {
+               pci_err(dev, "no suspend buffer for LTR; ASPM issues possible after resume\n");
+               return;
+       }
+
+       cap = (u16 *)&save_state->cap.data[0];
+       pci_read_config_word(dev, ltr + PCI_LTR_MAX_SNOOP_LAT, cap++);
+       pci_read_config_word(dev, ltr + PCI_LTR_MAX_NOSNOOP_LAT, cap++);
+}
+
+static void pci_restore_ltr_state(struct pci_dev *dev)
+{
+       struct pci_cap_saved_state *save_state;
+       int ltr;
+       u16 *cap;
+
+       save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_LTR);
+       ltr = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_LTR);
+       if (!save_state || !ltr)
+               return;
+
+       cap = (u16 *)&save_state->cap.data[0];
+       pci_write_config_word(dev, ltr + PCI_LTR_MAX_SNOOP_LAT, *cap++);
+       pci_write_config_word(dev, ltr + PCI_LTR_MAX_NOSNOOP_LAT, *cap++);
+}
 
 /**
  * pci_save_state - save the PCI configuration space of a device before suspending
@@ -1291,6 +1329,7 @@ int pci_save_state(struct pci_dev *dev)
        if (i != 0)
                return i;
 
+       pci_save_ltr_state(dev);
        pci_save_dpc_state(dev);
        return pci_save_vc_state(dev);
 }
@@ -1390,7 +1429,12 @@ void pci_restore_state(struct pci_dev *dev)
        if (!dev->state_saved)
                return;
 
-       /* PCI Express register must be restored first */
+       /*
+        * Restore max latencies (in the LTR capability) before enabling
+        * LTR itself (in the PCIe capability).
+        */
+       pci_restore_ltr_state(dev);
+
        pci_restore_pcie_state(dev);
        pci_restore_pasid_state(dev);
        pci_restore_pri_state(dev);
@@ -2260,7 +2304,7 @@ static pci_power_t pci_target_state(struct pci_dev *dev, bool wakeup)
                case PCI_D2:
                        if (pci_no_d1d2(dev))
                                break;
-                       /* else: fall through */
+                       /* else, fall through */
                default:
                        target_state = state;
                }
@@ -2501,6 +2545,25 @@ void pci_config_pm_runtime_put(struct pci_dev *pdev)
                pm_runtime_put_sync(parent);
 }
 
+static const struct dmi_system_id bridge_d3_blacklist[] = {
+#ifdef CONFIG_X86
+       {
+               /*
+                * Gigabyte X299 root port is not marked as hotplug capable
+                * which allows Linux to power manage it.  However, this
+                * confuses the BIOS SMI handler so don't power manage root
+                * ports on that system.
+                */
+               .ident = "X299 DESIGNARE EX-CF",
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
+                       DMI_MATCH(DMI_BOARD_NAME, "X299 DESIGNARE EX-CF"),
+               },
+       },
+#endif
+       { }
+};
+
 /**
  * pci_bridge_d3_possible - Is it possible to put the bridge into D3
  * @bridge: Bridge to check
@@ -2546,6 +2609,9 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge)
                if (bridge->is_hotplug_bridge)
                        return false;
 
+               if (dmi_check_system(bridge_d3_blacklist))
+                       return false;
+
                /*
                 * It should be safe to put PCIe ports from 2015 or newer
                 * to D3.
@@ -2998,6 +3064,11 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev)
        if (error)
                pci_err(dev, "unable to preallocate PCI-X save buffer\n");
 
+       error = pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_LTR,
+                                           2 * sizeof(u16));
+       if (error)
+               pci_err(dev, "unable to allocate suspend buffer for LTR\n");
+
        pci_allocate_vc_save_buffers(dev);
 }
 
@@ -5058,39 +5129,42 @@ unlock:
        return 0;
 }
 
-/* Save and disable devices from the top of the tree down */
-static void pci_bus_save_and_disable(struct pci_bus *bus)
+/*
+ * Save and disable devices from the top of the tree down while holding
+ * the @dev mutex lock for the entire tree.
+ */
+static void pci_bus_save_and_disable_locked(struct pci_bus *bus)
 {
        struct pci_dev *dev;
 
        list_for_each_entry(dev, &bus->devices, bus_list) {
-               pci_dev_lock(dev);
                pci_dev_save_and_disable(dev);
-               pci_dev_unlock(dev);
                if (dev->subordinate)
-                       pci_bus_save_and_disable(dev->subordinate);
+                       pci_bus_save_and_disable_locked(dev->subordinate);
        }
 }
 
 /*
- * Restore devices from top of the tree down - parent bridges need to be
- * restored before we can get to subordinate devices.
+ * Restore devices from top of the tree down while holding @dev mutex lock
+ * for the entire tree.  Parent bridges need to be restored before we can
+ * get to subordinate devices.
  */
-static void pci_bus_restore(struct pci_bus *bus)
+static void pci_bus_restore_locked(struct pci_bus *bus)
 {
        struct pci_dev *dev;
 
        list_for_each_entry(dev, &bus->devices, bus_list) {
-               pci_dev_lock(dev);
                pci_dev_restore(dev);
-               pci_dev_unlock(dev);
                if (dev->subordinate)
-                       pci_bus_restore(dev->subordinate);
+                       pci_bus_restore_locked(dev->subordinate);
        }
 }
 
-/* Save and disable devices from the top of the tree down */
-static void pci_slot_save_and_disable(struct pci_slot *slot)
+/*
+ * Save and disable devices from the top of the tree down while holding
+ * the @dev mutex lock for the entire tree.
+ */
+static void pci_slot_save_and_disable_locked(struct pci_slot *slot)
 {
        struct pci_dev *dev;
 
@@ -5099,26 +5173,25 @@ static void pci_slot_save_and_disable(struct pci_slot *slot)
                        continue;
                pci_dev_save_and_disable(dev);
                if (dev->subordinate)
-                       pci_bus_save_and_disable(dev->subordinate);
+                       pci_bus_save_and_disable_locked(dev->subordinate);
        }
 }
 
 /*
- * Restore devices from top of the tree down - parent bridges need to be
- * restored before we can get to subordinate devices.
+ * Restore devices from top of the tree down while holding @dev mutex lock
+ * for the entire tree.  Parent bridges need to be restored before we can
+ * get to subordinate devices.
  */
-static void pci_slot_restore(struct pci_slot *slot)
+static void pci_slot_restore_locked(struct pci_slot *slot)
 {
        struct pci_dev *dev;
 
        list_for_each_entry(dev, &slot->bus->devices, bus_list) {
                if (!dev->slot || dev->slot != slot)
                        continue;
-               pci_dev_lock(dev);
                pci_dev_restore(dev);
-               pci_dev_unlock(dev);
                if (dev->subordinate)
-                       pci_bus_restore(dev->subordinate);
+                       pci_bus_restore_locked(dev->subordinate);
        }
 }
 
@@ -5177,17 +5250,15 @@ static int __pci_reset_slot(struct pci_slot *slot)
        if (rc)
                return rc;
 
-       pci_slot_save_and_disable(slot);
-
        if (pci_slot_trylock(slot)) {
+               pci_slot_save_and_disable_locked(slot);
                might_sleep();
                rc = pci_reset_hotplug_slot(slot->hotplug, 0);
+               pci_slot_restore_locked(slot);
                pci_slot_unlock(slot);
        } else
                rc = -EAGAIN;
 
-       pci_slot_restore(slot);
-
        return rc;
 }
 
@@ -5273,17 +5344,15 @@ static int __pci_reset_bus(struct pci_bus *bus)
        if (rc)
                return rc;
 
-       pci_bus_save_and_disable(bus);
-
        if (pci_bus_trylock(bus)) {
+               pci_bus_save_and_disable_locked(bus);
                might_sleep();
                rc = pci_bridge_secondary_bus_reset(bus->self);
+               pci_bus_restore_locked(bus);
                pci_bus_unlock(bus);
        } else
                rc = -EAGAIN;
 
-       pci_bus_restore(bus);
-
        return rc;
 }
 
@@ -6000,8 +6069,7 @@ void pci_reassigndev_resource_alignment(struct pci_dev *dev)
         * to enable the kernel to reassign new resource
         * window later on.
         */
-       if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE &&
-           (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
+       if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
                for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) {
                        r = &dev->resource[i];
                        if (!(r->flags & IORESOURCE_MEM))