diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-04 14:14:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-04 14:14:36 -0400 |
commit | f46e9913faeebcb6bd29edf795f12b60acbff171 (patch) | |
tree | 1ed8871d0ebd638094d27317de1d8a53712ae15a /drivers | |
parent | 8d91530c5fd7f0b1e8c4ddfea2905e55a178569b (diff) | |
parent | 8d4b9d1bfef117862a2889dec4dac227068544c9 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6:
PM / Runtime: Add runtime PM statistics (v3)
PM / Runtime: Make runtime_status attribute not debug-only (v. 2)
PM: Do not use dynamically allocated objects in pm_wakeup_event()
PM / Suspend: Fix ordering of calls in suspend error paths
PM / Hibernate: Fix snapshot error code path
PM / Hibernate: Fix hibernation_platform_enter()
pm_qos: Get rid of the allocation in pm_qos_add_request()
pm_qos: Reimplement using plists
plist: Add plist_last
PM: Make it possible to avoid races between wakeup and system sleep
PNPACPI: Add support for remote wakeup
PM: describe kernel policy regarding wakeup defaults (v. 2)
PM / Hibernate: Fix typos in comments in kernel/power/swap.c
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/base/power/Makefile | 2 | ||||
-rw-r--r-- | drivers/base/power/main.c | 1 | ||||
-rw-r--r-- | drivers/base/power/runtime.c | 54 | ||||
-rw-r--r-- | drivers/base/power/sysfs.c | 98 | ||||
-rw-r--r-- | drivers/base/power/wakeup.c | 247 | ||||
-rw-r--r-- | drivers/net/e1000e/netdev.c | 17 | ||||
-rw-r--r-- | drivers/net/igbvf/netdev.c | 9 | ||||
-rw-r--r-- | drivers/net/wireless/ipw2x00/ipw2100.c | 12 | ||||
-rw-r--r-- | drivers/pci/pci-acpi.c | 1 | ||||
-rw-r--r-- | drivers/pci/pci.c | 20 | ||||
-rw-r--r-- | drivers/pci/pci.h | 1 | ||||
-rw-r--r-- | drivers/pci/pcie/pme/pcie_pme.c | 5 | ||||
-rw-r--r-- | drivers/pnp/core.c | 3 | ||||
-rw-r--r-- | drivers/pnp/pnpacpi/core.c | 23 |
14 files changed, 442 insertions, 51 deletions
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index 89de75325cea..cbccf9a3cee4 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile | |||
@@ -1,5 +1,5 @@ | |||
1 | obj-$(CONFIG_PM) += sysfs.o | 1 | obj-$(CONFIG_PM) += sysfs.o |
2 | obj-$(CONFIG_PM_SLEEP) += main.o | 2 | obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o |
3 | obj-$(CONFIG_PM_RUNTIME) += runtime.o | 3 | obj-$(CONFIG_PM_RUNTIME) += runtime.o |
4 | obj-$(CONFIG_PM_OPS) += generic_ops.o | 4 | obj-$(CONFIG_PM_OPS) += generic_ops.o |
5 | obj-$(CONFIG_PM_TRACE_RTC) += trace.o | 5 | obj-$(CONFIG_PM_TRACE_RTC) += trace.o |
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 941fcb87e52a..5419a49ff135 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
@@ -59,6 +59,7 @@ void device_pm_init(struct device *dev) | |||
59 | { | 59 | { |
60 | dev->power.status = DPM_ON; | 60 | dev->power.status = DPM_ON; |
61 | init_completion(&dev->power.completion); | 61 | init_completion(&dev->power.completion); |
62 | dev->power.wakeup_count = 0; | ||
62 | pm_runtime_init(dev); | 63 | pm_runtime_init(dev); |
63 | } | 64 | } |
64 | 65 | ||
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index b0ec0e9f27e9..b78c401ffa73 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c | |||
@@ -123,6 +123,45 @@ int pm_runtime_idle(struct device *dev) | |||
123 | } | 123 | } |
124 | EXPORT_SYMBOL_GPL(pm_runtime_idle); | 124 | EXPORT_SYMBOL_GPL(pm_runtime_idle); |
125 | 125 | ||
126 | |||
127 | /** | ||
128 | * update_pm_runtime_accounting - Update the time accounting of power states | ||
129 | * @dev: Device to update the accounting for | ||
130 | * | ||
131 | * In order to be able to have time accounting of the various power states | ||
132 | * (as used by programs such as PowerTOP to show the effectiveness of runtime | ||
133 | * PM), we need to track the time spent in each state. | ||
134 | * update_pm_runtime_accounting must be called each time before the | ||
135 | * runtime_status field is updated, to account the time in the old state | ||
136 | * correctly. | ||
137 | */ | ||
138 | void update_pm_runtime_accounting(struct device *dev) | ||
139 | { | ||
140 | unsigned long now = jiffies; | ||
141 | int delta; | ||
142 | |||
143 | delta = now - dev->power.accounting_timestamp; | ||
144 | |||
145 | if (delta < 0) | ||
146 | delta = 0; | ||
147 | |||
148 | dev->power.accounting_timestamp = now; | ||
149 | |||
150 | if (dev->power.disable_depth > 0) | ||
151 | return; | ||
152 | |||
153 | if (dev->power.runtime_status == RPM_SUSPENDED) | ||
154 | dev->power.suspended_jiffies += delta; | ||
155 | else | ||
156 | dev->power.active_jiffies += delta; | ||
157 | } | ||
158 | |||
159 | static void __update_runtime_status(struct device *dev, enum rpm_status status) | ||
160 | { | ||
161 | update_pm_runtime_accounting(dev); | ||
162 | dev->power.runtime_status = status; | ||
163 | } | ||
164 | |||
126 | /** | 165 | /** |
127 | * __pm_runtime_suspend - Carry out run-time suspend of given device. | 166 | * __pm_runtime_suspend - Carry out run-time suspend of given device. |
128 | * @dev: Device to suspend. | 167 | * @dev: Device to suspend. |
@@ -197,7 +236,7 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq) | |||
197 | goto repeat; | 236 | goto repeat; |
198 | } | 237 | } |
199 | 238 | ||
200 | dev->power.runtime_status = RPM_SUSPENDING; | 239 | __update_runtime_status(dev, RPM_SUSPENDING); |
201 | dev->power.deferred_resume = false; | 240 | dev->power.deferred_resume = false; |
202 | 241 | ||
203 | if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) { | 242 | if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) { |
@@ -228,7 +267,7 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq) | |||
228 | } | 267 | } |
229 | 268 | ||
230 | if (retval) { | 269 | if (retval) { |
231 | dev->power.runtime_status = RPM_ACTIVE; | 270 | __update_runtime_status(dev, RPM_ACTIVE); |
232 | if (retval == -EAGAIN || retval == -EBUSY) { | 271 | if (retval == -EAGAIN || retval == -EBUSY) { |
233 | if (dev->power.timer_expires == 0) | 272 | if (dev->power.timer_expires == 0) |
234 | notify = true; | 273 | notify = true; |
@@ -237,7 +276,7 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq) | |||
237 | pm_runtime_cancel_pending(dev); | 276 | pm_runtime_cancel_pending(dev); |
238 | } | 277 | } |
239 | } else { | 278 | } else { |
240 | dev->power.runtime_status = RPM_SUSPENDED; | 279 | __update_runtime_status(dev, RPM_SUSPENDED); |
241 | pm_runtime_deactivate_timer(dev); | 280 | pm_runtime_deactivate_timer(dev); |
242 | 281 | ||
243 | if (dev->parent) { | 282 | if (dev->parent) { |
@@ -381,7 +420,7 @@ int __pm_runtime_resume(struct device *dev, bool from_wq) | |||
381 | goto repeat; | 420 | goto repeat; |
382 | } | 421 | } |
383 | 422 | ||
384 | dev->power.runtime_status = RPM_RESUMING; | 423 | __update_runtime_status(dev, RPM_RESUMING); |
385 | 424 | ||
386 | if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) { | 425 | if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) { |
387 | spin_unlock_irq(&dev->power.lock); | 426 | spin_unlock_irq(&dev->power.lock); |
@@ -411,10 +450,10 @@ int __pm_runtime_resume(struct device *dev, bool from_wq) | |||
411 | } | 450 | } |
412 | 451 | ||
413 | if (retval) { | 452 | if (retval) { |
414 | dev->power.runtime_status = RPM_SUSPENDED; | 453 | __update_runtime_status(dev, RPM_SUSPENDED); |
415 | pm_runtime_cancel_pending(dev); | 454 | pm_runtime_cancel_pending(dev); |
416 | } else { | 455 | } else { |
417 | dev->power.runtime_status = RPM_ACTIVE; | 456 | __update_runtime_status(dev, RPM_ACTIVE); |
418 | if (parent) | 457 | if (parent) |
419 | atomic_inc(&parent->power.child_count); | 458 | atomic_inc(&parent->power.child_count); |
420 | } | 459 | } |
@@ -848,7 +887,7 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status) | |||
848 | } | 887 | } |
849 | 888 | ||
850 | out_set: | 889 | out_set: |
851 | dev->power.runtime_status = status; | 890 | __update_runtime_status(dev, status); |
852 | dev->power.runtime_error = 0; | 891 | dev->power.runtime_error = 0; |
853 | out: | 892 | out: |
854 | spin_unlock_irqrestore(&dev->power.lock, flags); | 893 | spin_unlock_irqrestore(&dev->power.lock, flags); |
@@ -1077,6 +1116,7 @@ void pm_runtime_init(struct device *dev) | |||
1077 | dev->power.request_pending = false; | 1116 | dev->power.request_pending = false; |
1078 | dev->power.request = RPM_REQ_NONE; | 1117 | dev->power.request = RPM_REQ_NONE; |
1079 | dev->power.deferred_resume = false; | 1118 | dev->power.deferred_resume = false; |
1119 | dev->power.accounting_timestamp = jiffies; | ||
1080 | INIT_WORK(&dev->power.work, pm_runtime_work); | 1120 | INIT_WORK(&dev->power.work, pm_runtime_work); |
1081 | 1121 | ||
1082 | dev->power.timer_expires = 0; | 1122 | dev->power.timer_expires = 0; |
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index a4c33bc51257..e56b4388fe61 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include <linux/string.h> | 6 | #include <linux/string.h> |
7 | #include <linux/pm_runtime.h> | 7 | #include <linux/pm_runtime.h> |
8 | #include <asm/atomic.h> | 8 | #include <asm/atomic.h> |
9 | #include <linux/jiffies.h> | ||
9 | #include "power.h" | 10 | #include "power.h" |
10 | 11 | ||
11 | /* | 12 | /* |
@@ -73,6 +74,8 @@ | |||
73 | * device are known to the PM core. However, for some devices this | 74 | * device are known to the PM core. However, for some devices this |
74 | * attribute is set to "enabled" by bus type code or device drivers and in | 75 | * attribute is set to "enabled" by bus type code or device drivers and in |
75 | * that cases it should be safe to leave the default value. | 76 | * that cases it should be safe to leave the default value. |
77 | * | ||
78 | * wakeup_count - Report the number of wakeup events related to the device | ||
76 | */ | 79 | */ |
77 | 80 | ||
78 | static const char enabled[] = "enabled"; | 81 | static const char enabled[] = "enabled"; |
@@ -108,6 +111,65 @@ static ssize_t control_store(struct device * dev, struct device_attribute *attr, | |||
108 | } | 111 | } |
109 | 112 | ||
110 | static DEVICE_ATTR(control, 0644, control_show, control_store); | 113 | static DEVICE_ATTR(control, 0644, control_show, control_store); |
114 | |||
115 | static ssize_t rtpm_active_time_show(struct device *dev, | ||
116 | struct device_attribute *attr, char *buf) | ||
117 | { | ||
118 | int ret; | ||
119 | spin_lock_irq(&dev->power.lock); | ||
120 | update_pm_runtime_accounting(dev); | ||
121 | ret = sprintf(buf, "%i\n", jiffies_to_msecs(dev->power.active_jiffies)); | ||
122 | spin_unlock_irq(&dev->power.lock); | ||
123 | return ret; | ||
124 | } | ||
125 | |||
126 | static DEVICE_ATTR(runtime_active_time, 0444, rtpm_active_time_show, NULL); | ||
127 | |||
128 | static ssize_t rtpm_suspended_time_show(struct device *dev, | ||
129 | struct device_attribute *attr, char *buf) | ||
130 | { | ||
131 | int ret; | ||
132 | spin_lock_irq(&dev->power.lock); | ||
133 | update_pm_runtime_accounting(dev); | ||
134 | ret = sprintf(buf, "%i\n", | ||
135 | jiffies_to_msecs(dev->power.suspended_jiffies)); | ||
136 | spin_unlock_irq(&dev->power.lock); | ||
137 | return ret; | ||
138 | } | ||
139 | |||
140 | static DEVICE_ATTR(runtime_suspended_time, 0444, rtpm_suspended_time_show, NULL); | ||
141 | |||
142 | static ssize_t rtpm_status_show(struct device *dev, | ||
143 | struct device_attribute *attr, char *buf) | ||
144 | { | ||
145 | const char *p; | ||
146 | |||
147 | if (dev->power.runtime_error) { | ||
148 | p = "error\n"; | ||
149 | } else if (dev->power.disable_depth) { | ||
150 | p = "unsupported\n"; | ||
151 | } else { | ||
152 | switch (dev->power.runtime_status) { | ||
153 | case RPM_SUSPENDED: | ||
154 | p = "suspended\n"; | ||
155 | break; | ||
156 | case RPM_SUSPENDING: | ||
157 | p = "suspending\n"; | ||
158 | break; | ||
159 | case RPM_RESUMING: | ||
160 | p = "resuming\n"; | ||
161 | break; | ||
162 | case RPM_ACTIVE: | ||
163 | p = "active\n"; | ||
164 | break; | ||
165 | default: | ||
166 | return -EIO; | ||
167 | } | ||
168 | } | ||
169 | return sprintf(buf, p); | ||
170 | } | ||
171 | |||
172 | static DEVICE_ATTR(runtime_status, 0444, rtpm_status_show, NULL); | ||
111 | #endif | 173 | #endif |
112 | 174 | ||
113 | static ssize_t | 175 | static ssize_t |
@@ -144,6 +206,16 @@ wake_store(struct device * dev, struct device_attribute *attr, | |||
144 | 206 | ||
145 | static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); | 207 | static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); |
146 | 208 | ||
209 | #ifdef CONFIG_PM_SLEEP | ||
210 | static ssize_t wakeup_count_show(struct device *dev, | ||
211 | struct device_attribute *attr, char *buf) | ||
212 | { | ||
213 | return sprintf(buf, "%lu\n", dev->power.wakeup_count); | ||
214 | } | ||
215 | |||
216 | static DEVICE_ATTR(wakeup_count, 0444, wakeup_count_show, NULL); | ||
217 | #endif | ||
218 | |||
147 | #ifdef CONFIG_PM_ADVANCED_DEBUG | 219 | #ifdef CONFIG_PM_ADVANCED_DEBUG |
148 | #ifdef CONFIG_PM_RUNTIME | 220 | #ifdef CONFIG_PM_RUNTIME |
149 | 221 | ||
@@ -172,27 +244,8 @@ static ssize_t rtpm_enabled_show(struct device *dev, | |||
172 | return sprintf(buf, "enabled\n"); | 244 | return sprintf(buf, "enabled\n"); |
173 | } | 245 | } |
174 | 246 | ||
175 | static ssize_t rtpm_status_show(struct device *dev, | ||
176 | struct device_attribute *attr, char *buf) | ||
177 | { | ||
178 | if (dev->power.runtime_error) | ||
179 | return sprintf(buf, "error\n"); | ||
180 | switch (dev->power.runtime_status) { | ||
181 | case RPM_SUSPENDED: | ||
182 | return sprintf(buf, "suspended\n"); | ||
183 | case RPM_SUSPENDING: | ||
184 | return sprintf(buf, "suspending\n"); | ||
185 | case RPM_RESUMING: | ||
186 | return sprintf(buf, "resuming\n"); | ||
187 | case RPM_ACTIVE: | ||
188 | return sprintf(buf, "active\n"); | ||
189 | } | ||
190 | return -EIO; | ||
191 | } | ||
192 | |||
193 | static DEVICE_ATTR(runtime_usage, 0444, rtpm_usagecount_show, NULL); | 247 | static DEVICE_ATTR(runtime_usage, 0444, rtpm_usagecount_show, NULL); |
194 | static DEVICE_ATTR(runtime_active_kids, 0444, rtpm_children_show, NULL); | 248 | static DEVICE_ATTR(runtime_active_kids, 0444, rtpm_children_show, NULL); |
195 | static DEVICE_ATTR(runtime_status, 0444, rtpm_status_show, NULL); | ||
196 | static DEVICE_ATTR(runtime_enabled, 0444, rtpm_enabled_show, NULL); | 249 | static DEVICE_ATTR(runtime_enabled, 0444, rtpm_enabled_show, NULL); |
197 | 250 | ||
198 | #endif | 251 | #endif |
@@ -228,14 +281,19 @@ static DEVICE_ATTR(async, 0644, async_show, async_store); | |||
228 | static struct attribute * power_attrs[] = { | 281 | static struct attribute * power_attrs[] = { |
229 | #ifdef CONFIG_PM_RUNTIME | 282 | #ifdef CONFIG_PM_RUNTIME |
230 | &dev_attr_control.attr, | 283 | &dev_attr_control.attr, |
284 | &dev_attr_runtime_status.attr, | ||
285 | &dev_attr_runtime_suspended_time.attr, | ||
286 | &dev_attr_runtime_active_time.attr, | ||
231 | #endif | 287 | #endif |
232 | &dev_attr_wakeup.attr, | 288 | &dev_attr_wakeup.attr, |
289 | #ifdef CONFIG_PM_SLEEP | ||
290 | &dev_attr_wakeup_count.attr, | ||
291 | #endif | ||
233 | #ifdef CONFIG_PM_ADVANCED_DEBUG | 292 | #ifdef CONFIG_PM_ADVANCED_DEBUG |
234 | &dev_attr_async.attr, | 293 | &dev_attr_async.attr, |
235 | #ifdef CONFIG_PM_RUNTIME | 294 | #ifdef CONFIG_PM_RUNTIME |
236 | &dev_attr_runtime_usage.attr, | 295 | &dev_attr_runtime_usage.attr, |
237 | &dev_attr_runtime_active_kids.attr, | 296 | &dev_attr_runtime_active_kids.attr, |
238 | &dev_attr_runtime_status.attr, | ||
239 | &dev_attr_runtime_enabled.attr, | 297 | &dev_attr_runtime_enabled.attr, |
240 | #endif | 298 | #endif |
241 | #endif | 299 | #endif |
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c new file mode 100644 index 000000000000..eb594facfc3f --- /dev/null +++ b/drivers/base/power/wakeup.c | |||
@@ -0,0 +1,247 @@ | |||
1 | /* | ||
2 | * drivers/base/power/wakeup.c - System wakeup events framework | ||
3 | * | ||
4 | * Copyright (c) 2010 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. | ||
5 | * | ||
6 | * This file is released under the GPLv2. | ||
7 | */ | ||
8 | |||
9 | #include <linux/device.h> | ||
10 | #include <linux/slab.h> | ||
11 | #include <linux/sched.h> | ||
12 | #include <linux/capability.h> | ||
13 | #include <linux/suspend.h> | ||
14 | #include <linux/pm.h> | ||
15 | |||
16 | /* | ||
17 | * If set, the suspend/hibernate code will abort transitions to a sleep state | ||
18 | * if wakeup events are registered during or immediately before the transition. | ||
19 | */ | ||
20 | bool events_check_enabled; | ||
21 | |||
22 | /* The counter of registered wakeup events. */ | ||
23 | static unsigned long event_count; | ||
24 | /* A preserved old value of event_count. */ | ||
25 | static unsigned long saved_event_count; | ||
26 | /* The counter of wakeup events being processed. */ | ||
27 | static unsigned long events_in_progress; | ||
28 | |||
29 | static DEFINE_SPINLOCK(events_lock); | ||
30 | |||
31 | static void pm_wakeup_timer_fn(unsigned long data); | ||
32 | |||
33 | static DEFINE_TIMER(events_timer, pm_wakeup_timer_fn, 0, 0); | ||
34 | static unsigned long events_timer_expires; | ||
35 | |||
36 | /* | ||
37 | * The functions below use the observation that each wakeup event starts a | ||
38 | * period in which the system should not be suspended. The moment this period | ||
39 | * will end depends on how the wakeup event is going to be processed after being | ||
40 | * detected and all of the possible cases can be divided into two distinct | ||
41 | * groups. | ||
42 | * | ||
43 | * First, a wakeup event may be detected by the same functional unit that will | ||
44 | * carry out the entire processing of it and possibly will pass it to user space | ||
45 | * for further processing. In that case the functional unit that has detected | ||
46 | * the event may later "close" the "no suspend" period associated with it | ||
47 | * directly as soon as it has been dealt with. The pair of pm_stay_awake() and | ||
48 | * pm_relax(), balanced with each other, is supposed to be used in such | ||
49 | * situations. | ||
50 | * | ||
51 | * Second, a wakeup event may be detected by one functional unit and processed | ||
52 | * by another one. In that case the unit that has detected it cannot really | ||
53 | * "close" the "no suspend" period associated with it, unless it knows in | ||
54 | * advance what's going to happen to the event during processing. This | ||
55 | * knowledge, however, may not be available to it, so it can simply specify time | ||
56 | * to wait before the system can be suspended and pass it as the second | ||
57 | * argument of pm_wakeup_event(). | ||
58 | */ | ||
59 | |||
60 | /** | ||
61 | * pm_stay_awake - Notify the PM core that a wakeup event is being processed. | ||
62 | * @dev: Device the wakeup event is related to. | ||
63 | * | ||
64 | * Notify the PM core of a wakeup event (signaled by @dev) by incrementing the | ||
65 | * counter of wakeup events being processed. If @dev is not NULL, the counter | ||
66 | * of wakeup events related to @dev is incremented too. | ||
67 | * | ||
68 | * Call this function after detecting of a wakeup event if pm_relax() is going | ||
69 | * to be called directly after processing the event (and possibly passing it to | ||
70 | * user space for further processing). | ||
71 | * | ||
72 | * It is safe to call this function from interrupt context. | ||
73 | */ | ||
74 | void pm_stay_awake(struct device *dev) | ||
75 | { | ||
76 | unsigned long flags; | ||
77 | |||
78 | spin_lock_irqsave(&events_lock, flags); | ||
79 | if (dev) | ||
80 | dev->power.wakeup_count++; | ||
81 | |||
82 | events_in_progress++; | ||
83 | spin_unlock_irqrestore(&events_lock, flags); | ||
84 | } | ||
85 | |||
86 | /** | ||
87 | * pm_relax - Notify the PM core that processing of a wakeup event has ended. | ||
88 | * | ||
89 | * Notify the PM core that a wakeup event has been processed by decrementing | ||
90 | * the counter of wakeup events being processed and incrementing the counter | ||
91 | * of registered wakeup events. | ||
92 | * | ||
93 | * Call this function for wakeup events whose processing started with calling | ||
94 | * pm_stay_awake(). | ||
95 | * | ||
96 | * It is safe to call it from interrupt context. | ||
97 | */ | ||
98 | void pm_relax(void) | ||
99 | { | ||
100 | unsigned long flags; | ||
101 | |||
102 | spin_lock_irqsave(&events_lock, flags); | ||
103 | if (events_in_progress) { | ||
104 | events_in_progress--; | ||
105 | event_count++; | ||
106 | } | ||
107 | spin_unlock_irqrestore(&events_lock, flags); | ||
108 | } | ||
109 | |||
110 | /** | ||
111 | * pm_wakeup_timer_fn - Delayed finalization of a wakeup event. | ||
112 | * | ||
113 | * Decrease the counter of wakeup events being processed after it was increased | ||
114 | * by pm_wakeup_event(). | ||
115 | */ | ||
116 | static void pm_wakeup_timer_fn(unsigned long data) | ||
117 | { | ||
118 | unsigned long flags; | ||
119 | |||
120 | spin_lock_irqsave(&events_lock, flags); | ||
121 | if (events_timer_expires | ||
122 | && time_before_eq(events_timer_expires, jiffies)) { | ||
123 | events_in_progress--; | ||
124 | events_timer_expires = 0; | ||
125 | } | ||
126 | spin_unlock_irqrestore(&events_lock, flags); | ||
127 | } | ||
128 | |||
129 | /** | ||
130 | * pm_wakeup_event - Notify the PM core of a wakeup event. | ||
131 | * @dev: Device the wakeup event is related to. | ||
132 | * @msec: Anticipated event processing time (in milliseconds). | ||
133 | * | ||
134 | * Notify the PM core of a wakeup event (signaled by @dev) that will take | ||
135 | * approximately @msec milliseconds to be processed by the kernel. Increment | ||
136 | * the counter of registered wakeup events and (if @msec is nonzero) set up | ||
137 | * the wakeup events timer to execute pm_wakeup_timer_fn() in future (if the | ||
138 | * timer has not been set up already, increment the counter of wakeup events | ||
139 | * being processed). If @dev is not NULL, the counter of wakeup events related | ||
140 | * to @dev is incremented too. | ||
141 | * | ||
142 | * It is safe to call this function from interrupt context. | ||
143 | */ | ||
144 | void pm_wakeup_event(struct device *dev, unsigned int msec) | ||
145 | { | ||
146 | unsigned long flags; | ||
147 | |||
148 | spin_lock_irqsave(&events_lock, flags); | ||
149 | event_count++; | ||
150 | if (dev) | ||
151 | dev->power.wakeup_count++; | ||
152 | |||
153 | if (msec) { | ||
154 | unsigned long expires; | ||
155 | |||
156 | expires = jiffies + msecs_to_jiffies(msec); | ||
157 | if (!expires) | ||
158 | expires = 1; | ||
159 | |||
160 | if (!events_timer_expires | ||
161 | || time_after(expires, events_timer_expires)) { | ||
162 | if (!events_timer_expires) | ||
163 | events_in_progress++; | ||
164 | |||
165 | mod_timer(&events_timer, expires); | ||
166 | events_timer_expires = expires; | ||
167 | } | ||
168 | } | ||
169 | spin_unlock_irqrestore(&events_lock, flags); | ||
170 | } | ||
171 | |||
172 | /** | ||
173 | * pm_check_wakeup_events - Check for new wakeup events. | ||
174 | * | ||
175 | * Compare the current number of registered wakeup events with its preserved | ||
176 | * value from the past to check if new wakeup events have been registered since | ||
177 | * the old value was stored. Check if the current number of wakeup events being | ||
178 | * processed is zero. | ||
179 | */ | ||
180 | bool pm_check_wakeup_events(void) | ||
181 | { | ||
182 | unsigned long flags; | ||
183 | bool ret = true; | ||
184 | |||
185 | spin_lock_irqsave(&events_lock, flags); | ||
186 | if (events_check_enabled) { | ||
187 | ret = (event_count == saved_event_count) && !events_in_progress; | ||
188 | events_check_enabled = ret; | ||
189 | } | ||
190 | spin_unlock_irqrestore(&events_lock, flags); | ||
191 | return ret; | ||
192 | } | ||
193 | |||
194 | /** | ||
195 | * pm_get_wakeup_count - Read the number of registered wakeup events. | ||
196 | * @count: Address to store the value at. | ||
197 | * | ||
198 | * Store the number of registered wakeup events at the address in @count. Block | ||
199 | * if the current number of wakeup events being processed is nonzero. | ||
200 | * | ||
201 | * Return false if the wait for the number of wakeup events being processed to | ||
202 | * drop down to zero has been interrupted by a signal (and the current number | ||
203 | * of wakeup events being processed is still nonzero). Otherwise return true. | ||
204 | */ | ||
205 | bool pm_get_wakeup_count(unsigned long *count) | ||
206 | { | ||
207 | bool ret; | ||
208 | |||
209 | spin_lock_irq(&events_lock); | ||
210 | if (capable(CAP_SYS_ADMIN)) | ||
211 | events_check_enabled = false; | ||
212 | |||
213 | while (events_in_progress && !signal_pending(current)) { | ||
214 | spin_unlock_irq(&events_lock); | ||
215 | |||
216 | schedule_timeout_interruptible(msecs_to_jiffies(100)); | ||
217 | |||
218 | spin_lock_irq(&events_lock); | ||
219 | } | ||
220 | *count = event_count; | ||
221 | ret = !events_in_progress; | ||
222 | spin_unlock_irq(&events_lock); | ||
223 | return ret; | ||
224 | } | ||
225 | |||
226 | /** | ||
227 | * pm_save_wakeup_count - Save the current number of registered wakeup events. | ||
228 | * @count: Value to compare with the current number of registered wakeup events. | ||
229 | * | ||
230 | * If @count is equal to the current number of registered wakeup events and the | ||
231 | * current number of wakeup events being processed is zero, store @count as the | ||
232 | * old number of registered wakeup events to be used by pm_check_wakeup_events() | ||
233 | * and return true. Otherwise return false. | ||
234 | */ | ||
235 | bool pm_save_wakeup_count(unsigned long count) | ||
236 | { | ||
237 | bool ret = false; | ||
238 | |||
239 | spin_lock_irq(&events_lock); | ||
240 | if (count == event_count && !events_in_progress) { | ||
241 | saved_event_count = count; | ||
242 | events_check_enabled = true; | ||
243 | ret = true; | ||
244 | } | ||
245 | spin_unlock_irq(&events_lock); | ||
246 | return ret; | ||
247 | } | ||
diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index 57a7e41da69e..9f13b660b801 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c | |||
@@ -2901,10 +2901,10 @@ static void e1000_configure_rx(struct e1000_adapter *adapter) | |||
2901 | * dropped transactions. | 2901 | * dropped transactions. |
2902 | */ | 2902 | */ |
2903 | pm_qos_update_request( | 2903 | pm_qos_update_request( |
2904 | adapter->netdev->pm_qos_req, 55); | 2904 | &adapter->netdev->pm_qos_req, 55); |
2905 | } else { | 2905 | } else { |
2906 | pm_qos_update_request( | 2906 | pm_qos_update_request( |
2907 | adapter->netdev->pm_qos_req, | 2907 | &adapter->netdev->pm_qos_req, |
2908 | PM_QOS_DEFAULT_VALUE); | 2908 | PM_QOS_DEFAULT_VALUE); |
2909 | } | 2909 | } |
2910 | } | 2910 | } |
@@ -3196,9 +3196,9 @@ int e1000e_up(struct e1000_adapter *adapter) | |||
3196 | 3196 | ||
3197 | /* DMA latency requirement to workaround early-receive/jumbo issue */ | 3197 | /* DMA latency requirement to workaround early-receive/jumbo issue */ |
3198 | if (adapter->flags & FLAG_HAS_ERT) | 3198 | if (adapter->flags & FLAG_HAS_ERT) |
3199 | adapter->netdev->pm_qos_req = | 3199 | pm_qos_add_request(&adapter->netdev->pm_qos_req, |
3200 | pm_qos_add_request(PM_QOS_CPU_DMA_LATENCY, | 3200 | PM_QOS_CPU_DMA_LATENCY, |
3201 | PM_QOS_DEFAULT_VALUE); | 3201 | PM_QOS_DEFAULT_VALUE); |
3202 | 3202 | ||
3203 | /* hardware has been reset, we need to reload some things */ | 3203 | /* hardware has been reset, we need to reload some things */ |
3204 | e1000_configure(adapter); | 3204 | e1000_configure(adapter); |
@@ -3263,11 +3263,8 @@ void e1000e_down(struct e1000_adapter *adapter) | |||
3263 | e1000_clean_tx_ring(adapter); | 3263 | e1000_clean_tx_ring(adapter); |
3264 | e1000_clean_rx_ring(adapter); | 3264 | e1000_clean_rx_ring(adapter); |
3265 | 3265 | ||
3266 | if (adapter->flags & FLAG_HAS_ERT) { | 3266 | if (adapter->flags & FLAG_HAS_ERT) |
3267 | pm_qos_remove_request( | 3267 | pm_qos_remove_request(&adapter->netdev->pm_qos_req); |
3268 | adapter->netdev->pm_qos_req); | ||
3269 | adapter->netdev->pm_qos_req = NULL; | ||
3270 | } | ||
3271 | 3268 | ||
3272 | /* | 3269 | /* |
3273 | * TODO: for power management, we could drop the link and | 3270 | * TODO: for power management, we could drop the link and |
diff --git a/drivers/net/igbvf/netdev.c b/drivers/net/igbvf/netdev.c index 5e2b2a8c56c6..add6197d3bcb 100644 --- a/drivers/net/igbvf/netdev.c +++ b/drivers/net/igbvf/netdev.c | |||
@@ -48,7 +48,7 @@ | |||
48 | #define DRV_VERSION "1.0.0-k0" | 48 | #define DRV_VERSION "1.0.0-k0" |
49 | char igbvf_driver_name[] = "igbvf"; | 49 | char igbvf_driver_name[] = "igbvf"; |
50 | const char igbvf_driver_version[] = DRV_VERSION; | 50 | const char igbvf_driver_version[] = DRV_VERSION; |
51 | struct pm_qos_request_list *igbvf_driver_pm_qos_req; | 51 | static struct pm_qos_request_list igbvf_driver_pm_qos_req; |
52 | static const char igbvf_driver_string[] = | 52 | static const char igbvf_driver_string[] = |
53 | "Intel(R) Virtual Function Network Driver"; | 53 | "Intel(R) Virtual Function Network Driver"; |
54 | static const char igbvf_copyright[] = "Copyright (c) 2009 Intel Corporation."; | 54 | static const char igbvf_copyright[] = "Copyright (c) 2009 Intel Corporation."; |
@@ -2902,8 +2902,8 @@ static int __init igbvf_init_module(void) | |||
2902 | printk(KERN_INFO "%s\n", igbvf_copyright); | 2902 | printk(KERN_INFO "%s\n", igbvf_copyright); |
2903 | 2903 | ||
2904 | ret = pci_register_driver(&igbvf_driver); | 2904 | ret = pci_register_driver(&igbvf_driver); |
2905 | igbvf_driver_pm_qos_req = pm_qos_add_request(PM_QOS_CPU_DMA_LATENCY, | 2905 | pm_qos_add_request(&igbvf_driver_pm_qos_req, PM_QOS_CPU_DMA_LATENCY, |
2906 | PM_QOS_DEFAULT_VALUE); | 2906 | PM_QOS_DEFAULT_VALUE); |
2907 | 2907 | ||
2908 | return ret; | 2908 | return ret; |
2909 | } | 2909 | } |
@@ -2918,8 +2918,7 @@ module_init(igbvf_init_module); | |||
2918 | static void __exit igbvf_exit_module(void) | 2918 | static void __exit igbvf_exit_module(void) |
2919 | { | 2919 | { |
2920 | pci_unregister_driver(&igbvf_driver); | 2920 | pci_unregister_driver(&igbvf_driver); |
2921 | pm_qos_remove_request(igbvf_driver_pm_qos_req); | 2921 | pm_qos_remove_request(&igbvf_driver_pm_qos_req); |
2922 | igbvf_driver_pm_qos_req = NULL; | ||
2923 | } | 2922 | } |
2924 | module_exit(igbvf_exit_module); | 2923 | module_exit(igbvf_exit_module); |
2925 | 2924 | ||
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c index 0bd4dfa59a8a..7f0d98b885bc 100644 --- a/drivers/net/wireless/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/ipw2x00/ipw2100.c | |||
@@ -174,7 +174,7 @@ that only one external action is invoked at a time. | |||
174 | #define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2100 Network Driver" | 174 | #define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2100 Network Driver" |
175 | #define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation" | 175 | #define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation" |
176 | 176 | ||
177 | struct pm_qos_request_list *ipw2100_pm_qos_req; | 177 | struct pm_qos_request_list ipw2100_pm_qos_req; |
178 | 178 | ||
179 | /* Debugging stuff */ | 179 | /* Debugging stuff */ |
180 | #ifdef CONFIG_IPW2100_DEBUG | 180 | #ifdef CONFIG_IPW2100_DEBUG |
@@ -1741,7 +1741,7 @@ static int ipw2100_up(struct ipw2100_priv *priv, int deferred) | |||
1741 | /* the ipw2100 hardware really doesn't want power management delays | 1741 | /* the ipw2100 hardware really doesn't want power management delays |
1742 | * longer than 175usec | 1742 | * longer than 175usec |
1743 | */ | 1743 | */ |
1744 | pm_qos_update_request(ipw2100_pm_qos_req, 175); | 1744 | pm_qos_update_request(&ipw2100_pm_qos_req, 175); |
1745 | 1745 | ||
1746 | /* If the interrupt is enabled, turn it off... */ | 1746 | /* If the interrupt is enabled, turn it off... */ |
1747 | spin_lock_irqsave(&priv->low_lock, flags); | 1747 | spin_lock_irqsave(&priv->low_lock, flags); |
@@ -1889,7 +1889,7 @@ static void ipw2100_down(struct ipw2100_priv *priv) | |||
1889 | ipw2100_disable_interrupts(priv); | 1889 | ipw2100_disable_interrupts(priv); |
1890 | spin_unlock_irqrestore(&priv->low_lock, flags); | 1890 | spin_unlock_irqrestore(&priv->low_lock, flags); |
1891 | 1891 | ||
1892 | pm_qos_update_request(ipw2100_pm_qos_req, PM_QOS_DEFAULT_VALUE); | 1892 | pm_qos_update_request(&ipw2100_pm_qos_req, PM_QOS_DEFAULT_VALUE); |
1893 | 1893 | ||
1894 | /* We have to signal any supplicant if we are disassociating */ | 1894 | /* We have to signal any supplicant if we are disassociating */ |
1895 | if (associated) | 1895 | if (associated) |
@@ -6669,8 +6669,8 @@ static int __init ipw2100_init(void) | |||
6669 | if (ret) | 6669 | if (ret) |
6670 | goto out; | 6670 | goto out; |
6671 | 6671 | ||
6672 | ipw2100_pm_qos_req = pm_qos_add_request(PM_QOS_CPU_DMA_LATENCY, | 6672 | pm_qos_add_request(&ipw2100_pm_qos_req, PM_QOS_CPU_DMA_LATENCY, |
6673 | PM_QOS_DEFAULT_VALUE); | 6673 | PM_QOS_DEFAULT_VALUE); |
6674 | #ifdef CONFIG_IPW2100_DEBUG | 6674 | #ifdef CONFIG_IPW2100_DEBUG |
6675 | ipw2100_debug_level = debug; | 6675 | ipw2100_debug_level = debug; |
6676 | ret = driver_create_file(&ipw2100_pci_driver.driver, | 6676 | ret = driver_create_file(&ipw2100_pci_driver.driver, |
@@ -6692,7 +6692,7 @@ static void __exit ipw2100_exit(void) | |||
6692 | &driver_attr_debug_level); | 6692 | &driver_attr_debug_level); |
6693 | #endif | 6693 | #endif |
6694 | pci_unregister_driver(&ipw2100_pci_driver); | 6694 | pci_unregister_driver(&ipw2100_pci_driver); |
6695 | pm_qos_remove_request(ipw2100_pm_qos_req); | 6695 | pm_qos_remove_request(&ipw2100_pm_qos_req); |
6696 | } | 6696 | } |
6697 | 6697 | ||
6698 | module_init(ipw2100_init); | 6698 | module_init(ipw2100_init); |
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 2e7a3bf13824..1ab98bbe58dd 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c | |||
@@ -48,6 +48,7 @@ static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context) | |||
48 | if (event == ACPI_NOTIFY_DEVICE_WAKE && pci_dev) { | 48 | if (event == ACPI_NOTIFY_DEVICE_WAKE && pci_dev) { |
49 | pci_check_pme_status(pci_dev); | 49 | pci_check_pme_status(pci_dev); |
50 | pm_runtime_resume(&pci_dev->dev); | 50 | pm_runtime_resume(&pci_dev->dev); |
51 | pci_wakeup_event(pci_dev); | ||
51 | if (pci_dev->subordinate) | 52 | if (pci_dev->subordinate) |
52 | pci_pme_wakeup_bus(pci_dev->subordinate); | 53 | pci_pme_wakeup_bus(pci_dev->subordinate); |
53 | } | 54 | } |
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 740fb4ea9669..130ed1daf0f8 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
@@ -1275,6 +1275,22 @@ bool pci_check_pme_status(struct pci_dev *dev) | |||
1275 | return ret; | 1275 | return ret; |
1276 | } | 1276 | } |
1277 | 1277 | ||
1278 | /* | ||
1279 | * Time to wait before the system can be put into a sleep state after reporting | ||
1280 | * a wakeup event signaled by a PCI device. | ||
1281 | */ | ||
1282 | #define PCI_WAKEUP_COOLDOWN 100 | ||
1283 | |||
1284 | /** | ||
1285 | * pci_wakeup_event - Report a wakeup event related to a given PCI device. | ||
1286 | * @dev: Device to report the wakeup event for. | ||
1287 | */ | ||
1288 | void pci_wakeup_event(struct pci_dev *dev) | ||
1289 | { | ||
1290 | if (device_may_wakeup(&dev->dev)) | ||
1291 | pm_wakeup_event(&dev->dev, PCI_WAKEUP_COOLDOWN); | ||
1292 | } | ||
1293 | |||
1278 | /** | 1294 | /** |
1279 | * pci_pme_wakeup - Wake up a PCI device if its PME Status bit is set. | 1295 | * pci_pme_wakeup - Wake up a PCI device if its PME Status bit is set. |
1280 | * @dev: Device to handle. | 1296 | * @dev: Device to handle. |
@@ -1285,8 +1301,10 @@ bool pci_check_pme_status(struct pci_dev *dev) | |||
1285 | */ | 1301 | */ |
1286 | static int pci_pme_wakeup(struct pci_dev *dev, void *ign) | 1302 | static int pci_pme_wakeup(struct pci_dev *dev, void *ign) |
1287 | { | 1303 | { |
1288 | if (pci_check_pme_status(dev)) | 1304 | if (pci_check_pme_status(dev)) { |
1289 | pm_request_resume(&dev->dev); | 1305 | pm_request_resume(&dev->dev); |
1306 | pci_wakeup_event(dev); | ||
1307 | } | ||
1290 | return 0; | 1308 | return 0; |
1291 | } | 1309 | } |
1292 | 1310 | ||
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index f8077b3c8c8c..c8b7fd056ccd 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h | |||
@@ -56,6 +56,7 @@ extern void pci_update_current_state(struct pci_dev *dev, pci_power_t state); | |||
56 | extern void pci_disable_enabled_device(struct pci_dev *dev); | 56 | extern void pci_disable_enabled_device(struct pci_dev *dev); |
57 | extern bool pci_check_pme_status(struct pci_dev *dev); | 57 | extern bool pci_check_pme_status(struct pci_dev *dev); |
58 | extern int pci_finish_runtime_suspend(struct pci_dev *dev); | 58 | extern int pci_finish_runtime_suspend(struct pci_dev *dev); |
59 | extern void pci_wakeup_event(struct pci_dev *dev); | ||
59 | extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign); | 60 | extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign); |
60 | extern void pci_pme_wakeup_bus(struct pci_bus *bus); | 61 | extern void pci_pme_wakeup_bus(struct pci_bus *bus); |
61 | extern void pci_pm_init(struct pci_dev *dev); | 62 | extern void pci_pm_init(struct pci_dev *dev); |
diff --git a/drivers/pci/pcie/pme/pcie_pme.c b/drivers/pci/pcie/pme/pcie_pme.c index d672a0a63816..bbdea18693d9 100644 --- a/drivers/pci/pcie/pme/pcie_pme.c +++ b/drivers/pci/pcie/pme/pcie_pme.c | |||
@@ -154,6 +154,7 @@ static bool pcie_pme_walk_bus(struct pci_bus *bus) | |||
154 | /* Skip PCIe devices in case we started from a root port. */ | 154 | /* Skip PCIe devices in case we started from a root port. */ |
155 | if (!pci_is_pcie(dev) && pci_check_pme_status(dev)) { | 155 | if (!pci_is_pcie(dev) && pci_check_pme_status(dev)) { |
156 | pm_request_resume(&dev->dev); | 156 | pm_request_resume(&dev->dev); |
157 | pci_wakeup_event(dev); | ||
157 | ret = true; | 158 | ret = true; |
158 | } | 159 | } |
159 | 160 | ||
@@ -254,8 +255,10 @@ static void pcie_pme_handle_request(struct pci_dev *port, u16 req_id) | |||
254 | if (found) { | 255 | if (found) { |
255 | /* The device is there, but we have to check its PME status. */ | 256 | /* The device is there, but we have to check its PME status. */ |
256 | found = pci_check_pme_status(dev); | 257 | found = pci_check_pme_status(dev); |
257 | if (found) | 258 | if (found) { |
258 | pm_request_resume(&dev->dev); | 259 | pm_request_resume(&dev->dev); |
260 | pci_wakeup_event(dev); | ||
261 | } | ||
259 | pci_dev_put(dev); | 262 | pci_dev_put(dev); |
260 | } else if (devfn) { | 263 | } else if (devfn) { |
261 | /* | 264 | /* |
diff --git a/drivers/pnp/core.c b/drivers/pnp/core.c index 5dba90995d9e..88b3cde52596 100644 --- a/drivers/pnp/core.c +++ b/drivers/pnp/core.c | |||
@@ -164,6 +164,9 @@ int __pnp_add_device(struct pnp_dev *dev) | |||
164 | list_add_tail(&dev->global_list, &pnp_global); | 164 | list_add_tail(&dev->global_list, &pnp_global); |
165 | list_add_tail(&dev->protocol_list, &dev->protocol->devices); | 165 | list_add_tail(&dev->protocol_list, &dev->protocol->devices); |
166 | spin_unlock(&pnp_lock); | 166 | spin_unlock(&pnp_lock); |
167 | if (dev->protocol->can_wakeup) | ||
168 | device_set_wakeup_capable(&dev->dev, | ||
169 | dev->protocol->can_wakeup(dev)); | ||
167 | return device_register(&dev->dev); | 170 | return device_register(&dev->dev); |
168 | } | 171 | } |
169 | 172 | ||
diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index f7ff628b7d94..dc4e32e031e9 100644 --- a/drivers/pnp/pnpacpi/core.c +++ b/drivers/pnp/pnpacpi/core.c | |||
@@ -122,17 +122,37 @@ static int pnpacpi_disable_resources(struct pnp_dev *dev) | |||
122 | } | 122 | } |
123 | 123 | ||
124 | #ifdef CONFIG_ACPI_SLEEP | 124 | #ifdef CONFIG_ACPI_SLEEP |
125 | static bool pnpacpi_can_wakeup(struct pnp_dev *dev) | ||
126 | { | ||
127 | struct acpi_device *acpi_dev = dev->data; | ||
128 | acpi_handle handle = acpi_dev->handle; | ||
129 | |||
130 | return acpi_bus_can_wakeup(handle); | ||
131 | } | ||
132 | |||
125 | static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state) | 133 | static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state) |
126 | { | 134 | { |
127 | struct acpi_device *acpi_dev = dev->data; | 135 | struct acpi_device *acpi_dev = dev->data; |
128 | acpi_handle handle = acpi_dev->handle; | 136 | acpi_handle handle = acpi_dev->handle; |
129 | int power_state; | 137 | int power_state; |
130 | 138 | ||
139 | if (device_can_wakeup(&dev->dev)) { | ||
140 | int rc = acpi_pm_device_sleep_wake(&dev->dev, | ||
141 | device_may_wakeup(&dev->dev)); | ||
142 | |||
143 | if (rc) | ||
144 | return rc; | ||
145 | } | ||
131 | power_state = acpi_pm_device_sleep_state(&dev->dev, NULL); | 146 | power_state = acpi_pm_device_sleep_state(&dev->dev, NULL); |
132 | if (power_state < 0) | 147 | if (power_state < 0) |
133 | power_state = (state.event == PM_EVENT_ON) ? | 148 | power_state = (state.event == PM_EVENT_ON) ? |
134 | ACPI_STATE_D0 : ACPI_STATE_D3; | 149 | ACPI_STATE_D0 : ACPI_STATE_D3; |
135 | 150 | ||
151 | /* acpi_bus_set_power() often fails (keyboard port can't be | ||
152 | * powered-down?), and in any case, our return value is ignored | ||
153 | * by pnp_bus_suspend(). Hence we don't revert the wakeup | ||
154 | * setting if the set_power fails. | ||
155 | */ | ||
136 | return acpi_bus_set_power(handle, power_state); | 156 | return acpi_bus_set_power(handle, power_state); |
137 | } | 157 | } |
138 | 158 | ||
@@ -141,6 +161,8 @@ static int pnpacpi_resume(struct pnp_dev *dev) | |||
141 | struct acpi_device *acpi_dev = dev->data; | 161 | struct acpi_device *acpi_dev = dev->data; |
142 | acpi_handle handle = acpi_dev->handle; | 162 | acpi_handle handle = acpi_dev->handle; |
143 | 163 | ||
164 | if (device_may_wakeup(&dev->dev)) | ||
165 | acpi_pm_device_sleep_wake(&dev->dev, false); | ||
144 | return acpi_bus_set_power(handle, ACPI_STATE_D0); | 166 | return acpi_bus_set_power(handle, ACPI_STATE_D0); |
145 | } | 167 | } |
146 | #endif | 168 | #endif |
@@ -151,6 +173,7 @@ struct pnp_protocol pnpacpi_protocol = { | |||
151 | .set = pnpacpi_set_resources, | 173 | .set = pnpacpi_set_resources, |
152 | .disable = pnpacpi_disable_resources, | 174 | .disable = pnpacpi_disable_resources, |
153 | #ifdef CONFIG_ACPI_SLEEP | 175 | #ifdef CONFIG_ACPI_SLEEP |
176 | .can_wakeup = pnpacpi_can_wakeup, | ||
154 | .suspend = pnpacpi_suspend, | 177 | .suspend = pnpacpi_suspend, |
155 | .resume = pnpacpi_resume, | 178 | .resume = pnpacpi_resume, |
156 | #endif | 179 | #endif |