]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/pci/pci.c
Merge tag 'pci-v4.13-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaa...
[karo-tx-linux.git] / drivers / pci / pci.c
index 0b5302a9fdaef5e1a92d0ac521e1478eab096ec5..d88edf5c563b6f07a709c917204b0a77f54ab1f3 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/pci_hotplug.h>
 #include <linux/vmalloc.h>
+#include <linux/pci-ats.h>
 #include <asm/setup.h>
 #include <asm/dma.h>
 #include <linux/aer.h>
@@ -455,7 +456,7 @@ struct resource *pci_find_parent_resource(const struct pci_dev *dev,
        pci_bus_for_each_resource(bus, r, i) {
                if (!r)
                        continue;
-               if (res->start && resource_contains(r, res)) {
+               if (resource_contains(r, res)) {
 
                        /*
                         * If the window is prefetchable but the BAR is
@@ -1166,6 +1167,8 @@ void pci_restore_state(struct pci_dev *dev)
 
        /* PCI Express register must be restored first */
        pci_restore_pcie_state(dev);
+       pci_restore_pasid_state(dev);
+       pci_restore_pri_state(dev);
        pci_restore_ats_state(dev);
        pci_restore_vc_state(dev);
 
@@ -1966,12 +1969,13 @@ EXPORT_SYMBOL(pci_wake_from_d3);
 /**
  * pci_target_state - find an appropriate low power state for a given PCI dev
  * @dev: PCI device
+ * @wakeup: Whether or not wakeup functionality will be enabled for the device.
  *
  * Use underlying platform code to find a supported low power state for @dev.
  * If the platform can't manage @dev, return the deepest state from which it
  * can generate wake events, based on any available PME info.
  */
-static pci_power_t pci_target_state(struct pci_dev *dev)
+static pci_power_t pci_target_state(struct pci_dev *dev, bool wakeup)
 {
        pci_power_t target_state = PCI_D3hot;
 
@@ -2008,7 +2012,7 @@ static pci_power_t pci_target_state(struct pci_dev *dev)
        if (dev->current_state == PCI_D3cold)
                target_state = PCI_D3cold;
 
-       if (device_may_wakeup(&dev->dev)) {
+       if (wakeup) {
                /*
                 * Find the deepest state from which the device can generate
                 * wake-up events, make it the target state and enable device
@@ -2034,13 +2038,14 @@ static pci_power_t pci_target_state(struct pci_dev *dev)
  */
 int pci_prepare_to_sleep(struct pci_dev *dev)
 {
-       pci_power_t target_state = pci_target_state(dev);
+       bool wakeup = device_may_wakeup(&dev->dev);
+       pci_power_t target_state = pci_target_state(dev, wakeup);
        int error;
 
        if (target_state == PCI_POWER_ERROR)
                return -EIO;
 
-       pci_enable_wake(dev, target_state, device_may_wakeup(&dev->dev));
+       pci_enable_wake(dev, target_state, wakeup);
 
        error = pci_set_power_state(dev, target_state);
 
@@ -2073,9 +2078,10 @@ EXPORT_SYMBOL(pci_back_from_sleep);
  */
 int pci_finish_runtime_suspend(struct pci_dev *dev)
 {
-       pci_power_t target_state = pci_target_state(dev);
+       pci_power_t target_state;
        int error;
 
+       target_state = pci_target_state(dev, device_can_wakeup(&dev->dev));
        if (target_state == PCI_POWER_ERROR)
                return -EIO;
 
@@ -2111,8 +2117,8 @@ bool pci_dev_run_wake(struct pci_dev *dev)
        if (!dev->pme_support)
                return false;
 
-       /* PME-capable in principle, but not from the intended sleep state */
-       if (!pci_pme_capable(dev, pci_target_state(dev)))
+       /* PME-capable in principle, but not from the target power state */
+       if (!pci_pme_capable(dev, pci_target_state(dev, false)))
                return false;
 
        while (bus->parent) {
@@ -2147,9 +2153,10 @@ EXPORT_SYMBOL_GPL(pci_dev_run_wake);
 bool pci_dev_keep_suspended(struct pci_dev *pci_dev)
 {
        struct device *dev = &pci_dev->dev;
+       bool wakeup = device_may_wakeup(dev);
 
        if (!pm_runtime_suspended(dev)
-           || pci_target_state(pci_dev) != pci_dev->current_state
+           || pci_target_state(pci_dev, wakeup) != pci_dev->current_state
            || platform_pci_need_resume(pci_dev)
            || (pci_dev->dev_flags & PCI_DEV_FLAGS_NEEDS_RESUME))
                return false;
@@ -2167,7 +2174,7 @@ bool pci_dev_keep_suspended(struct pci_dev *pci_dev)
        spin_lock_irq(&dev->power.lock);
 
        if (pm_runtime_suspended(dev) && pci_dev->current_state < PCI_D3cold &&
-           !device_may_wakeup(dev))
+           !wakeup)
                __pci_pme_active(pci_dev, false);
 
        spin_unlock_irq(&dev->power.lock);
@@ -3715,46 +3722,6 @@ void pci_intx(struct pci_dev *pdev, int enable)
 }
 EXPORT_SYMBOL_GPL(pci_intx);
 
-/**
- * pci_intx_mask_supported - probe for INTx masking support
- * @dev: the PCI device to operate on
- *
- * Check if the device dev support INTx masking via the config space
- * command word.
- */
-bool pci_intx_mask_supported(struct pci_dev *dev)
-{
-       bool mask_supported = false;
-       u16 orig, new;
-
-       if (dev->broken_intx_masking)
-               return false;
-
-       pci_cfg_access_lock(dev);
-
-       pci_read_config_word(dev, PCI_COMMAND, &orig);
-       pci_write_config_word(dev, PCI_COMMAND,
-                             orig ^ PCI_COMMAND_INTX_DISABLE);
-       pci_read_config_word(dev, PCI_COMMAND, &new);
-
-       /*
-        * There's no way to protect against hardware bugs or detect them
-        * reliably, but as long as we know what the value should be, let's
-        * go ahead and check it.
-        */
-       if ((new ^ orig) & ~PCI_COMMAND_INTX_DISABLE) {
-               dev_err(&dev->dev, "Command register changed from 0x%x to 0x%x: driver or hardware bug?\n",
-                       orig, new);
-       } else if ((new ^ orig) & PCI_COMMAND_INTX_DISABLE) {
-               mask_supported = true;
-               pci_write_config_word(dev, PCI_COMMAND, orig);
-       }
-
-       pci_cfg_access_unlock(dev);
-       return mask_supported;
-}
-EXPORT_SYMBOL_GPL(pci_intx_mask_supported);
-
 static bool pci_check_and_set_intx_mask(struct pci_dev *dev, bool mask)
 {
        struct pci_bus *bus = dev->bus;
@@ -3805,7 +3772,7 @@ done:
  * @dev: the PCI device to operate on
  *
  * Check if the device dev has its INTx line asserted, mask it and
- * return true in that case. False is returned if not interrupt was
+ * return true in that case. False is returned if no interrupt was
  * pending.
  */
 bool pci_check_and_mask_intx(struct pci_dev *dev)
@@ -4075,40 +4042,6 @@ static int pci_dev_reset_slot_function(struct pci_dev *dev, int probe)
        return pci_reset_hotplug_slot(dev->slot->hotplug, probe);
 }
 
-static int __pci_dev_reset(struct pci_dev *dev, int probe)
-{
-       int rc;
-
-       might_sleep();
-
-       rc = pci_dev_specific_reset(dev, probe);
-       if (rc != -ENOTTY)
-               goto done;
-
-       if (pcie_has_flr(dev)) {
-               if (!probe)
-                       pcie_flr(dev);
-               rc = 0;
-               goto done;
-       }
-
-       rc = pci_af_flr(dev, probe);
-       if (rc != -ENOTTY)
-               goto done;
-
-       rc = pci_pm_reset(dev, probe);
-       if (rc != -ENOTTY)
-               goto done;
-
-       rc = pci_dev_reset_slot_function(dev, probe);
-       if (rc != -ENOTTY)
-               goto done;
-
-       rc = pci_parent_bus_reset(dev, probe);
-done:
-       return rc;
-}
-
 static void pci_dev_lock(struct pci_dev *dev)
 {
        pci_cfg_access_lock(dev);
@@ -4134,26 +4067,18 @@ static void pci_dev_unlock(struct pci_dev *dev)
        pci_cfg_access_unlock(dev);
 }
 
-/**
- * pci_reset_notify - notify device driver of reset
- * @dev: device to be notified of reset
- * @prepare: 'true' if device is about to be reset; 'false' if reset attempt
- *           completed
- *
- * Must be called prior to device access being disabled and after device
- * access is restored.
- */
-static void pci_reset_notify(struct pci_dev *dev, bool prepare)
+static void pci_dev_save_and_disable(struct pci_dev *dev)
 {
        const struct pci_error_handlers *err_handler =
                        dev->driver ? dev->driver->err_handler : NULL;
-       if (err_handler && err_handler->reset_notify)
-               err_handler->reset_notify(dev, prepare);
-}
 
-static void pci_dev_save_and_disable(struct pci_dev *dev)
-{
-       pci_reset_notify(dev, true);
+       /*
+        * dev->driver->err_handler->reset_prepare() is protected against
+        * races with ->remove() by the device lock, which must be held by
+        * the caller.
+        */
+       if (err_handler && err_handler->reset_prepare)
+               err_handler->reset_prepare(dev);
 
        /*
         * Wake-up device prior to save.  PM registers default to D0 after
@@ -4175,23 +4100,18 @@ static void pci_dev_save_and_disable(struct pci_dev *dev)
 
 static void pci_dev_restore(struct pci_dev *dev)
 {
-       pci_restore_state(dev);
-       pci_reset_notify(dev, false);
-}
-
-static int pci_dev_reset(struct pci_dev *dev, int probe)
-{
-       int rc;
-
-       if (!probe)
-               pci_dev_lock(dev);
-
-       rc = __pci_dev_reset(dev, probe);
+       const struct pci_error_handlers *err_handler =
+                       dev->driver ? dev->driver->err_handler : NULL;
 
-       if (!probe)
-               pci_dev_unlock(dev);
+       pci_restore_state(dev);
 
-       return rc;
+       /*
+        * dev->driver->err_handler->reset_done() is protected against
+        * races with ->remove() by the device lock, which must be held by
+        * the caller.
+        */
+       if (err_handler && err_handler->reset_done)
+               err_handler->reset_done(dev);
 }
 
 /**
@@ -4213,7 +4133,13 @@ static int pci_dev_reset(struct pci_dev *dev, int probe)
  */
 int __pci_reset_function(struct pci_dev *dev)
 {
-       return pci_dev_reset(dev, 0);
+       int ret;
+
+       pci_dev_lock(dev);
+       ret = __pci_reset_function_locked(dev);
+       pci_dev_unlock(dev);
+
+       return ret;
 }
 EXPORT_SYMBOL_GPL(__pci_reset_function);
 
@@ -4238,7 +4164,27 @@ EXPORT_SYMBOL_GPL(__pci_reset_function);
  */
 int __pci_reset_function_locked(struct pci_dev *dev)
 {
-       return __pci_dev_reset(dev, 0);
+       int rc;
+
+       might_sleep();
+
+       rc = pci_dev_specific_reset(dev, 0);
+       if (rc != -ENOTTY)
+               return rc;
+       if (pcie_has_flr(dev)) {
+               pcie_flr(dev);
+               return 0;
+       }
+       rc = pci_af_flr(dev, 0);
+       if (rc != -ENOTTY)
+               return rc;
+       rc = pci_pm_reset(dev, 0);
+       if (rc != -ENOTTY)
+               return rc;
+       rc = pci_dev_reset_slot_function(dev, 0);
+       if (rc != -ENOTTY)
+               return rc;
+       return pci_parent_bus_reset(dev, 0);
 }
 EXPORT_SYMBOL_GPL(__pci_reset_function_locked);
 
@@ -4255,7 +4201,26 @@ EXPORT_SYMBOL_GPL(__pci_reset_function_locked);
  */
 int pci_probe_reset_function(struct pci_dev *dev)
 {
-       return pci_dev_reset(dev, 1);
+       int rc;
+
+       might_sleep();
+
+       rc = pci_dev_specific_reset(dev, 1);
+       if (rc != -ENOTTY)
+               return rc;
+       if (pcie_has_flr(dev))
+               return 0;
+       rc = pci_af_flr(dev, 1);
+       if (rc != -ENOTTY)
+               return rc;
+       rc = pci_pm_reset(dev, 1);
+       if (rc != -ENOTTY)
+               return rc;
+       rc = pci_dev_reset_slot_function(dev, 1);
+       if (rc != -ENOTTY)
+               return rc;
+
+       return pci_parent_bus_reset(dev, 1);
 }
 
 /**
@@ -4278,15 +4243,17 @@ int pci_reset_function(struct pci_dev *dev)
 {
        int rc;
 
-       rc = pci_dev_reset(dev, 1);
+       rc = pci_probe_reset_function(dev);
        if (rc)
                return rc;
 
+       pci_dev_lock(dev);
        pci_dev_save_and_disable(dev);
 
-       rc = pci_dev_reset(dev, 0);
+       rc = __pci_reset_function_locked(dev);
 
        pci_dev_restore(dev);
+       pci_dev_unlock(dev);
 
        return rc;
 }
@@ -4302,20 +4269,18 @@ int pci_try_reset_function(struct pci_dev *dev)
 {
        int rc;
 
-       rc = pci_dev_reset(dev, 1);
+       rc = pci_probe_reset_function(dev);
        if (rc)
                return rc;
 
-       pci_dev_save_and_disable(dev);
+       if (!pci_dev_trylock(dev))
+               return -EAGAIN;
 
-       if (pci_dev_trylock(dev)) {
-               rc = __pci_dev_reset(dev, 0);
-               pci_dev_unlock(dev);
-       } else
-               rc = -EAGAIN;
+       pci_dev_save_and_disable(dev);
+       rc = __pci_reset_function_locked(dev);
+       pci_dev_unlock(dev);
 
        pci_dev_restore(dev);
-
        return rc;
 }
 EXPORT_SYMBOL_GPL(pci_try_reset_function);
@@ -4465,7 +4430,9 @@ static void pci_bus_save_and_disable(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);
        }
@@ -4480,7 +4447,9 @@ static void pci_bus_restore(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);
        }