aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2010-11-30 18:14:42 -0500
committerRafael J. Wysocki <rjw@sisk.pl>2010-12-24 09:02:41 -0500
commitc7b61de5b7b17f0df34dc7d2f8b9576f8bd36fce (patch)
treed8cf6be1aab175839973d2c76060bd41d65b2ed5 /drivers/base
parent5262a47502adcfc3a64403120768f528418a3b79 (diff)
PM / Runtime: Add synchronous runtime interface for interrupt handlers (v3)
This patch (as1431c) makes the synchronous runtime-PM interface suitable for use in interrupt handlers. Subsystems can call the new pm_runtime_irq_safe() function to tell the PM core that a device's runtime_suspend and runtime_resume callbacks should be invoked with interrupts disabled and the spinlock held. This permits the pm_runtime_get_sync() and the new pm_runtime_put_sync_suspend() routines to be called from within interrupt handlers. When a device is declared irq-safe in this way, the PM core increments the parent's usage count, so the parent will never be runtime suspended. This prevents difficult situations in which an irq-safe device can't resume because it is forced to wait for its non-irq-safe parent. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/power/runtime.c47
1 files changed, 38 insertions, 9 deletions
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 02c652be83e7..656493a5e073 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -250,13 +250,16 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev)
250 if (!cb) 250 if (!cb)
251 return -ENOSYS; 251 return -ENOSYS;
252 252
253 spin_unlock_irq(&dev->power.lock); 253 if (dev->power.irq_safe) {
254 retval = cb(dev);
255 } else {
256 spin_unlock_irq(&dev->power.lock);
254 257
255 retval = cb(dev); 258 retval = cb(dev);
256 259
257 spin_lock_irq(&dev->power.lock); 260 spin_lock_irq(&dev->power.lock);
261 }
258 dev->power.runtime_error = retval; 262 dev->power.runtime_error = retval;
259
260 return retval; 263 return retval;
261} 264}
262 265
@@ -404,7 +407,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
404 goto out; 407 goto out;
405 } 408 }
406 409
407 if (parent && !parent->power.ignore_children) { 410 if (parent && !parent->power.ignore_children && !dev->power.irq_safe) {
408 spin_unlock_irq(&dev->power.lock); 411 spin_unlock_irq(&dev->power.lock);
409 412
410 pm_request_idle(parent); 413 pm_request_idle(parent);
@@ -527,10 +530,13 @@ static int rpm_resume(struct device *dev, int rpmflags)
527 530
528 if (!parent && dev->parent) { 531 if (!parent && dev->parent) {
529 /* 532 /*
530 * Increment the parent's resume counter and resume it if 533 * Increment the parent's usage counter and resume it if
531 * necessary. 534 * necessary. Not needed if dev is irq-safe; then the
535 * parent is permanently resumed.
532 */ 536 */
533 parent = dev->parent; 537 parent = dev->parent;
538 if (dev->power.irq_safe)
539 goto skip_parent;
534 spin_unlock(&dev->power.lock); 540 spin_unlock(&dev->power.lock);
535 541
536 pm_runtime_get_noresume(parent); 542 pm_runtime_get_noresume(parent);
@@ -553,6 +559,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
553 goto out; 559 goto out;
554 goto repeat; 560 goto repeat;
555 } 561 }
562 skip_parent:
556 563
557 if (dev->power.no_callbacks) 564 if (dev->power.no_callbacks)
558 goto no_callback; /* Assume success. */ 565 goto no_callback; /* Assume success. */
@@ -584,7 +591,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
584 rpm_idle(dev, RPM_ASYNC); 591 rpm_idle(dev, RPM_ASYNC);
585 592
586 out: 593 out:
587 if (parent) { 594 if (parent && !dev->power.irq_safe) {
588 spin_unlock_irq(&dev->power.lock); 595 spin_unlock_irq(&dev->power.lock);
589 596
590 pm_runtime_put(parent); 597 pm_runtime_put(parent);
@@ -1065,7 +1072,6 @@ EXPORT_SYMBOL_GPL(pm_runtime_allow);
1065 * Set the power.no_callbacks flag, which tells the PM core that this 1072 * Set the power.no_callbacks flag, which tells the PM core that this
1066 * device is power-managed through its parent and has no run-time PM 1073 * device is power-managed through its parent and has no run-time PM
1067 * callbacks of its own. The run-time sysfs attributes will be removed. 1074 * callbacks of its own. The run-time sysfs attributes will be removed.
1068 *
1069 */ 1075 */
1070void pm_runtime_no_callbacks(struct device *dev) 1076void pm_runtime_no_callbacks(struct device *dev)
1071{ 1077{
@@ -1078,6 +1084,27 @@ void pm_runtime_no_callbacks(struct device *dev)
1078EXPORT_SYMBOL_GPL(pm_runtime_no_callbacks); 1084EXPORT_SYMBOL_GPL(pm_runtime_no_callbacks);
1079 1085
1080/** 1086/**
1087 * pm_runtime_irq_safe - Leave interrupts disabled during callbacks.
1088 * @dev: Device to handle
1089 *
1090 * Set the power.irq_safe flag, which tells the PM core that the
1091 * ->runtime_suspend() and ->runtime_resume() callbacks for this device should
1092 * always be invoked with the spinlock held and interrupts disabled. It also
1093 * causes the parent's usage counter to be permanently incremented, preventing
1094 * the parent from runtime suspending -- otherwise an irq-safe child might have
1095 * to wait for a non-irq-safe parent.
1096 */
1097void pm_runtime_irq_safe(struct device *dev)
1098{
1099 if (dev->parent)
1100 pm_runtime_get_sync(dev->parent);
1101 spin_lock_irq(&dev->power.lock);
1102 dev->power.irq_safe = 1;
1103 spin_unlock_irq(&dev->power.lock);
1104}
1105EXPORT_SYMBOL_GPL(pm_runtime_irq_safe);
1106
1107/**
1081 * update_autosuspend - Handle a change to a device's autosuspend settings. 1108 * update_autosuspend - Handle a change to a device's autosuspend settings.
1082 * @dev: Device to handle. 1109 * @dev: Device to handle.
1083 * @old_delay: The former autosuspend_delay value. 1110 * @old_delay: The former autosuspend_delay value.
@@ -1199,4 +1226,6 @@ void pm_runtime_remove(struct device *dev)
1199 /* Change the status back to 'suspended' to match the initial status. */ 1226 /* Change the status back to 'suspended' to match the initial status. */
1200 if (dev->power.runtime_status == RPM_ACTIVE) 1227 if (dev->power.runtime_status == RPM_ACTIVE)
1201 pm_runtime_set_suspended(dev); 1228 pm_runtime_set_suspended(dev);
1229 if (dev->power.irq_safe && dev->parent)
1230 pm_runtime_put_sync(dev->parent);
1202} 1231}