aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/power/devices.txt45
-rw-r--r--drivers/base/power/main.c37
-rw-r--r--drivers/base/power/runtime.c19
-rw-r--r--include/linux/device.h1
-rw-r--r--include/linux/pm.h8
5 files changed, 107 insertions, 3 deletions
diff --git a/Documentation/power/devices.txt b/Documentation/power/devices.txt
index dd9b49251db3..df1a5cb10c42 100644
--- a/Documentation/power/devices.txt
+++ b/Documentation/power/devices.txt
@@ -1,6 +1,6 @@
1Device Power Management 1Device Power Management
2 2
3Copyright (c) 2010 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. 3Copyright (c) 2010-2011 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
4Copyright (c) 2010 Alan Stern <stern@rowland.harvard.edu> 4Copyright (c) 2010 Alan Stern <stern@rowland.harvard.edu>
5 5
6 6
@@ -507,6 +507,49 @@ routines. Nevertheless, different callback pointers are used in case there is a
507situation where it actually matters. 507situation where it actually matters.
508 508
509 509
510Device Power Domains
511--------------------
512Sometimes devices share reference clocks or other power resources. In those
513cases it generally is not possible to put devices into low-power states
514individually. Instead, a set of devices sharing a power resource can be put
515into a low-power state together at the same time by turning off the shared
516power resource. Of course, they also need to be put into the full-power state
517together, by turning the shared power resource on. A set of devices with this
518property is often referred to as a power domain.
519
520Support for power domains is provided through the pwr_domain field of struct
521device. This field is a pointer to an object of type struct dev_power_domain,
522defined in include/linux/pm.h, providing a set of power management callbacks
523analogous to the subsystem-level and device driver callbacks that are executed
524for the given device during all power transitions, in addition to the respective
525subsystem-level callbacks. Specifically, the power domain "suspend" callbacks
526(i.e. ->runtime_suspend(), ->suspend(), ->freeze(), ->poweroff(), etc.) are
527executed after the analogous subsystem-level callbacks, while the power domain
528"resume" callbacks (i.e. ->runtime_resume(), ->resume(), ->thaw(), ->restore,
529etc.) are executed before the analogous subsystem-level callbacks. Error codes
530returned by the "suspend" and "resume" power domain callbacks are ignored.
531
532Power domain ->runtime_idle() callback is executed before the subsystem-level
533->runtime_idle() callback and the result returned by it is not ignored. Namely,
534if it returns error code, the subsystem-level ->runtime_idle() callback will not
535be called and the helper function rpm_idle() executing it will return error
536code. This mechanism is intended to help platforms where saving device state
537is a time consuming operation and should only be carried out if all devices
538in the power domain are idle, before turning off the shared power resource(s).
539Namely, the power domain ->runtime_idle() callback may return error code until
540the pm_runtime_idle() helper (or its asychronous version) has been called for
541all devices in the power domain (it is recommended that the returned error code
542be -EBUSY in those cases), preventing the subsystem-level ->runtime_idle()
543callback from being run prematurely.
544
545The support for device power domains is only relevant to platforms needing to
546use the same subsystem-level (e.g. platform bus type) and device driver power
547management callbacks in many different power domain configurations and wanting
548to avoid incorporating the support for power domains into the subsystem-level
549callbacks. The other platforms need not implement it or take it into account
550in any way.
551
552
510System Devices 553System Devices
511-------------- 554--------------
512System devices (sysdevs) follow a slightly different API, which can be found in 555System devices (sysdevs) follow a slightly different API, which can be found in
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index f7a755923751..05b989139b54 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -423,6 +423,11 @@ static int device_resume_noirq(struct device *dev, pm_message_t state)
423 TRACE_DEVICE(dev); 423 TRACE_DEVICE(dev);
424 TRACE_RESUME(0); 424 TRACE_RESUME(0);
425 425
426 if (dev->pwr_domain) {
427 pm_dev_dbg(dev, state, "EARLY power domain ");
428 pm_noirq_op(dev, &dev->pwr_domain->ops, state);
429 }
430
426 if (dev->bus && dev->bus->pm) { 431 if (dev->bus && dev->bus->pm) {
427 pm_dev_dbg(dev, state, "EARLY "); 432 pm_dev_dbg(dev, state, "EARLY ");
428 error = pm_noirq_op(dev, dev->bus->pm, state); 433 error = pm_noirq_op(dev, dev->bus->pm, state);
@@ -518,6 +523,11 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
518 523
519 dev->power.in_suspend = false; 524 dev->power.in_suspend = false;
520 525
526 if (dev->pwr_domain) {
527 pm_dev_dbg(dev, state, "power domain ");
528 pm_op(dev, &dev->pwr_domain->ops, state);
529 }
530
521 if (dev->bus) { 531 if (dev->bus) {
522 if (dev->bus->pm) { 532 if (dev->bus->pm) {
523 pm_dev_dbg(dev, state, ""); 533 pm_dev_dbg(dev, state, "");
@@ -629,6 +639,11 @@ static void device_complete(struct device *dev, pm_message_t state)
629{ 639{
630 device_lock(dev); 640 device_lock(dev);
631 641
642 if (dev->pwr_domain && dev->pwr_domain->ops.complete) {
643 pm_dev_dbg(dev, state, "completing power domain ");
644 dev->pwr_domain->ops.complete(dev);
645 }
646
632 if (dev->class && dev->class->pm && dev->class->pm->complete) { 647 if (dev->class && dev->class->pm && dev->class->pm->complete) {
633 pm_dev_dbg(dev, state, "completing class "); 648 pm_dev_dbg(dev, state, "completing class ");
634 dev->class->pm->complete(dev); 649 dev->class->pm->complete(dev);
@@ -745,6 +760,13 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state)
745 if (dev->bus && dev->bus->pm) { 760 if (dev->bus && dev->bus->pm) {
746 pm_dev_dbg(dev, state, "LATE "); 761 pm_dev_dbg(dev, state, "LATE ");
747 error = pm_noirq_op(dev, dev->bus->pm, state); 762 error = pm_noirq_op(dev, dev->bus->pm, state);
763 if (error)
764 goto End;
765 }
766
767 if (dev->pwr_domain) {
768 pm_dev_dbg(dev, state, "LATE power domain ");
769 pm_noirq_op(dev, &dev->pwr_domain->ops, state);
748 } 770 }
749 771
750End: 772End:
@@ -864,6 +886,13 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
864 pm_dev_dbg(dev, state, "legacy "); 886 pm_dev_dbg(dev, state, "legacy ");
865 error = legacy_suspend(dev, state, dev->bus->suspend); 887 error = legacy_suspend(dev, state, dev->bus->suspend);
866 } 888 }
889 if (error)
890 goto End;
891 }
892
893 if (dev->pwr_domain) {
894 pm_dev_dbg(dev, state, "power domain ");
895 pm_op(dev, &dev->pwr_domain->ops, state);
867 } 896 }
868 897
869 End: 898 End:
@@ -976,7 +1005,15 @@ static int device_prepare(struct device *dev, pm_message_t state)
976 pm_dev_dbg(dev, state, "preparing class "); 1005 pm_dev_dbg(dev, state, "preparing class ");
977 error = dev->class->pm->prepare(dev); 1006 error = dev->class->pm->prepare(dev);
978 suspend_report_result(dev->class->pm->prepare, error); 1007 suspend_report_result(dev->class->pm->prepare, error);
1008 if (error)
1009 goto End;
1010 }
1011
1012 if (dev->pwr_domain && dev->pwr_domain->ops.prepare) {
1013 pm_dev_dbg(dev, state, "preparing power domain ");
1014 dev->pwr_domain->ops.prepare(dev);
979 } 1015 }
1016
980 End: 1017 End:
981 device_unlock(dev); 1018 device_unlock(dev);
982 1019
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 42615b419dfb..25edc9a3a489 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -168,6 +168,7 @@ static int rpm_check_suspend_allowed(struct device *dev)
168static int rpm_idle(struct device *dev, int rpmflags) 168static int rpm_idle(struct device *dev, int rpmflags)
169{ 169{
170 int (*callback)(struct device *); 170 int (*callback)(struct device *);
171 int (*domain_callback)(struct device *);
171 int retval; 172 int retval;
172 173
173 retval = rpm_check_suspend_allowed(dev); 174 retval = rpm_check_suspend_allowed(dev);
@@ -222,10 +223,19 @@ static int rpm_idle(struct device *dev, int rpmflags)
222 else 223 else
223 callback = NULL; 224 callback = NULL;
224 225
225 if (callback) { 226 if (dev->pwr_domain)
227 domain_callback = dev->pwr_domain->ops.runtime_idle;
228 else
229 domain_callback = NULL;
230
231 if (callback || domain_callback) {
226 spin_unlock_irq(&dev->power.lock); 232 spin_unlock_irq(&dev->power.lock);
227 233
228 callback(dev); 234 if (domain_callback)
235 retval = domain_callback(dev);
236
237 if (!retval && callback)
238 callback(dev);
229 239
230 spin_lock_irq(&dev->power.lock); 240 spin_lock_irq(&dev->power.lock);
231 } 241 }
@@ -390,6 +400,8 @@ static int rpm_suspend(struct device *dev, int rpmflags)
390 else 400 else
391 pm_runtime_cancel_pending(dev); 401 pm_runtime_cancel_pending(dev);
392 } else { 402 } else {
403 if (dev->pwr_domain)
404 rpm_callback(dev->pwr_domain->ops.runtime_suspend, dev);
393 no_callback: 405 no_callback:
394 __update_runtime_status(dev, RPM_SUSPENDED); 406 __update_runtime_status(dev, RPM_SUSPENDED);
395 pm_runtime_deactivate_timer(dev); 407 pm_runtime_deactivate_timer(dev);
@@ -569,6 +581,9 @@ static int rpm_resume(struct device *dev, int rpmflags)
569 581
570 __update_runtime_status(dev, RPM_RESUMING); 582 __update_runtime_status(dev, RPM_RESUMING);
571 583
584 if (dev->pwr_domain)
585 rpm_callback(dev->pwr_domain->ops.runtime_resume, dev);
586
572 if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) 587 if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume)
573 callback = dev->bus->pm->runtime_resume; 588 callback = dev->bus->pm->runtime_resume;
574 else if (dev->type && dev->type->pm && dev->type->pm->runtime_resume) 589 else if (dev->type && dev->type->pm && dev->type->pm->runtime_resume)
diff --git a/include/linux/device.h b/include/linux/device.h
index 1bf5cf0b4513..22e9a8a7e1bc 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -422,6 +422,7 @@ struct device {
422 void *platform_data; /* Platform specific data, device 422 void *platform_data; /* Platform specific data, device
423 core doesn't touch it */ 423 core doesn't touch it */
424 struct dev_pm_info power; 424 struct dev_pm_info power;
425 struct dev_power_domain *pwr_domain;
425 426
426#ifdef CONFIG_NUMA 427#ifdef CONFIG_NUMA
427 int numa_node; /* NUMA node this device is close to */ 428 int numa_node; /* NUMA node this device is close to */
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 1f79c98f1e56..6618216bb973 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -465,6 +465,14 @@ struct dev_pm_info {
465 465
466extern void update_pm_runtime_accounting(struct device *dev); 466extern void update_pm_runtime_accounting(struct device *dev);
467 467
468/*
469 * Power domains provide callbacks that are executed during system suspend,
470 * hibernation, system resume and during runtime PM transitions along with
471 * subsystem-level and driver-level callbacks.
472 */
473struct dev_power_domain {
474 struct dev_pm_ops ops;
475};
468 476
469/* 477/*
470 * The PM_EVENT_ messages are also used by drivers implementing the legacy 478 * The PM_EVENT_ messages are also used by drivers implementing the legacy