Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 212371
b: refs/heads/master
c: 7490e44
h: refs/heads/master
i:
  212369: 94bd7f0
  212367: 03deb7c
v: v3
  • Loading branch information
Alan Stern authored and Rafael J. Wysocki committed Oct 16, 2010
1 parent ae4df20 commit 06e2674
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 9 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: 140a6c945211ee911dec776fafa52e03a7d7bb9a
refs/heads/master: 7490e44239e60293bca0c2663229050c36c660c2
37 changes: 37 additions & 0 deletions trunk/Documentation/power/runtime_pm.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Run-time Power Management Framework for I/O Devices

(C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
(C) 2010 Alan Stern <stern@rowland.harvard.edu>

1. Introduction

Expand Down Expand Up @@ -230,6 +231,11 @@ defined in include/linux/pm.h:
interface; it may only be modified with the help of the pm_runtime_allow()
and pm_runtime_forbid() helper functions

unsigned int no_callbacks;
- indicates that the device does not use the run-time PM callbacks (see
Section 8); it may be modified only by the pm_runtime_no_callbacks()
helper function

All of the above fields are members of the 'power' member of 'struct device'.

4. Run-time PM Device Helper Functions
Expand Down Expand Up @@ -349,6 +355,11 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h:
counter (used by the /sys/devices/.../power/control interface to
effectively prevent the device from being power managed at run time)

void pm_runtime_no_callbacks(struct device *dev);
- set the power.no_callbacks flag for the device and remove the run-time
PM attributes from /sys/devices/.../power (or prevent them from being
added when the device is registered)

It is safe to execute the following helper functions from interrupt context:

pm_request_idle()
Expand Down Expand Up @@ -524,3 +535,29 @@ poweroff and run-time suspend callback, and similarly for system resume, thaw,
restore, and run-time resume, can achieve this with the help of the
UNIVERSAL_DEV_PM_OPS macro defined in include/linux/pm.h (possibly setting its
last argument to NULL).

8. "No-Callback" Devices

Some "devices" are only logical sub-devices of their parent and cannot be
power-managed on their own. (The prototype example is a USB interface. Entire
USB devices can go into low-power mode or send wake-up requests, but neither is
possible for individual interfaces.) The drivers for these devices have no
need of run-time PM callbacks; if the callbacks did exist, ->runtime_suspend()
and ->runtime_resume() would always return 0 without doing anything else and
->runtime_idle() would always call pm_runtime_suspend().

Subsystems can tell the PM core about these devices by calling
pm_runtime_no_callbacks(). This should be done after the device structure is
initialized and before it is registered (although after device registration is
also okay). The routine will set the device's power.no_callbacks flag and
prevent the non-debugging run-time PM sysfs attributes from being created.

When power.no_callbacks is set, the PM core will not invoke the
->runtime_idle(), ->runtime_suspend(), or ->runtime_resume() callbacks.
Instead it will assume that suspends and resumes always succeed and that idle
devices should be suspended.

As a consequence, the PM core will never directly inform the device's subsystem
or driver about run-time power changes. Instead, the driver for the device's
parent must take responsibility for telling the device's driver when the
parent's power state changes.
1 change: 1 addition & 0 deletions trunk/drivers/base/power/power.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ static inline void device_pm_move_last(struct device *dev) {}

extern int dpm_sysfs_add(struct device *);
extern void dpm_sysfs_remove(struct device *);
extern void rpm_sysfs_remove(struct device *);

#else /* CONFIG_PM */

Expand Down
54 changes: 53 additions & 1 deletion trunk/drivers/base/power/runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
#include <linux/sched.h>
#include <linux/pm_runtime.h>
#include <linux/jiffies.h>
#include "power.h"

static int rpm_resume(struct device *dev, int rpmflags);
static int rpm_suspend(struct device *dev, int rpmflags);

/**
* update_pm_runtime_accounting - Update the time accounting of power states
Expand Down Expand Up @@ -148,6 +150,12 @@ static int rpm_idle(struct device *dev, int rpmflags)
/* Pending requests need to be canceled. */
dev->power.request = RPM_REQ_NONE;

if (dev->power.no_callbacks) {
/* Assume ->runtime_idle() callback would have suspended. */
retval = rpm_suspend(dev, rpmflags);
goto out;
}

/* Carry out an asynchronous or a synchronous idle notification. */
if (rpmflags & RPM_ASYNC) {
dev->power.request = RPM_REQ_IDLE;
Expand Down Expand Up @@ -254,6 +262,10 @@ static int rpm_suspend(struct device *dev, int rpmflags)
goto repeat;
}

dev->power.deferred_resume = false;
if (dev->power.no_callbacks)
goto no_callback; /* Assume success. */

/* Carry out an asynchronous or a synchronous suspend. */
if (rpmflags & RPM_ASYNC) {
dev->power.request = RPM_REQ_SUSPEND;
Expand All @@ -265,7 +277,6 @@ static int rpm_suspend(struct device *dev, int rpmflags)
}

__update_runtime_status(dev, RPM_SUSPENDING);
dev->power.deferred_resume = false;

if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) {
spin_unlock_irq(&dev->power.lock);
Expand Down Expand Up @@ -305,6 +316,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
pm_runtime_cancel_pending(dev);
}
} else {
no_callback:
__update_runtime_status(dev, RPM_SUSPENDED);
pm_runtime_deactivate_timer(dev);

Expand Down Expand Up @@ -409,6 +421,23 @@ static int rpm_resume(struct device *dev, int rpmflags)
goto repeat;
}

/*
* See if we can skip waking up the parent. This is safe only if
* power.no_callbacks is set, because otherwise we don't know whether
* the resume will actually succeed.
*/
if (dev->power.no_callbacks && !parent && dev->parent) {
spin_lock(&dev->parent->power.lock);
if (dev->parent->power.disable_depth > 0
|| dev->parent->power.ignore_children
|| dev->parent->power.runtime_status == RPM_ACTIVE) {
atomic_inc(&dev->parent->power.child_count);
spin_unlock(&dev->parent->power.lock);
goto no_callback; /* Assume success. */
}
spin_unlock(&dev->parent->power.lock);
}

/* Carry out an asynchronous or a synchronous resume. */
if (rpmflags & RPM_ASYNC) {
dev->power.request = RPM_REQ_RESUME;
Expand Down Expand Up @@ -449,6 +478,9 @@ static int rpm_resume(struct device *dev, int rpmflags)
goto repeat;
}

if (dev->power.no_callbacks)
goto no_callback; /* Assume success. */

__update_runtime_status(dev, RPM_RESUMING);

if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) {
Expand Down Expand Up @@ -482,6 +514,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
__update_runtime_status(dev, RPM_SUSPENDED);
pm_runtime_cancel_pending(dev);
} else {
no_callback:
__update_runtime_status(dev, RPM_ACTIVE);
if (parent)
atomic_inc(&parent->power.child_count);
Expand Down Expand Up @@ -954,6 +987,25 @@ void pm_runtime_allow(struct device *dev)
}
EXPORT_SYMBOL_GPL(pm_runtime_allow);

/**
* pm_runtime_no_callbacks - Ignore run-time PM callbacks for a device.
* @dev: Device to handle.
*
* Set the power.no_callbacks flag, which tells the PM core that this
* device is power-managed through its parent and has no run-time PM
* callbacks of its own. The run-time sysfs attributes will be removed.
*
*/
void pm_runtime_no_callbacks(struct device *dev)
{
spin_lock_irq(&dev->power.lock);
dev->power.no_callbacks = 1;
spin_unlock_irq(&dev->power.lock);
if (device_is_registered(dev))
rpm_sysfs_remove(dev);
}
EXPORT_SYMBOL_GPL(pm_runtime_no_callbacks);

/**
* pm_runtime_init - Initialize run-time PM fields in given device object.
* @dev: Device object to initialize.
Expand Down
56 changes: 49 additions & 7 deletions trunk/drivers/base/power/sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@
static const char enabled[] = "enabled";
static const char disabled[] = "disabled";

const char power_group_name[] = "power";
EXPORT_SYMBOL_GPL(power_group_name);

#ifdef CONFIG_PM_RUNTIME
static const char ctrl_auto[] = "auto";
static const char ctrl_on[] = "on";
Expand Down Expand Up @@ -390,12 +393,6 @@ static DEVICE_ATTR(async, 0644, async_show, async_store);
#endif /* CONFIG_PM_ADVANCED_DEBUG */

static struct attribute * power_attrs[] = {
#ifdef CONFIG_PM_RUNTIME
&dev_attr_control.attr,
&dev_attr_runtime_status.attr,
&dev_attr_runtime_suspended_time.attr,
&dev_attr_runtime_active_time.attr,
#endif
&dev_attr_wakeup.attr,
#ifdef CONFIG_PM_SLEEP
&dev_attr_wakeup_count.attr,
Expand All @@ -409,6 +406,7 @@ static struct attribute * power_attrs[] = {
#ifdef CONFIG_PM_ADVANCED_DEBUG
&dev_attr_async.attr,
#ifdef CONFIG_PM_RUNTIME
&dev_attr_runtime_status.attr,
&dev_attr_runtime_usage.attr,
&dev_attr_runtime_active_kids.attr,
&dev_attr_runtime_enabled.attr,
Expand All @@ -417,10 +415,52 @@ static struct attribute * power_attrs[] = {
NULL,
};
static struct attribute_group pm_attr_group = {
.name = "power",
.name = power_group_name,
.attrs = power_attrs,
};

#ifdef CONFIG_PM_RUNTIME

static struct attribute *runtime_attrs[] = {
#ifndef CONFIG_PM_ADVANCED_DEBUG
&dev_attr_runtime_status.attr,
#endif
&dev_attr_control.attr,
&dev_attr_runtime_suspended_time.attr,
&dev_attr_runtime_active_time.attr,
NULL,
};
static struct attribute_group pm_runtime_attr_group = {
.name = power_group_name,
.attrs = runtime_attrs,
};

int dpm_sysfs_add(struct device *dev)
{
int rc;

rc = sysfs_create_group(&dev->kobj, &pm_attr_group);
if (rc == 0 && !dev->power.no_callbacks) {
rc = sysfs_merge_group(&dev->kobj, &pm_runtime_attr_group);
if (rc)
sysfs_remove_group(&dev->kobj, &pm_attr_group);
}
return rc;
}

void rpm_sysfs_remove(struct device *dev)
{
sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group);
}

void dpm_sysfs_remove(struct device *dev)
{
rpm_sysfs_remove(dev);
sysfs_remove_group(&dev->kobj, &pm_attr_group);
}

#else /* CONFIG_PM_RUNTIME */

int dpm_sysfs_add(struct device * dev)
{
return sysfs_create_group(&dev->kobj, &pm_attr_group);
Expand All @@ -430,3 +470,5 @@ void dpm_sysfs_remove(struct device * dev)
{
sysfs_remove_group(&dev->kobj, &pm_attr_group);
}

#endif
7 changes: 7 additions & 0 deletions trunk/include/linux/pm.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ extern void (*pm_power_off_prepare)(void);

struct device;

#ifdef CONFIG_PM
extern const char power_group_name[]; /* = "power" */
#else
#define power_group_name NULL
#endif

typedef struct pm_message {
int event;
} pm_message_t;
Expand Down Expand Up @@ -475,6 +481,7 @@ struct dev_pm_info {
unsigned int deferred_resume:1;
unsigned int run_wake:1;
unsigned int runtime_auto:1;
unsigned int no_callbacks:1;
enum rpm_request request;
enum rpm_status runtime_status;
int runtime_error;
Expand Down
2 changes: 2 additions & 0 deletions trunk/include/linux/pm_runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ extern void pm_runtime_forbid(struct device *dev);
extern int pm_generic_runtime_idle(struct device *dev);
extern int pm_generic_runtime_suspend(struct device *dev);
extern int pm_generic_runtime_resume(struct device *dev);
extern void pm_runtime_no_callbacks(struct device *dev);

static inline bool pm_children_suspended(struct device *dev)
{
Expand Down Expand Up @@ -110,6 +111,7 @@ static inline bool pm_runtime_suspended(struct device *dev) { return false; }
static inline int pm_generic_runtime_idle(struct device *dev) { return 0; }
static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; }
static inline int pm_generic_runtime_resume(struct device *dev) { return 0; }
static inline void pm_runtime_no_callbacks(struct device *dev) {}

#endif /* !CONFIG_PM_RUNTIME */

Expand Down

0 comments on commit 06e2674

Please sign in to comment.