diff options
| author | David Brownell <david-b@pacbell.net> | 2007-04-26 03:12:06 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-04-27 13:57:33 -0400 |
| commit | 075c1771526c85849ed22298d048bc07e400aee5 (patch) | |
| tree | a1579e93b450b0e870a7a65698f9a07bddbfd899 | |
| parent | 057f6c019fff9ee290641d50647359bb8898918e (diff) | |
define platform wakeup hook, use in pci_enable_wake()
This defines a platform hook to enable/disable a device as a wakeup event
source. It's initially for use with ACPI, but more generally it could be used
whenever enable_irq_wake()/disable_irq_wake() don't suffice.
The hook is called -- if available -- inside pci_enable_wake(); and the
semantics of that call are enhanced so that support for PCI PME# is no longer
needed. It can now work for devices with "legacy PCI PM", when platform
support allows it. (That support would use some board-specific signal for for
the same purpose as PME#.)
[akpm@linux-foundation.org: Make it compile with CONFIG_PM=n]
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Cc: Len Brown <lenb@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
| -rw-r--r-- | drivers/base/power/main.c | 3 | ||||
| -rw-r--r-- | drivers/pci/pci.c | 58 | ||||
| -rw-r--r-- | include/linux/pm.h | 19 |
3 files changed, 63 insertions, 17 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index bbbb973a9d3c..05dc8764e765 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
| @@ -29,6 +29,9 @@ LIST_HEAD(dpm_off_irq); | |||
| 29 | DECLARE_MUTEX(dpm_sem); | 29 | DECLARE_MUTEX(dpm_sem); |
| 30 | DECLARE_MUTEX(dpm_list_sem); | 30 | DECLARE_MUTEX(dpm_list_sem); |
| 31 | 31 | ||
| 32 | int (*platform_enable_wakeup)(struct device *dev, int is_on); | ||
| 33 | |||
| 34 | |||
| 32 | /** | 35 | /** |
| 33 | * device_pm_set_parent - Specify power dependency. | 36 | * device_pm_set_parent - Specify power dependency. |
| 34 | * @dev: Device who needs power. | 37 | * @dev: Device who needs power. |
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d3eab057b2d3..2a458279327a 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <linux/delay.h> | 13 | #include <linux/delay.h> |
| 14 | #include <linux/init.h> | 14 | #include <linux/init.h> |
| 15 | #include <linux/pci.h> | 15 | #include <linux/pci.h> |
| 16 | #include <linux/pm.h> | ||
| 16 | #include <linux/module.h> | 17 | #include <linux/module.h> |
| 17 | #include <linux/spinlock.h> | 18 | #include <linux/spinlock.h> |
| 18 | #include <linux/string.h> | 19 | #include <linux/string.h> |
| @@ -891,31 +892,48 @@ pci_disable_device(struct pci_dev *dev) | |||
| 891 | } | 892 | } |
| 892 | 893 | ||
| 893 | /** | 894 | /** |
| 894 | * pci_enable_wake - enable device to generate PME# when suspended | 895 | * pci_enable_wake - enable PCI device as wakeup event source |
| 895 | * @dev: - PCI device to operate on | 896 | * @dev: PCI device affected |
| 896 | * @state: - Current state of device. | 897 | * @state: PCI state from which device will issue wakeup events |
| 897 | * @enable: - Flag to enable or disable generation | 898 | * @enable: True to enable event generation; false to disable |
| 898 | * | ||
| 899 | * Set the bits in the device's PM Capabilities to generate PME# when | ||
| 900 | * the system is suspended. | ||
| 901 | * | 899 | * |
| 902 | * -EIO is returned if device doesn't have PM Capabilities. | 900 | * This enables the device as a wakeup event source, or disables it. |
| 903 | * -EINVAL is returned if device supports it, but can't generate wake events. | 901 | * When such events involves platform-specific hooks, those hooks are |
| 904 | * 0 if operation is successful. | 902 | * called automatically by this routine. |
| 905 | * | 903 | * |
| 904 | * Devices with legacy power management (no standard PCI PM capabilities) | ||
| 905 | * always require such platform hooks. Depending on the platform, devices | ||
| 906 | * supporting the standard PCI PME# signal may require such platform hooks; | ||
| 907 | * they always update bits in config space to allow PME# generation. | ||
| 908 | * | ||
| 909 | * -EIO is returned if the device can't ever be a wakeup event source. | ||
| 910 | * -EINVAL is returned if the device can't generate wakeup events from | ||
| 911 | * the specified PCI state. Returns zero if the operation is successful. | ||
| 906 | */ | 912 | */ |
| 907 | int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) | 913 | int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) |
| 908 | { | 914 | { |
| 909 | int pm; | 915 | int pm; |
| 916 | int status; | ||
| 910 | u16 value; | 917 | u16 value; |
| 911 | 918 | ||
| 919 | /* Note that drivers should verify device_may_wakeup(&dev->dev) | ||
| 920 | * before calling this function. Platform code should report | ||
| 921 | * errors when drivers try to enable wakeup on devices that | ||
| 922 | * can't issue wakeups, or on which wakeups were disabled by | ||
| 923 | * userspace updating the /sys/devices.../power/wakeup file. | ||
| 924 | */ | ||
| 925 | |||
| 926 | status = call_platform_enable_wakeup(&dev->dev, enable); | ||
| 927 | |||
| 912 | /* find PCI PM capability in list */ | 928 | /* find PCI PM capability in list */ |
| 913 | pm = pci_find_capability(dev, PCI_CAP_ID_PM); | 929 | pm = pci_find_capability(dev, PCI_CAP_ID_PM); |
| 914 | 930 | ||
| 915 | /* If device doesn't support PM Capabilities, but request is to disable | 931 | /* If device doesn't support PM Capabilities, but caller wants to |
| 916 | * wake events, it's a nop; otherwise fail */ | 932 | * disable wake events, it's a NOP. Otherwise fail unless the |
| 917 | if (!pm) | 933 | * platform hooks handled this legacy device already. |
| 918 | return enable ? -EIO : 0; | 934 | */ |
| 935 | if (!pm) | ||
| 936 | return enable ? status : 0; | ||
| 919 | 937 | ||
| 920 | /* Check device's ability to generate PME# */ | 938 | /* Check device's ability to generate PME# */ |
| 921 | pci_read_config_word(dev,pm+PCI_PM_PMC,&value); | 939 | pci_read_config_word(dev,pm+PCI_PM_PMC,&value); |
| @@ -924,8 +942,14 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) | |||
| 924 | value >>= ffs(PCI_PM_CAP_PME_MASK) - 1; /* First bit of mask */ | 942 | value >>= ffs(PCI_PM_CAP_PME_MASK) - 1; /* First bit of mask */ |
| 925 | 943 | ||
| 926 | /* Check if it can generate PME# from requested state. */ | 944 | /* Check if it can generate PME# from requested state. */ |
| 927 | if (!value || !(value & (1 << state))) | 945 | if (!value || !(value & (1 << state))) { |
| 946 | /* if it can't, revert what the platform hook changed, | ||
| 947 | * always reporting the base "EINVAL, can't PME#" error | ||
| 948 | */ | ||
| 949 | if (enable) | ||
| 950 | call_platform_enable_wakeup(&dev->dev, 0); | ||
| 928 | return enable ? -EINVAL : 0; | 951 | return enable ? -EINVAL : 0; |
| 952 | } | ||
| 929 | 953 | ||
| 930 | pci_read_config_word(dev, pm + PCI_PM_CTRL, &value); | 954 | pci_read_config_word(dev, pm + PCI_PM_CTRL, &value); |
| 931 | 955 | ||
| @@ -936,7 +960,7 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) | |||
| 936 | value &= ~PCI_PM_CTRL_PME_ENABLE; | 960 | value &= ~PCI_PM_CTRL_PME_ENABLE; |
| 937 | 961 | ||
| 938 | pci_write_config_word(dev, pm + PCI_PM_CTRL, value); | 962 | pci_write_config_word(dev, pm + PCI_PM_CTRL, value); |
| 939 | 963 | ||
| 940 | return 0; | 964 | return 0; |
| 941 | } | 965 | } |
| 942 | 966 | ||
diff --git a/include/linux/pm.h b/include/linux/pm.h index 21db05ac7c0b..b0ab623adbf5 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h | |||
| @@ -273,6 +273,20 @@ extern void __suspend_report_result(const char *function, void *fn, int ret); | |||
| 273 | __suspend_report_result(__FUNCTION__, fn, ret); \ | 273 | __suspend_report_result(__FUNCTION__, fn, ret); \ |
| 274 | } while (0) | 274 | } while (0) |
| 275 | 275 | ||
| 276 | /* | ||
| 277 | * Platform hook to activate device wakeup capability, if that's not already | ||
| 278 | * handled by enable_irq_wake() etc. | ||
| 279 | * Returns zero on success, else negative errno | ||
| 280 | */ | ||
| 281 | extern int (*platform_enable_wakeup)(struct device *dev, int is_on); | ||
| 282 | |||
| 283 | static inline int call_platform_enable_wakeup(struct device *dev, int is_on) | ||
| 284 | { | ||
| 285 | if (platform_enable_wakeup) | ||
| 286 | return (*platform_enable_wakeup)(dev, is_on); | ||
| 287 | return 0; | ||
| 288 | } | ||
| 289 | |||
| 276 | #else /* !CONFIG_PM */ | 290 | #else /* !CONFIG_PM */ |
| 277 | 291 | ||
| 278 | static inline int device_suspend(pm_message_t state) | 292 | static inline int device_suspend(pm_message_t state) |
| @@ -294,6 +308,11 @@ static inline void dpm_runtime_resume(struct device * dev) | |||
| 294 | 308 | ||
| 295 | #define suspend_report_result(fn, ret) do { } while (0) | 309 | #define suspend_report_result(fn, ret) do { } while (0) |
| 296 | 310 | ||
| 311 | static inline int call_platform_enable_wakeup(struct device *dev, int is_on) | ||
| 312 | { | ||
| 313 | return -EIO; | ||
| 314 | } | ||
| 315 | |||
| 297 | #endif | 316 | #endif |
| 298 | 317 | ||
| 299 | /* changes to device_may_wakeup take effect on the next pm state change. | 318 | /* changes to device_may_wakeup take effect on the next pm state change. |
