diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2011-02-16 21:53:17 +0100 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2011-03-15 00:43:16 +0100 |
commit | 7538e3db6e015e890825fbd9f8659952896ddd5b (patch) | |
tree | 01a6d8c97599474d9c5fc1ed0eb6f0addaec5652 /drivers/base/power | |
parent | 6831c6edc7b272a08dd2a6c71bb183a48fe98ae6 (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/power')
-rw-r--r-- | drivers/base/power/main.c | 37 | ||||
-rw-r--r-- | drivers/base/power/runtime.c | 19 |
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) TRACE_DEVICE(dev); TRACE_RESUME(0); + if (dev->pwr_domain) { + pm_dev_dbg(dev, state, "EARLY power domain "); + pm_noirq_op(dev, &dev->pwr_domain->ops, state); + } + if (dev->bus && dev->bus->pm) { pm_dev_dbg(dev, state, "EARLY "); 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) dev->power.in_suspend = false; + if (dev->pwr_domain) { + pm_dev_dbg(dev, state, "power domain "); + pm_op(dev, &dev->pwr_domain->ops, state); + } + if (dev->bus) { if (dev->bus->pm) { pm_dev_dbg(dev, state, ""); @@ -629,6 +639,11 @@ static void device_complete(struct device *dev, pm_message_t state) { device_lock(dev); + if (dev->pwr_domain && dev->pwr_domain->ops.complete) { + pm_dev_dbg(dev, state, "completing power domain "); + dev->pwr_domain->ops.complete(dev); + } + if (dev->class && dev->class->pm && dev->class->pm->complete) { pm_dev_dbg(dev, state, "completing class "); dev->class->pm->complete(dev); @@ -745,6 +760,13 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state) if (dev->bus && dev->bus->pm) { pm_dev_dbg(dev, state, "LATE "); error = pm_noirq_op(dev, dev->bus->pm, state); + if (error) + goto End; + } + + if (dev->pwr_domain) { + pm_dev_dbg(dev, state, "LATE power domain "); + pm_noirq_op(dev, &dev->pwr_domain->ops, state); } End: @@ -864,6 +886,13 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) pm_dev_dbg(dev, state, "legacy "); error = legacy_suspend(dev, state, dev->bus->suspend); } + if (error) + goto End; + } + + if (dev->pwr_domain) { + pm_dev_dbg(dev, state, "power domain "); + pm_op(dev, &dev->pwr_domain->ops, state); } End: @@ -976,7 +1005,15 @@ static int device_prepare(struct device *dev, pm_message_t state) pm_dev_dbg(dev, state, "preparing class "); error = dev->class->pm->prepare(dev); suspend_report_result(dev->class->pm->prepare, error); + if (error) + goto End; + } + + if (dev->pwr_domain && dev->pwr_domain->ops.prepare) { + pm_dev_dbg(dev, state, "preparing power domain "); + dev->pwr_domain->ops.prepare(dev); } + End: device_unlock(dev); 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) static int rpm_idle(struct device *dev, int rpmflags) { int (*callback)(struct device *); + int (*domain_callback)(struct device *); int retval; retval = rpm_check_suspend_allowed(dev); @@ -222,10 +223,19 @@ static int rpm_idle(struct device *dev, int rpmflags) else callback = NULL; - if (callback) { + if (dev->pwr_domain) + domain_callback = dev->pwr_domain->ops.runtime_idle; + else + domain_callback = NULL; + + if (callback || domain_callback) { spin_unlock_irq(&dev->power.lock); - callback(dev); + if (domain_callback) + retval = domain_callback(dev); + + if (!retval && callback) + callback(dev); spin_lock_irq(&dev->power.lock); } @@ -390,6 +400,8 @@ static int rpm_suspend(struct device *dev, int rpmflags) else pm_runtime_cancel_pending(dev); } else { + if (dev->pwr_domain) + rpm_callback(dev->pwr_domain->ops.runtime_suspend, dev); no_callback: __update_runtime_status(dev, RPM_SUSPENDED); pm_runtime_deactivate_timer(dev); @@ -569,6 +581,9 @@ static int rpm_resume(struct device *dev, int rpmflags) __update_runtime_status(dev, RPM_RESUMING); + if (dev->pwr_domain) + rpm_callback(dev->pwr_domain->ops.runtime_resume, dev); + if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) callback = dev->bus->pm->runtime_resume; else if (dev->type && dev->type->pm && dev->type->pm->runtime_resume) |