PM: Introduce core framework for run-time PM of I/O devices (rev. 17)
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>
This commit is contained in:
@@ -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,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
|
||||
|
@@ -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) {
|
||||
|
@@ -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
|
||||
|
||||
|
1011
drivers/base/power/runtime.c
Normal file
1011
drivers/base/power/runtime.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user