Merge git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[cascardo/linux.git] / drivers / pci / pci.c
index 1e74e1e..fd47ac0 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/pci.h>
+#include <linux/pm.h>
 #include <linux/module.h>
 #include <linux/spinlock.h>
 #include <linux/string.h>
@@ -34,8 +35,7 @@ unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE;
  * Given a PCI bus, returns the highest PCI bus number present in the set
  * including the given PCI bus and its list of child PCI buses.
  */
-unsigned char __devinit
-pci_bus_max_busnr(struct pci_bus* bus)
+unsigned char pci_bus_max_busnr(struct pci_bus* bus)
 {
        struct list_head *tmp;
        unsigned char max, n;
@@ -551,7 +551,9 @@ static int pci_save_pcie_state(struct pci_dev *dev)
        if (pos <= 0)
                return 0;
 
-       save_state = kzalloc(sizeof(*save_state) + sizeof(u16) * 4, GFP_KERNEL);
+       save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP);
+       if (!save_state)
+               save_state = kzalloc(sizeof(*save_state) + sizeof(u16) * 4, GFP_KERNEL);
        if (!save_state) {
                dev_err(&dev->dev, "Out of memory in pci_save_pcie_state\n");
                return -ENOMEM;
@@ -582,8 +584,6 @@ static void pci_restore_pcie_state(struct pci_dev *dev)
        pci_write_config_word(dev, pos + PCI_EXP_LNKCTL, cap[i++]);
        pci_write_config_word(dev, pos + PCI_EXP_SLTCTL, cap[i++]);
        pci_write_config_word(dev, pos + PCI_EXP_RTCTL, cap[i++]);
-       pci_remove_saved_cap(save_state);
-       kfree(save_state);
 }
 
 
@@ -597,7 +597,9 @@ static int pci_save_pcix_state(struct pci_dev *dev)
        if (pos <= 0)
                return 0;
 
-       save_state = kzalloc(sizeof(*save_state) + sizeof(u16), GFP_KERNEL);
+       save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP);
+       if (!save_state)
+               save_state = kzalloc(sizeof(*save_state) + sizeof(u16), GFP_KERNEL);
        if (!save_state) {
                dev_err(&dev->dev, "Out of memory in pci_save_pcie_state\n");
                return -ENOMEM;
@@ -622,8 +624,6 @@ static void pci_restore_pcix_state(struct pci_dev *dev)
        cap = (u16 *)&save_state->data[0];
 
        pci_write_config_word(dev, pos + PCI_X_CMD, cap[i++]);
-       pci_remove_saved_cap(save_state);
-       kfree(save_state);
 }
 
 
@@ -638,8 +638,6 @@ pci_save_state(struct pci_dev *dev)
        /* XXX: 100% dword access ok here? */
        for (i = 0; i < 16; i++)
                pci_read_config_dword(dev, i * 4,&dev->saved_config_space[i]);
-       if ((i = pci_save_msi_state(dev)) != 0)
-               return i;
        if ((i = pci_save_pcie_state(dev)) != 0)
                return i;
        if ((i = pci_save_pcix_state(dev)) != 0)
@@ -757,7 +755,8 @@ int pci_enable_device(struct pci_dev *dev)
  * when a device is enabled using managed PCI device enable interface.
  */
 struct pci_devres {
-       unsigned int disable:1;
+       unsigned int enabled:1;
+       unsigned int pinned:1;
        unsigned int orig_intx:1;
        unsigned int restore_intx:1;
        u32 region_mask;
@@ -781,7 +780,7 @@ static void pcim_release(struct device *gendev, void *res)
        if (this->restore_intx)
                pci_intx(dev, this->orig_intx);
 
-       if (this->disable)
+       if (this->enabled && !this->pinned)
                pci_disable_device(dev);
 }
 
@@ -820,12 +819,12 @@ int pcim_enable_device(struct pci_dev *pdev)
        dr = get_pci_dr(pdev);
        if (unlikely(!dr))
                return -ENOMEM;
-       WARN_ON(!!dr->disable);
+       WARN_ON(!!dr->enabled);
 
        rc = pci_enable_device(pdev);
        if (!rc) {
                pdev->is_managed = 1;
-               dr->disable = 1;
+               dr->enabled = 1;
        }
        return rc;
 }
@@ -843,9 +842,9 @@ void pcim_pin_device(struct pci_dev *pdev)
        struct pci_devres *dr;
 
        dr = find_pci_dr(pdev);
-       WARN_ON(!dr || !dr->disable);
+       WARN_ON(!dr || !dr->enabled);
        if (dr)
-               dr->disable = 0;
+               dr->pinned = 1;
 }
 
 /**
@@ -876,18 +875,11 @@ pci_disable_device(struct pci_dev *dev)
 
        dr = find_pci_dr(dev);
        if (dr)
-               dr->disable = 0;
+               dr->enabled = 0;
 
        if (atomic_sub_return(1, &dev->enable_cnt) != 0)
                return;
 
-       if (dev->msi_enabled)
-               disable_msi_mode(dev, pci_find_capability(dev, PCI_CAP_ID_MSI),
-                       PCI_CAP_ID_MSI);
-       if (dev->msix_enabled)
-               disable_msi_mode(dev, pci_find_capability(dev, PCI_CAP_ID_MSI),
-                       PCI_CAP_ID_MSIX);
-
        pci_read_config_word(dev, PCI_COMMAND, &pci_command);
        if (pci_command & PCI_COMMAND_MASTER) {
                pci_command &= ~PCI_COMMAND_MASTER;
@@ -899,31 +891,76 @@ pci_disable_device(struct pci_dev *dev)
 }
 
 /**
- * pci_enable_wake - enable device to generate PME# when suspended
- * @dev: - PCI device to operate on
- * @state: - Current state of device.
- * @enable: - Flag to enable or disable generation
- * 
- * Set the bits in the device's PM Capabilities to generate PME# when
- * the system is suspended. 
+ * pcibios_set_pcie_reset_state - set reset state for device dev
+ * @dev: the PCI-E device reset
+ * @state: Reset state to enter into
+ *
+ *
+ * Sets the PCI-E reset state for the device. This is the default
+ * implementation. Architecture implementations can override this.
+ */
+int __attribute__ ((weak)) pcibios_set_pcie_reset_state(struct pci_dev *dev,
+                                                       enum pcie_reset_state state)
+{
+       return -EINVAL;
+}
+
+/**
+ * pci_set_pcie_reset_state - set reset state for device dev
+ * @dev: the PCI-E device reset
+ * @state: Reset state to enter into
+ *
+ *
+ * Sets the PCI reset state for the device.
+ */
+int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
+{
+       return pcibios_set_pcie_reset_state(dev, state);
+}
+
+/**
+ * pci_enable_wake - enable PCI device as wakeup event source
+ * @dev: PCI device affected
+ * @state: PCI state from which device will issue wakeup events
+ * @enable: True to enable event generation; false to disable
+ *
+ * This enables the device as a wakeup event source, or disables it.
+ * When such events involves platform-specific hooks, those hooks are
+ * called automatically by this routine.
+ *
+ * Devices with legacy power management (no standard PCI PM capabilities)
+ * always require such platform hooks.  Depending on the platform, devices
+ * supporting the standard PCI PME# signal may require such platform hooks;
+ * they always update bits in config space to allow PME# generation.
  *
- * -EIO is returned if device doesn't have PM Capabilities. 
- * -EINVAL is returned if device supports it, but can't generate wake events.
- * 0 if operation is successful.
- * 
+ * -EIO is returned if the device can't ever be a wakeup event source.
+ * -EINVAL is returned if the device can't generate wakeup events from
+ * the specified PCI state.  Returns zero if the operation is successful.
  */
 int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable)
 {
        int pm;
+       int status;
        u16 value;
 
+       /* Note that drivers should verify device_may_wakeup(&dev->dev)
+        * before calling this function.  Platform code should report
+        * errors when drivers try to enable wakeup on devices that
+        * can't issue wakeups, or on which wakeups were disabled by
+        * userspace updating the /sys/devices.../power/wakeup file.
+        */
+
+       status = call_platform_enable_wakeup(&dev->dev, enable);
+
        /* find PCI PM capability in list */
        pm = pci_find_capability(dev, PCI_CAP_ID_PM);
 
-       /* If device doesn't support PM Capabilities, but request is to disable
-        * wake events, it's a nop; otherwise fail */
-       if (!pm) 
-               return enable ? -EIO : 0; 
+       /* If device doesn't support PM Capabilities, but caller wants to
+        * disable wake events, it's a NOP.  Otherwise fail unless the
+        * platform hooks handled this legacy device already.
+        */
+       if (!pm)
+               return enable ? status : 0;
 
        /* Check device's ability to generate PME# */
        pci_read_config_word(dev,pm+PCI_PM_PMC,&value);
@@ -932,8 +969,14 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable)
        value >>= ffs(PCI_PM_CAP_PME_MASK) - 1;   /* First bit of mask */
 
        /* Check if it can generate PME# from requested state. */
-       if (!value || !(value & (1 << state))) 
+       if (!value || !(value & (1 << state))) {
+               /* if it can't, revert what the platform hook changed,
+                * always reporting the base "EINVAL, can't PME#" error
+                */
+               if (enable)
+                       call_platform_enable_wakeup(&dev->dev, 0);
                return enable ? -EINVAL : 0;
+       }
 
        pci_read_config_word(dev, pm + PCI_PM_CTRL, &value);
 
@@ -944,7 +987,7 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable)
                value &= ~PCI_PM_CTRL_PME_ENABLE;
 
        pci_write_config_word(dev, pm + PCI_PM_CTRL, value);
-       
+
        return 0;
 }
 
@@ -1277,6 +1320,33 @@ pci_intx(struct pci_dev *pdev, int enable)
        }
 }
 
+/**
+ * pci_msi_off - disables any msi or msix capabilities
+ * @dev: the PCI device to operate on
+ *
+ * If you want to use msi see pci_enable_msi and friends.
+ * This is a lower level primitive that allows us to disable
+ * msi operation at the device level.
+ */
+void pci_msi_off(struct pci_dev *dev)
+{
+       int pos;
+       u16 control;
+
+       pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
+       if (pos) {
+               pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);
+               control &= ~PCI_MSI_FLAGS_ENABLE;
+               pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control);
+       }
+       pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
+       if (pos) {
+               pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control);
+               control &= ~PCI_MSIX_FLAGS_ENABLE;
+               pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
+       }
+}
+
 #ifndef HAVE_ARCH_PCI_SET_DMA_MASK
 /*
  * These can be overridden by arch-specific implementations
@@ -1384,4 +1454,5 @@ EXPORT_SYMBOL(pci_set_power_state);
 EXPORT_SYMBOL(pci_save_state);
 EXPORT_SYMBOL(pci_restore_state);
 EXPORT_SYMBOL(pci_enable_wake);
+EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state);