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 |
