summaryrefslogtreecommitdiff
path: root/drivers/pci/pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/pci.c')
-rw-r--r--drivers/pci/pci.c67
1 files changed, 51 insertions, 16 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 5e7a94a8c9ca..0f6ff9fe7760 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -229,7 +229,7 @@ pci_find_parent_resource(const struct pci_dev *dev, struct resource *res)
/**
* pci_set_power_state - Set the power state of a PCI device
* @dev: PCI device to be suspended
- * @state: Power state we're entering
+ * @state: PCI power state (D0, D1, D2, D3hot, D3cold) we're entering
*
* Transition a device to a new power state, using the Power Management
* Capabilities in the device's config space.
@@ -242,19 +242,20 @@ pci_find_parent_resource(const struct pci_dev *dev, struct resource *res)
*/
int
-pci_set_power_state(struct pci_dev *dev, int state)
+pci_set_power_state(struct pci_dev *dev, pci_power_t state)
{
int pm;
- u16 pmcsr;
+ u16 pmcsr, pmc;
/* bound the state we're entering */
- if (state > 3) state = 3;
+ if (state > PCI_D3hot)
+ state = PCI_D3hot;
/* Validate current state:
* Can enter D0 from any state, but if we can only go deeper
* to sleep if we're already in a low power state
*/
- if (state > 0 && dev->current_state > state)
+ if (state != PCI_D0 && dev->current_state > state)
return -EINVAL;
else if (dev->current_state == state)
return 0; /* we're already there */
@@ -263,21 +264,30 @@ pci_set_power_state(struct pci_dev *dev, int state)
pm = pci_find_capability(dev, PCI_CAP_ID_PM);
/* abort if the device doesn't support PM capabilities */
- if (!pm) return -EIO;
+ if (!pm)
+ return -EIO;
+
+ pci_read_config_word(dev,pm + PCI_PM_PMC,&pmc);
+ if ((pmc & PCI_PM_CAP_VER_MASK) != 2) {
+ printk(KERN_WARNING
+ "PCI: %s has unsupported PM cap regs version (%u)\n",
+ dev->slot_name, pmc & PCI_PM_CAP_VER_MASK);
+ return -EIO;
+ }
/* check if this device supports the desired state */
- if (state == 1 || state == 2) {
- u16 pmc;
- pci_read_config_word(dev,pm + PCI_PM_PMC,&pmc);
- if (state == 1 && !(pmc & PCI_PM_CAP_D1)) return -EIO;
- else if (state == 2 && !(pmc & PCI_PM_CAP_D2)) return -EIO;
+ if (state == PCI_D1 || state == PCI_D2) {
+ if (state == PCI_D1 && !(pmc & PCI_PM_CAP_D1))
+ return -EIO;
+ else if (state == PCI_D2 && !(pmc & PCI_PM_CAP_D2))
+ return -EIO;
}
/* If we're in D3, force entire word to 0.
* This doesn't affect PME_Status, disables PME_En, and
* sets PowerState to 0.
*/
- if (dev->current_state >= 3)
+ if (dev->current_state >= PCI_D3hot)
pmcsr = 0;
else {
pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);
@@ -290,9 +300,9 @@ pci_set_power_state(struct pci_dev *dev, int state)
/* Mandatory power management transition delays */
/* see PCI PM 1.1 5.6.1 table 18 */
- if(state == 3 || dev->current_state == 3)
+ if (state == PCI_D3hot || dev->current_state == PCI_D3hot)
msleep(10);
- else if(state == 2 || dev->current_state == 2)
+ else if (state == PCI_D2 || dev->current_state == PCI_D2)
udelay(200);
dev->current_state = state;
@@ -300,6 +310,31 @@ pci_set_power_state(struct pci_dev *dev, int state)
}
/**
+ * pci_choose_state - Choose the power state of a PCI device
+ * @dev: PCI device to be suspended
+ * @state: target sleep state for the whole system
+ *
+ * Returns PCI power state suitable for given device and given system
+ * message.
+ */
+
+pci_power_t pci_choose_state(struct pci_dev *dev, u32 state)
+{
+ if (!pci_find_capability(dev, PCI_CAP_ID_PM))
+ return PCI_D0;
+
+ switch (state) {
+ case 0: return PCI_D0;
+ case 2: return PCI_D2;
+ case 3: return PCI_D3hot;
+ default: BUG();
+ }
+ return PCI_D0;
+}
+
+EXPORT_SYMBOL(pci_choose_state);
+
+/**
* pci_save_state - save the PCI configuration space of a device before suspending
* @dev: - PCI device that we're dealing with
* @buffer: - buffer to hold config space context
@@ -348,7 +383,7 @@ pci_enable_device_bars(struct pci_dev *dev, int bars)
{
int err;
- pci_set_power_state(dev, 0);
+ pci_set_power_state(dev, PCI_D0);
if ((err = pcibios_enable_device(dev, bars)) < 0)
return err;
return 0;
@@ -422,7 +457,7 @@ pci_disable_device(struct pci_dev *dev)
* 0 if operation is successful.
*
*/
-int pci_enable_wake(struct pci_dev *dev, u32 state, int enable)
+int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable)
{
int pm;
u16 value;