aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Brownell <david-b@pacbell.net>2007-04-26 03:12:06 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2007-04-27 13:57:33 -0400
commit075c1771526c85849ed22298d048bc07e400aee5 (patch)
treea1579e93b450b0e870a7a65698f9a07bddbfd899
parent057f6c019fff9ee290641d50647359bb8898918e (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.c3
-rw-r--r--drivers/pci/pci.c58
-rw-r--r--include/linux/pm.h19
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);
29DECLARE_MUTEX(dpm_sem); 29DECLARE_MUTEX(dpm_sem);
30DECLARE_MUTEX(dpm_list_sem); 30DECLARE_MUTEX(dpm_list_sem);
31 31
32int (*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 */
907int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) 913int 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 */
281extern int (*platform_enable_wakeup)(struct device *dev, int is_on);
282
283static 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
278static inline int device_suspend(pm_message_t state) 292static 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
311static 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.