Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 235150
b: refs/heads/master
c: 7538e3d
h: refs/heads/master
v: v3
  • Loading branch information
Rafael J. Wysocki committed Mar 14, 2011
1 parent 97828e2 commit 3b743f4
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 4 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 6831c6edc7b272a08dd2a6c71bb183a48fe98ae6
refs/heads/master: 7538e3db6e015e890825fbd9f8659952896ddd5b
45 changes: 44 additions & 1 deletion trunk/Documentation/power/devices.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Device Power Management

Copyright (c) 2010 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
Copyright (c) 2010-2011 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
Copyright (c) 2010 Alan Stern <stern@rowland.harvard.edu>


Expand Down Expand Up @@ -507,6 +507,49 @@ routines. Nevertheless, different callback pointers are used in case there is a
situation where it actually matters.


Device Power Domains
--------------------
Sometimes devices share reference clocks or other power resources. In those
cases it generally is not possible to put devices into low-power states
individually. Instead, a set of devices sharing a power resource can be put
into a low-power state together at the same time by turning off the shared
power resource. Of course, they also need to be put into the full-power state
together, by turning the shared power resource on. A set of devices with this
property is often referred to as a power domain.

Support for power domains is provided through the pwr_domain field of struct
device. This field is a pointer to an object of type struct dev_power_domain,
defined in include/linux/pm.h, providing a set of power management callbacks
analogous to the subsystem-level and device driver callbacks that are executed
for the given device during all power transitions, in addition to the respective
subsystem-level callbacks. Specifically, the power domain "suspend" callbacks
(i.e. ->runtime_suspend(), ->suspend(), ->freeze(), ->poweroff(), etc.) are
executed after the analogous subsystem-level callbacks, while the power domain
"resume" callbacks (i.e. ->runtime_resume(), ->resume(), ->thaw(), ->restore,
etc.) are executed before the analogous subsystem-level callbacks. Error codes
returned by the "suspend" and "resume" power domain callbacks are ignored.

Power domain ->runtime_idle() callback is executed before the subsystem-level
->runtime_idle() callback and the result returned by it is not ignored. Namely,
if it returns error code, the subsystem-level ->runtime_idle() callback will not
be called and the helper function rpm_idle() executing it will return error
code. This mechanism is intended to help platforms where saving device state
is a time consuming operation and should only be carried out if all devices
in the power domain are idle, before turning off the shared power resource(s).
Namely, the power domain ->runtime_idle() callback may return error code until
the pm_runtime_idle() helper (or its asychronous version) has been called for
all devices in the power domain (it is recommended that the returned error code
be -EBUSY in those cases), preventing the subsystem-level ->runtime_idle()
callback from being run prematurely.

The support for device power domains is only relevant to platforms needing to
use the same subsystem-level (e.g. platform bus type) and device driver power
management callbacks in many different power domain configurations and wanting
to avoid incorporating the support for power domains into the subsystem-level
callbacks. The other platforms need not implement it or take it into account
in any way.


System Devices
--------------
System devices (sysdevs) follow a slightly different API, which can be found in
Expand Down
37 changes: 37 additions & 0 deletions trunk/drivers/base/power/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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, "");
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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);

Expand Down
19 changes: 17 additions & 2 deletions trunk/drivers/base/power/runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions trunk/include/linux/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ struct device {
void *platform_data; /* Platform specific data, device
core doesn't touch it */
struct dev_pm_info power;
struct dev_power_domain *pwr_domain;

#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
Expand Down
8 changes: 8 additions & 0 deletions trunk/include/linux/pm.h
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,14 @@ struct dev_pm_info {

extern void update_pm_runtime_accounting(struct device *dev);

/*
* Power domains provide callbacks that are executed during system suspend,
* hibernation, system resume and during runtime PM transitions along with
* subsystem-level and driver-level callbacks.
*/
struct dev_power_domain {
struct dev_pm_ops ops;
};

/*
* The PM_EVENT_ messages are also used by drivers implementing the legacy
Expand Down

0 comments on commit 3b743f4

Please sign in to comment.