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. |