Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 181853
b: refs/heads/master
c: 5af84b8
h: refs/heads/master
i:
  181851: fc54414
v: v3
  • Loading branch information
Rafael J. Wysocki committed Feb 26, 2010
1 parent d87bf4c commit 2bf3a5f
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 7 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: 8cc6b39ff36b4bbce2d7471da088df122b0e9033
refs/heads/master: 5af84b82701a96be4b033aaa51d86c72e2ded061
115 changes: 109 additions & 6 deletions trunk/drivers/base/power/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <linux/resume-trace.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/async.h>

#include "../base.h"
#include "power.h"
Expand All @@ -42,6 +43,7 @@
LIST_HEAD(dpm_list);

static DEFINE_MUTEX(dpm_list_mtx);
static pm_message_t pm_transition;

/*
* Set once the preparation of devices for a PM transition has started, reset
Expand All @@ -56,6 +58,7 @@ static bool transition_started;
void device_pm_init(struct device *dev)
{
dev->power.status = DPM_ON;
init_completion(&dev->power.completion);
pm_runtime_init(dev);
}

Expand Down Expand Up @@ -111,6 +114,7 @@ void device_pm_remove(struct device *dev)
pr_debug("PM: Removing info for %s:%s\n",
dev->bus ? dev->bus->name : "No Bus",
kobject_name(&dev->kobj));
complete_all(&dev->power.completion);
mutex_lock(&dpm_list_mtx);
list_del_init(&dev->power.entry);
mutex_unlock(&dpm_list_mtx);
Expand Down Expand Up @@ -187,6 +191,31 @@ static void initcall_debug_report(struct device *dev, ktime_t calltime,
}
}

/**
* dpm_wait - Wait for a PM operation to complete.
* @dev: Device to wait for.
* @async: If unset, wait only if the device's power.async_suspend flag is set.
*/
static void dpm_wait(struct device *dev, bool async)
{
if (!dev)
return;

if (async || dev->power.async_suspend)
wait_for_completion(&dev->power.completion);
}

static int dpm_wait_fn(struct device *dev, void *async_ptr)
{
dpm_wait(dev, *((bool *)async_ptr));
return 0;
}

static void dpm_wait_for_children(struct device *dev, bool async)
{
device_for_each_child(dev, &async, dpm_wait_fn);
}

/**
* pm_op - Execute the PM operation appropriate for given PM event.
* @dev: Device to handle.
Expand Down Expand Up @@ -466,17 +495,19 @@ static int legacy_resume(struct device *dev, int (*cb)(struct device *dev))
}

/**
* device_resume - Execute "resume" callbacks for given device.
* __device_resume - Execute "resume" callbacks for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
* @async: If true, the device is being resumed asynchronously.
*/
static int device_resume(struct device *dev, pm_message_t state)
static int __device_resume(struct device *dev, pm_message_t state, bool async)
{
int error = 0;

TRACE_DEVICE(dev);
TRACE_RESUME(0);

dpm_wait(dev->parent, async);
down(&dev->sem);

if (dev->bus) {
Expand Down Expand Up @@ -511,11 +542,36 @@ static int device_resume(struct device *dev, pm_message_t state)
}
End:
up(&dev->sem);
complete_all(&dev->power.completion);

TRACE_RESUME(error);
return error;
}

static void async_resume(void *data, async_cookie_t cookie)
{
struct device *dev = (struct device *)data;
int error;

error = __device_resume(dev, pm_transition, true);
if (error)
pm_dev_err(dev, pm_transition, " async", error);
put_device(dev);
}

static int device_resume(struct device *dev)
{
INIT_COMPLETION(dev->power.completion);

if (dev->power.async_suspend && !pm_trace_is_enabled()) {
get_device(dev);
async_schedule(async_resume, dev);
return 0;
}

return __device_resume(dev, pm_transition, false);
}

/**
* dpm_resume - Execute "resume" callbacks for non-sysdev devices.
* @state: PM transition of the system being carried out.
Expand All @@ -530,6 +586,7 @@ static void dpm_resume(pm_message_t state)

INIT_LIST_HEAD(&list);
mutex_lock(&dpm_list_mtx);
pm_transition = state;
while (!list_empty(&dpm_list)) {
struct device *dev = to_device(dpm_list.next);

Expand All @@ -540,7 +597,7 @@ static void dpm_resume(pm_message_t state)
dev->power.status = DPM_RESUMING;
mutex_unlock(&dpm_list_mtx);

error = device_resume(dev, state);
error = device_resume(dev);

mutex_lock(&dpm_list_mtx);
if (error)
Expand All @@ -555,6 +612,7 @@ static void dpm_resume(pm_message_t state)
}
list_splice(&list, &dpm_list);
mutex_unlock(&dpm_list_mtx);
async_synchronize_full();
dpm_show_time(starttime, state, NULL);
}

Expand Down Expand Up @@ -732,17 +790,24 @@ static int legacy_suspend(struct device *dev, pm_message_t state,
return error;
}

static int async_error;

/**
* device_suspend - Execute "suspend" callbacks for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
* @async: If true, the device is being suspended asynchronously.
*/
static int device_suspend(struct device *dev, pm_message_t state)
static int __device_suspend(struct device *dev, pm_message_t state, bool async)
{
int error = 0;

dpm_wait_for_children(dev, async);
down(&dev->sem);

if (async_error)
goto End;

if (dev->class) {
if (dev->class->pm) {
pm_dev_dbg(dev, state, "class ");
Expand Down Expand Up @@ -773,12 +838,44 @@ static int device_suspend(struct device *dev, pm_message_t state)
error = legacy_suspend(dev, state, dev->bus->suspend);
}
}

if (!error)
dev->power.status = DPM_OFF;

End:
up(&dev->sem);
complete_all(&dev->power.completion);

return error;
}

static void async_suspend(void *data, async_cookie_t cookie)
{
struct device *dev = (struct device *)data;
int error;

error = __device_suspend(dev, pm_transition, true);
if (error) {
pm_dev_err(dev, pm_transition, " async", error);
async_error = error;
}

put_device(dev);
}

static int device_suspend(struct device *dev)
{
INIT_COMPLETION(dev->power.completion);

if (dev->power.async_suspend) {
get_device(dev);
async_schedule(async_suspend, dev);
return 0;
}

return __device_suspend(dev, pm_transition, false);
}

/**
* dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices.
* @state: PM transition of the system being carried out.
Expand All @@ -791,27 +888,33 @@ static int dpm_suspend(pm_message_t state)

INIT_LIST_HEAD(&list);
mutex_lock(&dpm_list_mtx);
pm_transition = state;
async_error = 0;
while (!list_empty(&dpm_list)) {
struct device *dev = to_device(dpm_list.prev);

get_device(dev);
mutex_unlock(&dpm_list_mtx);

error = device_suspend(dev, state);
error = device_suspend(dev);

mutex_lock(&dpm_list_mtx);
if (error) {
pm_dev_err(dev, state, "", error);
put_device(dev);
break;
}
dev->power.status = DPM_OFF;
if (!list_empty(&dev->power.entry))
list_move(&dev->power.entry, &list);
put_device(dev);
if (async_error)
break;
}
list_splice(&list, dpm_list.prev);
mutex_unlock(&dpm_list_mtx);
async_synchronize_full();
if (!error)
error = async_error;
if (!error)
dpm_show_time(starttime, state, NULL);
return error;
Expand Down
6 changes: 6 additions & 0 deletions trunk/include/linux/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,12 @@ static inline int device_is_registered(struct device *dev)
return dev->kobj.state_in_sysfs;
}

static inline void device_enable_async_suspend(struct device *dev)
{
if (dev->power.status == DPM_ON)
dev->power.async_suspend = true;
}

void driver_init(void);

/*
Expand Down
3 changes: 3 additions & 0 deletions trunk/include/linux/pm.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <linux/timer.h>
#include <linux/completion.h>

/*
* Callbacks for platform drivers to implement.
Expand Down Expand Up @@ -412,9 +413,11 @@ struct dev_pm_info {
pm_message_t power_state;
unsigned int can_wakeup:1;
unsigned int should_wakeup:1;
unsigned async_suspend:1;
enum dpm_state status; /* Owned by the PM core */
#ifdef CONFIG_PM_SLEEP
struct list_head entry;
struct completion completion;
#endif
#ifdef CONFIG_PM_RUNTIME
struct timer_list suspend_timer;
Expand Down
7 changes: 7 additions & 0 deletions trunk/include/linux/resume-trace.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@

extern int pm_trace_enabled;

static inline int pm_trace_is_enabled(void)
{
return pm_trace_enabled;
}

struct device;
extern void set_trace_device(struct device *);
extern void generate_resume_trace(const void *tracedata, unsigned int user);
Expand All @@ -17,6 +22,8 @@ extern void generate_resume_trace(const void *tracedata, unsigned int user);

#else

static inline int pm_trace_is_enabled(void) { return 0; }

#define TRACE_DEVICE(dev) do { } while (0)
#define TRACE_RESUME(dev) do { } while (0)

Expand Down

0 comments on commit 2bf3a5f

Please sign in to comment.