[RFC][PATCH 1/6] PCI PM: create pm.c and relocate PM functions

From: Adam Belay
Date: Tue Nov 15 2005 - 22:22:20 EST


pci_save_state(), pci_restore_state(), pci_enable_wake(),
pci_choose_state(), and pci_set_power_state() are moved to
drivers/pci/pm.c.

This patch makes several code cleanups but no functional changes.


--- a/drivers/pci/Makefile 2005-10-16 16:55:02.000000000 -0400
+++ b/drivers/pci/Makefile 2005-10-24 06:18:32.000000000 -0400
@@ -2,7 +2,7 @@
# Makefile for the PCI bus specific drivers.
#

-obj-y += access.o bus.o probe.o remove.o pci.o quirks.o \
+obj-y += access.o bus.o probe.o remove.o pci.o pm.o quirks.o \
pci-driver.o search.o pci-sysfs.o rom.o setup-res.o
obj-$(CONFIG_PROC_FS) += proc.o

--- a/drivers/pci/pci.c 2005-10-16 16:55:02.000000000 -0400
+++ b/drivers/pci/pci.c 2005-10-24 06:21:40.000000000 -0400
@@ -10,12 +10,12 @@
*/

#include <linux/kernel.h>
-#include <linux/delay.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <asm/dma.h> /* isa_dma_bridge_buggy */
+
#include "pci.h"


@@ -222,220 +222,6 @@
}

/**
- * pci_restore_bars - restore a devices BAR values (e.g. after wake-up)
- * @dev: PCI device to have its BARs restored
- *
- * Restore the BAR values for a given device, so as to make it
- * accessible by its driver.
- */
-void
-pci_restore_bars(struct pci_dev *dev)
-{
- int i, numres;
-
- switch (dev->hdr_type) {
- case PCI_HEADER_TYPE_NORMAL:
- numres = 6;
- break;
- case PCI_HEADER_TYPE_BRIDGE:
- numres = 2;
- break;
- case PCI_HEADER_TYPE_CARDBUS:
- numres = 1;
- break;
- default:
- /* Should never get here, but just in case... */
- return;
- }
-
- for (i = 0; i < numres; i ++)
- pci_update_resource(dev, &dev->resource[i], i);
-}
-
-/**
- * pci_set_power_state - Set the power state of a PCI device
- * @dev: PCI device to be suspended
- * @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.
- *
- * RETURN VALUE:
- * -EINVAL if trying to enter a lower state than we're already in.
- * 0 if we're already in the requested state.
- * -EIO if device does not support PCI PM.
- * 0 if we can successfully change the power state.
- */
-int (*platform_pci_set_power_state)(struct pci_dev *dev, pci_power_t t);
-int
-pci_set_power_state(struct pci_dev *dev, pci_power_t state)
-{
- int pm, need_restore = 0;
- u16 pmcsr, pmc;
-
- /* bound the state we're entering */
- 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 != PCI_D0 && dev->current_state > state)
- return -EINVAL;
- else if (dev->current_state == state)
- return 0; /* we're already there */
-
- /* find PCI PM capability in list */
- pm = pci_find_capability(dev, PCI_CAP_ID_PM);
-
- /* abort if the device doesn't support PM capabilities */
- if (!pm)
- return -EIO;
-
- pci_read_config_word(dev,pm + PCI_PM_PMC,&pmc);
- if ((pmc & PCI_PM_CAP_VER_MASK) > 3) {
- printk(KERN_DEBUG
- "PCI: %s has unsupported PM cap regs version (%u)\n",
- pci_name(dev), pmc & PCI_PM_CAP_VER_MASK);
- return -EIO;
- }
-
- /* check if this device supports the desired state */
- if (state == PCI_D1 && !(pmc & PCI_PM_CAP_D1))
- return -EIO;
- else if (state == PCI_D2 && !(pmc & PCI_PM_CAP_D2))
- return -EIO;
-
- pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);
-
- /* If we're (effectively) in D3, force entire word to 0.
- * This doesn't affect PME_Status, disables PME_En, and
- * sets PowerState to 0.
- */
- switch (dev->current_state) {
- case PCI_UNKNOWN: /* Boot-up */
- if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot
- && !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET))
- need_restore = 1;
- /* Fall-through: force to D0 */
- case PCI_D3hot:
- case PCI_D3cold:
- case PCI_POWER_ERROR:
- pmcsr = 0;
- break;
- default:
- pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
- pmcsr |= state;
- break;
- }
-
- /* enter specified state */
- pci_write_config_word(dev, pm + PCI_PM_CTRL, pmcsr);
-
- /* Mandatory power management transition delays */
- /* see PCI PM 1.1 5.6.1 table 18 */
- if (state == PCI_D3hot || dev->current_state == PCI_D3hot)
- msleep(10);
- else if (state == PCI_D2 || dev->current_state == PCI_D2)
- udelay(200);
-
- /*
- * Give firmware a chance to be called, such as ACPI _PRx, _PSx
- * Firmware method after natice method ?
- */
- if (platform_pci_set_power_state)
- platform_pci_set_power_state(dev, state);
-
- dev->current_state = state;
-
- /* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT
- * INTERFACE SPECIFICATION, REV. 1.2", a device transitioning
- * from D3hot to D0 _may_ perform an internal reset, thereby
- * going to "D0 Uninitialized" rather than "D0 Initialized".
- * For example, at least some versions of the 3c905B and the
- * 3c556B exhibit this behaviour.
- *
- * At least some laptop BIOSen (e.g. the Thinkpad T21) leave
- * devices in a D3hot state at boot. Consequently, we need to
- * restore at least the BARs so that the device will be
- * accessible to its driver.
- */
- if (need_restore)
- pci_restore_bars(dev);
-
- return 0;
-}
-
-int (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_t 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. This is the value
- * that is passed to suspend() function.
- *
- * Returns PCI power state suitable for given device and given system
- * message.
- */
-
-pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
-{
- int ret;
-
- if (!pci_find_capability(dev, PCI_CAP_ID_PM))
- return PCI_D0;
-
- if (platform_pci_choose_state) {
- ret = platform_pci_choose_state(dev, state);
- if (ret >= 0)
- state.event = ret;
- }
-
- switch (state.event) {
- case PM_EVENT_ON:
- return PCI_D0;
- case PM_EVENT_FREEZE:
- case PM_EVENT_SUSPEND:
- return PCI_D3hot;
- default:
- printk("They asked me for state %d\n", state.event);
- 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
- */
-int
-pci_save_state(struct pci_dev *dev)
-{
- int i;
- /* XXX: 100% dword access ok here? */
- for (i = 0; i < 16; i++)
- pci_read_config_dword(dev, i * 4,&dev->saved_config_space[i]);
- return 0;
-}
-
-/**
- * pci_restore_state - Restore the saved state of a PCI device
- * @dev: - PCI device that we're dealing with
- */
-int
-pci_restore_state(struct pci_dev *dev)
-{
- int i;
-
- for (i = 0; i < 16; i++)
- pci_write_config_dword(dev,i * 4, dev->saved_config_space[i]);
- return 0;
-}
-
-/**
* pci_enable_device_bars - Initialize some of a device for use
* @dev: PCI device to be initialized
* @bars: bitmask of BAR's that must be configured
@@ -512,56 +298,6 @@
dev->is_enabled = 0;
}

-/**
- * 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.
- *
- * -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.
- *
- */
-int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable)
-{
- int pm;
- u16 value;
-
- /* 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;
-
- /* Check device's ability to generate PME# */
- pci_read_config_word(dev,pm+PCI_PM_PMC,&value);
-
- value &= PCI_PM_CAP_PME_MASK;
- 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)))
- return enable ? -EINVAL : 0;
-
- pci_read_config_word(dev, pm + PCI_PM_CTRL, &value);
-
- /* Clear PME_Status by writing 1 to it and enable PME# */
- value |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE;
-
- if (!enable)
- value &= ~PCI_PM_CTRL_PME_ENABLE;
-
- pci_write_config_word(dev, pm + PCI_PM_CTRL, value);
-
- return 0;
-}
-
int
pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge)
{
@@ -893,7 +629,6 @@
EXPORT_SYMBOL(isa_bridge);
#endif

-EXPORT_SYMBOL_GPL(pci_restore_bars);
EXPORT_SYMBOL(pci_enable_device_bars);
EXPORT_SYMBOL(pci_enable_device);
EXPORT_SYMBOL(pci_disable_device);
@@ -914,11 +649,6 @@
EXPORT_SYMBOL(pci_assign_resource);
EXPORT_SYMBOL(pci_find_parent_resource);

-EXPORT_SYMBOL(pci_set_power_state);
-EXPORT_SYMBOL(pci_save_state);
-EXPORT_SYMBOL(pci_restore_state);
-EXPORT_SYMBOL(pci_enable_wake);
-
/* Quirk info */

EXPORT_SYMBOL(isa_dma_bridge_buggy);
--- a/drivers/pci/pm.c 1969-12-31 19:00:00.000000000 -0500
+++ b/drivers/pci/pm.c 2005-10-24 06:23:15.000000000 -0400
@@ -0,0 +1,296 @@
+/*
+ * pm.c - PCI Device Power Management
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/module.h>
+
+#include "pci.h"
+
+
+/*
+ * context saving and restoring
+ */
+
+/**
+ * pci_restore_bars - restore a devices BAR values (e.g. after wake-up)
+ * @dev: PCI device to have its BARs restored
+ *
+ * Restore the BAR values for a given device, so as to make it
+ * accessible by its driver.
+ */
+void pci_restore_bars(struct pci_dev *dev)
+{
+ int i, numres;
+
+ switch (dev->hdr_type) {
+ case PCI_HEADER_TYPE_NORMAL:
+ numres = 6;
+ break;
+ case PCI_HEADER_TYPE_BRIDGE:
+ numres = 2;
+ break;
+ case PCI_HEADER_TYPE_CARDBUS:
+ numres = 1;
+ break;
+ default:
+ /* Should never get here, but just in case... */
+ return;
+ }
+
+ for (i = 0; i < numres; i ++)
+ pci_update_resource(dev, &dev->resource[i], i);
+}
+
+EXPORT_SYMBOL_GPL(pci_restore_bars);
+
+/**
+ * pci_save_state - save the PCI configuration space of a device before suspending
+ * @dev: - PCI device that we're dealing with
+ */
+int pci_save_state(struct pci_dev *dev)
+{
+ int i;
+ /* XXX: 100% dword access ok here? */
+ for (i = 0; i < 16; i++)
+ pci_read_config_dword(dev, i * 4,&dev->saved_config_space[i]);
+ return 0;
+}
+
+EXPORT_SYMBOL(pci_save_state);
+
+/**
+ * pci_restore_state - Restore the saved state of a PCI device
+ * @dev: - PCI device that we're dealing with
+ */
+int pci_restore_state(struct pci_dev *dev)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ pci_write_config_dword(dev,i * 4, dev->saved_config_space[i]);
+ return 0;
+}
+
+EXPORT_SYMBOL(pci_restore_state);
+
+
+/*
+ * device state
+ */
+
+int (*platform_pci_set_power_state)(struct pci_dev *dev, pci_power_t t);
+
+/**
+ * pci_set_power_state - Set the power state of a PCI device
+ * @dev: PCI device to be suspended
+ * @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.
+ *
+ * RETURN VALUE:
+ * -EINVAL if trying to enter a lower state than we're already in.
+ * 0 if we're already in the requested state.
+ * -EIO if device does not support PCI PM.
+ * 0 if we can successfully change the power state.
+ */
+int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
+{
+ int pm, need_restore = 0;
+ u16 pmcsr, pmc;
+
+ /* bound the state we're entering */
+ 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 != PCI_D0 && dev->current_state > state)
+ return -EINVAL;
+ else if (dev->current_state == state)
+ return 0; /* we're already there */
+
+ /* find PCI PM capability in list */
+ pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+
+ /* abort if the device doesn't support PM capabilities */
+ if (!pm)
+ return -EIO;
+
+ pci_read_config_word(dev,pm + PCI_PM_PMC,&pmc);
+ if ((pmc & PCI_PM_CAP_VER_MASK) > 3) {
+ printk(KERN_DEBUG
+ "PCI: %s has unsupported PM cap regs version (%u)\n",
+ pci_name(dev), pmc & PCI_PM_CAP_VER_MASK);
+ return -EIO;
+ }
+
+ /* check if this device supports the desired state */
+ if (state == PCI_D1 && !(pmc & PCI_PM_CAP_D1))
+ return -EIO;
+ else if (state == PCI_D2 && !(pmc & PCI_PM_CAP_D2))
+ return -EIO;
+
+ pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);
+
+ /* If we're (effectively) in D3, force entire word to 0.
+ * This doesn't affect PME_Status, disables PME_En, and
+ * sets PowerState to 0.
+ */
+ switch (dev->current_state) {
+ case PCI_UNKNOWN: /* Boot-up */
+ if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot
+ && !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET))
+ need_restore = 1;
+ /* Fall-through: force to D0 */
+ case PCI_D3hot:
+ case PCI_D3cold:
+ case PCI_POWER_ERROR:
+ pmcsr = 0;
+ break;
+ default:
+ pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
+ pmcsr |= state;
+ break;
+ }
+
+ /* enter specified state */
+ pci_write_config_word(dev, pm + PCI_PM_CTRL, pmcsr);
+
+ /* Mandatory power management transition delays */
+ /* see PCI PM 1.1 5.6.1 table 18 */
+ if (state == PCI_D3hot || dev->current_state == PCI_D3hot)
+ msleep(10);
+ else if (state == PCI_D2 || dev->current_state == PCI_D2)
+ udelay(200);
+
+ /*
+ * Give firmware a chance to be called, such as ACPI _PRx, _PSx
+ * Firmware method after natice method ?
+ */
+ if (platform_pci_set_power_state)
+ platform_pci_set_power_state(dev, state);
+
+ dev->current_state = state;
+
+ /* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT
+ * INTERFACE SPECIFICATION, REV. 1.2", a device transitioning
+ * from D3hot to D0 _may_ perform an internal reset, thereby
+ * going to "D0 Uninitialized" rather than "D0 Initialized".
+ * For example, at least some versions of the 3c905B and the
+ * 3c556B exhibit this behaviour.
+ *
+ * At least some laptop BIOSen (e.g. the Thinkpad T21) leave
+ * devices in a D3hot state at boot. Consequently, we need to
+ * restore at least the BARs so that the device will be
+ * accessible to its driver.
+ */
+ if (need_restore)
+ pci_restore_bars(dev);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(pci_set_power_state);
+
+int (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_t 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. This is the value
+ * that is passed to suspend() function.
+ *
+ * Returns PCI power state suitable for given device and given system
+ * message.
+ */
+pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
+{
+ int ret;
+
+ if (!pci_find_capability(dev, PCI_CAP_ID_PM))
+ return PCI_D0;
+
+ if (platform_pci_choose_state) {
+ ret = platform_pci_choose_state(dev, state);
+ if (ret >= 0)
+ state.event = ret;
+ }
+
+ switch (state.event) {
+ case PM_EVENT_ON:
+ return PCI_D0;
+ case PM_EVENT_FREEZE:
+ case PM_EVENT_SUSPEND:
+ return PCI_D3hot;
+ default:
+ printk("They asked me for state %d\n", state.event);
+ BUG();
+ }
+ return PCI_D0;
+}
+
+EXPORT_SYMBOL(pci_choose_state);
+
+
+/*
+ * wake (PME) support
+ */
+
+/**
+ * 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.
+ *
+ * -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.
+ *
+ */
+int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable)
+{
+ int pm;
+ u16 value;
+
+ /* 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;
+
+ /* Check device's ability to generate PME# */
+ pci_read_config_word(dev,pm+PCI_PM_PMC,&value);
+
+ value &= PCI_PM_CAP_PME_MASK;
+ 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)))
+ return enable ? -EINVAL : 0;
+
+ pci_read_config_word(dev, pm + PCI_PM_CTRL, &value);
+
+ /* Clear PME_Status by writing 1 to it and enable PME# */
+ value |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE;
+
+ if (!enable)
+ value &= ~PCI_PM_CTRL_PME_ENABLE;
+
+ pci_write_config_word(dev, pm + PCI_PM_CTRL, value);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(pci_enable_wake);


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/