aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2011-02-16 15:53:17 -0500
committerRafael J. Wysocki <rjw@sisk.pl>2011-03-14 19:43:16 -0400
commit7538e3db6e015e890825fbd9f8659952896ddd5b (patch)
tree01a6d8c97599474d9c5fc1ed0eb6f0addaec5652
parent6831c6edc7b272a08dd2a6c71bb183a48fe98ae6 (diff)
PM: Add support for device power domains
The platform bus type is often used to handle Systems-on-a-Chip (SoC) where all devices are represented by objects of type struct platform_device. In those cases the same "platform" device driver may be used with multiple different system configurations, but the actions needed to put the devices it handles into a low-power state and back into the full-power state may depend on the design of the given SoC. The driver, however, cannot possibly include all the information necessary for the power management of its device on all the systems it is used with. Moreover, the device hierarchy in its current form also is not suitable for representing this kind of information. The patch below attempts to address this problem by introducing objects of type struct dev_power_domain that can be used for representing power domains within a SoC. Every struct dev_power_domain object provides a sets of device power management callbacks that can be used to perform what's needed for device power management in addition to the operations carried out by the device's driver and subsystem. Namely, if a struct dev_power_domain object is pointed to by the pwr_domain field in a struct device, the callbacks provided by its ops member will be executed in addition to the corresponding callbacks provided by the device's subsystem and driver during all power transitions. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Tested-and-acked-by: Kevin Hilman <khilman@ti.com>
-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