aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base
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 /drivers/base
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>
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/power/main.c37
-rw-r--r--drivers/base/power/runtime.c19
2 files changed, 54 insertions, 2 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index f7a75592375..05b989139b5 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 42615b419df..25edc9a3a48 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)