aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/base/power/power.h1
-rw-r--r--drivers/base/power/runtime.c54
-rw-r--r--drivers/base/power/sysfs.c56
3 files changed, 103 insertions, 8 deletions
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index 8b2745c69e2a..698dde742587 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -60,6 +60,7 @@ static inline void device_pm_move_last(struct device *dev) {}
60 60
61extern int dpm_sysfs_add(struct device *); 61extern int dpm_sysfs_add(struct device *);
62extern void dpm_sysfs_remove(struct device *); 62extern void dpm_sysfs_remove(struct device *);
63extern void rpm_sysfs_remove(struct device *);
63 64
64#else /* CONFIG_PM */ 65#else /* CONFIG_PM */
65 66
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
14static int rpm_resume(struct device *dev, int rpmflags); 15static int rpm_resume(struct device *dev, int rpmflags);
16static 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)
955EXPORT_SYMBOL_GPL(pm_runtime_allow); 988EXPORT_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 */
999void 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}
1007EXPORT_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 */
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index 8859780817e1..b5708c47ce2d 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -81,6 +81,9 @@
81static const char enabled[] = "enabled"; 81static const char enabled[] = "enabled";
82static const char disabled[] = "disabled"; 82static const char disabled[] = "disabled";
83 83
84const char power_group_name[] = "power";
85EXPORT_SYMBOL_GPL(power_group_name);
86
84#ifdef CONFIG_PM_RUNTIME 87#ifdef CONFIG_PM_RUNTIME
85static const char ctrl_auto[] = "auto"; 88static const char ctrl_auto[] = "auto";
86static const char ctrl_on[] = "on"; 89static const char ctrl_on[] = "on";
@@ -390,12 +393,6 @@ static DEVICE_ATTR(async, 0644, async_show, async_store);
390#endif /* CONFIG_PM_ADVANCED_DEBUG */ 393#endif /* CONFIG_PM_ADVANCED_DEBUG */
391 394
392static struct attribute * power_attrs[] = { 395static struct attribute * power_attrs[] = {
393#ifdef CONFIG_PM_RUNTIME
394 &dev_attr_control.attr,
395 &dev_attr_runtime_status.attr,
396 &dev_attr_runtime_suspended_time.attr,
397 &dev_attr_runtime_active_time.attr,
398#endif
399 &dev_attr_wakeup.attr, 396 &dev_attr_wakeup.attr,
400#ifdef CONFIG_PM_SLEEP 397#ifdef CONFIG_PM_SLEEP
401 &dev_attr_wakeup_count.attr, 398 &dev_attr_wakeup_count.attr,
@@ -409,6 +406,7 @@ static struct attribute * power_attrs[] = {
409#ifdef CONFIG_PM_ADVANCED_DEBUG 406#ifdef CONFIG_PM_ADVANCED_DEBUG
410 &dev_attr_async.attr, 407 &dev_attr_async.attr,
411#ifdef CONFIG_PM_RUNTIME 408#ifdef CONFIG_PM_RUNTIME
409 &dev_attr_runtime_status.attr,
412 &dev_attr_runtime_usage.attr, 410 &dev_attr_runtime_usage.attr,
413 &dev_attr_runtime_active_kids.attr, 411 &dev_attr_runtime_active_kids.attr,
414 &dev_attr_runtime_enabled.attr, 412 &dev_attr_runtime_enabled.attr,
@@ -417,10 +415,52 @@ static struct attribute * power_attrs[] = {
417 NULL, 415 NULL,
418}; 416};
419static struct attribute_group pm_attr_group = { 417static struct attribute_group pm_attr_group = {
420 .name = "power", 418 .name = power_group_name,
421 .attrs = power_attrs, 419 .attrs = power_attrs,
422}; 420};
423 421
422#ifdef CONFIG_PM_RUNTIME
423
424static struct attribute *runtime_attrs[] = {
425#ifndef CONFIG_PM_ADVANCED_DEBUG
426 &dev_attr_runtime_status.attr,
427#endif
428 &dev_attr_control.attr,
429 &dev_attr_runtime_suspended_time.attr,
430 &dev_attr_runtime_active_time.attr,
431 NULL,
432};
433static struct attribute_group pm_runtime_attr_group = {
434 .name = power_group_name,
435 .attrs = runtime_attrs,
436};
437
438int dpm_sysfs_add(struct device *dev)
439{
440 int rc;
441
442 rc = sysfs_create_group(&dev->kobj, &pm_attr_group);
443 if (rc == 0 && !dev->power.no_callbacks) {
444 rc = sysfs_merge_group(&dev->kobj, &pm_runtime_attr_group);
445 if (rc)
446 sysfs_remove_group(&dev->kobj, &pm_attr_group);
447 }
448 return rc;
449}
450
451void rpm_sysfs_remove(struct device *dev)
452{
453 sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group);
454}
455
456void dpm_sysfs_remove(struct device *dev)
457{
458 rpm_sysfs_remove(dev);
459 sysfs_remove_group(&dev->kobj, &pm_attr_group);
460}
461
462#else /* CONFIG_PM_RUNTIME */
463
424int dpm_sysfs_add(struct device * dev) 464int dpm_sysfs_add(struct device * dev)
425{ 465{
426 return sysfs_create_group(&dev->kobj, &pm_attr_group); 466 return sysfs_create_group(&dev->kobj, &pm_attr_group);
@@ -430,3 +470,5 @@ void dpm_sysfs_remove(struct device * dev)
430{ 470{
431 sysfs_remove_group(&dev->kobj, &pm_attr_group); 471 sysfs_remove_group(&dev->kobj, &pm_attr_group);
432} 472}
473
474#endif