Skip to content

Commit

Permalink
PM: Introduce core framework for run-time PM of I/O devices (rev. 17)
Browse files Browse the repository at this point in the history
Introduce a core framework for run-time power management of I/O
devices.  Add device run-time PM fields to 'struct dev_pm_info'
and device run-time PM callbacks to 'struct dev_pm_ops'.  Introduce
a run-time PM workqueue and define some device run-time PM helper
functions at the core level.  Document all these things.

Special thanks to Alan Stern for his help with the design and
multiple detailed reviews of the pereceding versions of this patch
and to Magnus Damm for testing feedback.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Magnus Damm <damm@igel.co.jp>
  • Loading branch information
Rafael J. Wysocki committed Aug 22, 2009
1 parent 8400146 commit 5e928f7
Showing 10 changed files with 1,689 additions and 11 deletions.
378 changes: 378 additions & 0 deletions Documentation/power/runtime_pm.txt

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions drivers/base/dd.c
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/async.h>
#include <linux/pm_runtime.h>

#include "base.h"
#include "power/power.h"
@@ -202,7 +203,10 @@ int driver_probe_device(struct device_driver *drv, struct device *dev)
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);

pm_runtime_get_noresume(dev);
pm_runtime_barrier(dev);
ret = really_probe(dev, drv);
pm_runtime_put_sync(dev);

return ret;
}
@@ -245,7 +249,9 @@ int device_attach(struct device *dev)
ret = 0;
}
} else {
pm_runtime_get_noresume(dev);
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
pm_runtime_put_sync(dev);
}
up(&dev->sem);
return ret;
@@ -306,6 +312,9 @@ static void __device_release_driver(struct device *dev)

drv = dev->driver;
if (drv) {
pm_runtime_get_noresume(dev);
pm_runtime_barrier(dev);

driver_sysfs_remove(dev);

if (dev->bus)
@@ -324,6 +333,8 @@ static void __device_release_driver(struct device *dev)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_UNBOUND_DRIVER,
dev);

pm_runtime_put_sync(dev);
}
}

1 change: 1 addition & 0 deletions drivers/base/power/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
obj-$(CONFIG_PM) += sysfs.o
obj-$(CONFIG_PM_SLEEP) += main.o
obj-$(CONFIG_PM_RUNTIME) += runtime.o
obj-$(CONFIG_PM_TRACE_RTC) += trace.o

ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
22 changes: 21 additions & 1 deletion drivers/base/power/main.c
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@
#include <linux/kallsyms.h>
#include <linux/mutex.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/resume-trace.h>
#include <linux/rwsem.h>
#include <linux/interrupt.h>
@@ -48,6 +49,16 @@ static DEFINE_MUTEX(dpm_list_mtx);
*/
static bool transition_started;

/**
* device_pm_init - Initialize the PM-related part of a device object
* @dev: Device object being initialized.
*/
void device_pm_init(struct device *dev)
{
dev->power.status = DPM_ON;
pm_runtime_init(dev);
}

/**
* device_pm_lock - lock the list of active devices used by the PM core
*/
@@ -105,6 +116,7 @@ void device_pm_remove(struct device *dev)
mutex_lock(&dpm_list_mtx);
list_del_init(&dev->power.entry);
mutex_unlock(&dpm_list_mtx);
pm_runtime_remove(dev);
}

/**
@@ -512,6 +524,7 @@ static void dpm_complete(pm_message_t state)
mutex_unlock(&dpm_list_mtx);

device_complete(dev, state);
pm_runtime_put_noidle(dev);

mutex_lock(&dpm_list_mtx);
}
@@ -757,7 +770,14 @@ static int dpm_prepare(pm_message_t state)
dev->power.status = DPM_PREPARING;
mutex_unlock(&dpm_list_mtx);

error = device_prepare(dev, state);
pm_runtime_get_noresume(dev);
if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) {
/* Wake-up requested during system sleep transition. */
pm_runtime_put_noidle(dev);
error = -EBUSY;
} else {
error = device_prepare(dev, state);
}

mutex_lock(&dpm_list_mtx);
if (error) {
31 changes: 24 additions & 7 deletions drivers/base/power/power.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
static inline void device_pm_init(struct device *dev)
{
dev->power.status = DPM_ON;
}
#ifdef CONFIG_PM_RUNTIME

extern void pm_runtime_init(struct device *dev);
extern void pm_runtime_remove(struct device *dev);

#else /* !CONFIG_PM_RUNTIME */

static inline void pm_runtime_init(struct device *dev) {}
static inline void pm_runtime_remove(struct device *dev) {}

#endif /* !CONFIG_PM_RUNTIME */

#ifdef CONFIG_PM_SLEEP

@@ -16,23 +23,33 @@ static inline struct device *to_device(struct list_head *entry)
return container_of(entry, struct device, power.entry);
}

extern void device_pm_init(struct device *dev);
extern void device_pm_add(struct device *);
extern void device_pm_remove(struct device *);
extern void device_pm_move_before(struct device *, struct device *);
extern void device_pm_move_after(struct device *, struct device *);
extern void device_pm_move_last(struct device *);

#else /* CONFIG_PM_SLEEP */
#else /* !CONFIG_PM_SLEEP */

static inline void device_pm_init(struct device *dev)
{
pm_runtime_init(dev);
}

static inline void device_pm_remove(struct device *dev)
{
pm_runtime_remove(dev);
}

static inline void device_pm_add(struct device *dev) {}
static inline void device_pm_remove(struct device *dev) {}
static inline void device_pm_move_before(struct device *deva,
struct device *devb) {}
static inline void device_pm_move_after(struct device *deva,
struct device *devb) {}
static inline void device_pm_move_last(struct device *dev) {}

#endif
#endif /* !CONFIG_PM_SLEEP */

#ifdef CONFIG_PM

Loading

0 comments on commit 5e928f7

Please sign in to comment.