diff options
Diffstat (limited to 'drivers/base/power/runtime.c')
-rw-r--r-- | drivers/base/power/runtime.c | 54 |
1 files changed, 53 insertions, 1 deletions
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index ed227b7c1bb5..5bd4daa93ef1 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c | |||
@@ -10,8 +10,10 @@ | |||
10 | #include <linux/sched.h> | 10 | #include <linux/sched.h> |
11 | #include <linux/pm_runtime.h> | 11 | #include <linux/pm_runtime.h> |
12 | #include <linux/jiffies.h> | 12 | #include <linux/jiffies.h> |
13 | #include "power.h" | ||
13 | 14 | ||
14 | static int rpm_resume(struct device *dev, int rpmflags); | 15 | static int rpm_resume(struct device *dev, int rpmflags); |
16 | static int rpm_suspend(struct device *dev, int rpmflags); | ||
15 | 17 | ||
16 | /** | 18 | /** |
17 | * update_pm_runtime_accounting - Update the time accounting of power states | 19 | * update_pm_runtime_accounting - Update the time accounting of power states |
@@ -148,6 +150,12 @@ static int rpm_idle(struct device *dev, int rpmflags) | |||
148 | /* Pending requests need to be canceled. */ | 150 | /* Pending requests need to be canceled. */ |
149 | dev->power.request = RPM_REQ_NONE; | 151 | dev->power.request = RPM_REQ_NONE; |
150 | 152 | ||
153 | if (dev->power.no_callbacks) { | ||
154 | /* Assume ->runtime_idle() callback would have suspended. */ | ||
155 | retval = rpm_suspend(dev, rpmflags); | ||
156 | goto out; | ||
157 | } | ||
158 | |||
151 | /* Carry out an asynchronous or a synchronous idle notification. */ | 159 | /* Carry out an asynchronous or a synchronous idle notification. */ |
152 | if (rpmflags & RPM_ASYNC) { | 160 | if (rpmflags & RPM_ASYNC) { |
153 | dev->power.request = RPM_REQ_IDLE; | 161 | dev->power.request = RPM_REQ_IDLE; |
@@ -254,6 +262,10 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
254 | goto repeat; | 262 | goto repeat; |
255 | } | 263 | } |
256 | 264 | ||
265 | dev->power.deferred_resume = false; | ||
266 | if (dev->power.no_callbacks) | ||
267 | goto no_callback; /* Assume success. */ | ||
268 | |||
257 | /* Carry out an asynchronous or a synchronous suspend. */ | 269 | /* Carry out an asynchronous or a synchronous suspend. */ |
258 | if (rpmflags & RPM_ASYNC) { | 270 | if (rpmflags & RPM_ASYNC) { |
259 | dev->power.request = RPM_REQ_SUSPEND; | 271 | dev->power.request = RPM_REQ_SUSPEND; |
@@ -265,7 +277,6 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
265 | } | 277 | } |
266 | 278 | ||
267 | __update_runtime_status(dev, RPM_SUSPENDING); | 279 | __update_runtime_status(dev, RPM_SUSPENDING); |
268 | dev->power.deferred_resume = false; | ||
269 | 280 | ||
270 | if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) { | 281 | if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) { |
271 | spin_unlock_irq(&dev->power.lock); | 282 | spin_unlock_irq(&dev->power.lock); |
@@ -305,6 +316,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
305 | pm_runtime_cancel_pending(dev); | 316 | pm_runtime_cancel_pending(dev); |
306 | } | 317 | } |
307 | } else { | 318 | } else { |
319 | no_callback: | ||
308 | __update_runtime_status(dev, RPM_SUSPENDED); | 320 | __update_runtime_status(dev, RPM_SUSPENDED); |
309 | pm_runtime_deactivate_timer(dev); | 321 | pm_runtime_deactivate_timer(dev); |
310 | 322 | ||
@@ -409,6 +421,23 @@ static int rpm_resume(struct device *dev, int rpmflags) | |||
409 | goto repeat; | 421 | goto repeat; |
410 | } | 422 | } |
411 | 423 | ||
424 | /* | ||
425 | * See if we can skip waking up the parent. This is safe only if | ||
426 | * power.no_callbacks is set, because otherwise we don't know whether | ||
427 | * the resume will actually succeed. | ||
428 | */ | ||
429 | if (dev->power.no_callbacks && !parent && dev->parent) { | ||
430 | spin_lock(&dev->parent->power.lock); | ||
431 | if (dev->parent->power.disable_depth > 0 | ||
432 | || dev->parent->power.ignore_children | ||
433 | || dev->parent->power.runtime_status == RPM_ACTIVE) { | ||
434 | atomic_inc(&dev->parent->power.child_count); | ||
435 | spin_unlock(&dev->parent->power.lock); | ||
436 | goto no_callback; /* Assume success. */ | ||
437 | } | ||
438 | spin_unlock(&dev->parent->power.lock); | ||
439 | } | ||
440 | |||
412 | /* Carry out an asynchronous or a synchronous resume. */ | 441 | /* Carry out an asynchronous or a synchronous resume. */ |
413 | if (rpmflags & RPM_ASYNC) { | 442 | if (rpmflags & RPM_ASYNC) { |
414 | dev->power.request = RPM_REQ_RESUME; | 443 | dev->power.request = RPM_REQ_RESUME; |
@@ -449,6 +478,9 @@ static int rpm_resume(struct device *dev, int rpmflags) | |||
449 | goto repeat; | 478 | goto repeat; |
450 | } | 479 | } |
451 | 480 | ||
481 | if (dev->power.no_callbacks) | ||
482 | goto no_callback; /* Assume success. */ | ||
483 | |||
452 | __update_runtime_status(dev, RPM_RESUMING); | 484 | __update_runtime_status(dev, RPM_RESUMING); |
453 | 485 | ||
454 | if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) { | 486 | if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) { |
@@ -482,6 +514,7 @@ static int rpm_resume(struct device *dev, int rpmflags) | |||
482 | __update_runtime_status(dev, RPM_SUSPENDED); | 514 | __update_runtime_status(dev, RPM_SUSPENDED); |
483 | pm_runtime_cancel_pending(dev); | 515 | pm_runtime_cancel_pending(dev); |
484 | } else { | 516 | } else { |
517 | no_callback: | ||
485 | __update_runtime_status(dev, RPM_ACTIVE); | 518 | __update_runtime_status(dev, RPM_ACTIVE); |
486 | if (parent) | 519 | if (parent) |
487 | atomic_inc(&parent->power.child_count); | 520 | atomic_inc(&parent->power.child_count); |
@@ -955,6 +988,25 @@ void pm_runtime_allow(struct device *dev) | |||
955 | EXPORT_SYMBOL_GPL(pm_runtime_allow); | 988 | EXPORT_SYMBOL_GPL(pm_runtime_allow); |
956 | 989 | ||
957 | /** | 990 | /** |
991 | * pm_runtime_no_callbacks - Ignore run-time PM callbacks for a device. | ||
992 | * @dev: Device to handle. | ||
993 | * | ||
994 | * Set the power.no_callbacks flag, which tells the PM core that this | ||
995 | * device is power-managed through its parent and has no run-time PM | ||
996 | * callbacks of its own. The run-time sysfs attributes will be removed. | ||
997 | * | ||
998 | */ | ||
999 | void pm_runtime_no_callbacks(struct device *dev) | ||
1000 | { | ||
1001 | spin_lock_irq(&dev->power.lock); | ||
1002 | dev->power.no_callbacks = 1; | ||
1003 | spin_unlock_irq(&dev->power.lock); | ||
1004 | if (device_is_registered(dev)) | ||
1005 | rpm_sysfs_remove(dev); | ||
1006 | } | ||
1007 | EXPORT_SYMBOL_GPL(pm_runtime_no_callbacks); | ||
1008 | |||
1009 | /** | ||
958 | * pm_runtime_init - Initialize run-time PM fields in given device object. | 1010 | * pm_runtime_init - Initialize run-time PM fields in given device object. |
959 | * @dev: Device object to initialize. | 1011 | * @dev: Device object to initialize. |
960 | */ | 1012 | */ |