]> Pileus Git - ~andy/linux/blobdiff - drivers/pci/pci.c
Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
[~andy/linux] / drivers / pci / pci.c
index 815674415267d831a2618b1e1f977c080d34ac92..8f169002dc7ec6b4581d4f7f049c35ff9ed7e8a2 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/interrupt.h>
 #include <linux/device.h>
 #include <linux/pm_runtime.h>
+#include <asm-generic/pci-bridge.h>
 #include <asm/setup.h>
 #include "pci.h"
 
@@ -967,16 +968,59 @@ pci_save_state(struct pci_dev *dev)
        return 0;
 }
 
+static void pci_restore_config_dword(struct pci_dev *pdev, int offset,
+                                    u32 saved_val, int retry)
+{
+       u32 val;
+
+       pci_read_config_dword(pdev, offset, &val);
+       if (val == saved_val)
+               return;
+
+       for (;;) {
+               dev_dbg(&pdev->dev, "restoring config space at offset "
+                       "%#x (was %#x, writing %#x)\n", offset, val, saved_val);
+               pci_write_config_dword(pdev, offset, saved_val);
+               if (retry-- <= 0)
+                       return;
+
+               pci_read_config_dword(pdev, offset, &val);
+               if (val == saved_val)
+                       return;
+
+               mdelay(1);
+       }
+}
+
+static void pci_restore_config_space_range(struct pci_dev *pdev,
+                                          int start, int end, int retry)
+{
+       int index;
+
+       for (index = end; index >= start; index--)
+               pci_restore_config_dword(pdev, 4 * index,
+                                        pdev->saved_config_space[index],
+                                        retry);
+}
+
+static void pci_restore_config_space(struct pci_dev *pdev)
+{
+       if (pdev->hdr_type == PCI_HEADER_TYPE_NORMAL) {
+               pci_restore_config_space_range(pdev, 10, 15, 0);
+               /* Restore BARs before the command register. */
+               pci_restore_config_space_range(pdev, 4, 9, 10);
+               pci_restore_config_space_range(pdev, 0, 3, 0);
+       } else {
+               pci_restore_config_space_range(pdev, 0, 15, 0);
+       }
+}
+
 /** 
  * pci_restore_state - Restore the saved state of a PCI device
  * @dev: - PCI device that we're dealing with
  */
 void pci_restore_state(struct pci_dev *dev)
 {
-       int i;
-       u32 val;
-       int tries;
-
        if (!dev->state_saved)
                return;
 
@@ -984,24 +1028,8 @@ void pci_restore_state(struct pci_dev *dev)
        pci_restore_pcie_state(dev);
        pci_restore_ats_state(dev);
 
-       /*
-        * The Base Address register should be programmed before the command
-        * register(s)
-        */
-       for (i = 15; i >= 0; i--) {
-               pci_read_config_dword(dev, i * 4, &val);
-               tries = 10;             
-               while (tries && val != dev->saved_config_space[i]) {
-                       dev_dbg(&dev->dev, "restoring config "
-                               "space at offset %#x (was %#x, writing %#x)\n",
-                               i, val, (int)dev->saved_config_space[i]);
-                       pci_write_config_dword(dev,i * 4,
-                               dev->saved_config_space[i]);
-                       pci_read_config_dword(dev, i * 4, &val);
-                       mdelay(10);
-                       tries--;
-               }
-       }
+       pci_restore_config_space(dev);
+
        pci_restore_pcix_state(dev);
        pci_restore_msi_state(dev);
        pci_restore_iov_state(dev);
@@ -3137,18 +3165,12 @@ static int pci_parent_bus_reset(struct pci_dev *dev, int probe)
        return 0;
 }
 
-static int pci_dev_reset(struct pci_dev *dev, int probe)
+static int __pci_dev_reset(struct pci_dev *dev, int probe)
 {
        int rc;
 
        might_sleep();
 
-       if (!probe) {
-               pci_cfg_access_lock(dev);
-               /* block PM suspend, driver probe, etc. */
-               device_lock(&dev->dev);
-       }
-
        rc = pci_dev_specific_reset(dev, probe);
        if (rc != -ENOTTY)
                goto done;
@@ -3167,14 +3189,27 @@ static int pci_dev_reset(struct pci_dev *dev, int probe)
 
        rc = pci_parent_bus_reset(dev, probe);
 done:
+       return rc;
+}
+
+static int pci_dev_reset(struct pci_dev *dev, int probe)
+{
+       int rc;
+
+       if (!probe) {
+               pci_cfg_access_lock(dev);
+               /* block PM suspend, driver probe, etc. */
+               device_lock(&dev->dev);
+       }
+
+       rc = __pci_dev_reset(dev, probe);
+
        if (!probe) {
                device_unlock(&dev->dev);
                pci_cfg_access_unlock(dev);
        }
-
        return rc;
 }
-
 /**
  * __pci_reset_function - reset a PCI device function
  * @dev: PCI device to reset
@@ -3219,7 +3254,7 @@ EXPORT_SYMBOL_GPL(__pci_reset_function);
  */
 int __pci_reset_function_locked(struct pci_dev *dev)
 {
-       return pci_dev_reset(dev, 1);
+       return __pci_dev_reset(dev, 0);
 }
 EXPORT_SYMBOL_GPL(__pci_reset_function_locked);
 
@@ -3866,6 +3901,8 @@ static int __init pci_setup(char *str)
                                pcie_bus_config = PCIE_BUS_PERFORMANCE;
                        } else if (!strncmp(str, "pcie_bus_peer2peer", 18)) {
                                pcie_bus_config = PCIE_BUS_PEER2PEER;
+                       } else if (!strncmp(str, "pcie_scan_all", 13)) {
+                               pci_add_flags(PCI_SCAN_ALL_PCIE_DEVS);
                        } else {
                                printk(KERN_ERR "PCI: Unknown option `%s'\n",
                                                str);