* [RFC/PATCH 2/3] Allow objects other than "struct device" in pm list
2006-10-11 6:01 [RFC/PATCH 0/3] Integrating struct class_device into power management framework Dmitry Torokhov
2006-10-11 6:01 ` [RFC/PATCH 1/3] Use kobject_name() to access kobject names Dmitry Torokhov
@ 2006-10-11 6:01 ` Dmitry Torokhov
2007-02-28 18:24 ` David Brownell
2006-10-11 6:01 ` [RFC/PATCH 3/3] add class_devices to power management list Dmitry Torokhov
2 siblings, 1 reply; 8+ messages in thread
From: Dmitry Torokhov @ 2006-10-11 6:01 UTC (permalink / raw)
To: linux-pm; +Cc: Greg KH, linux-kernel, Andrew Morton, pavel, Kay Sievers
[-- Attachment #1: dpm-make-mutiobject.patch --]
[-- Type: text/plain, Size: 20037 bytes --]
PM: allow objects other than "struct device" in pm list
Any object with embedded dev_pm_info structure can be added to
ower management list and have its suspend/resume methods called
automatically by driver core.
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---
drivers/base/power/main.c | 69 ++++++++++++++++++++++++++--------
drivers/base/power/power.h | 23 ++++++++++-
drivers/base/power/resume.c | 61 +++++++++++++++++-------------
drivers/base/power/runtime.c | 30 ++++++++-------
drivers/base/power/suspend.c | 85 ++++++++++++++++++++++++-------------------
drivers/base/power/trace.c | 17 +++++---
drivers/pcmcia/ds.c | 8 ++--
include/linux/pm.h | 21 +++++++---
include/linux/resume-trace.h | 10 ++---
9 files changed, 207 insertions(+), 117 deletions(-)
Index: work/drivers/base/power/main.c
===================================================================
--- work.orig/drivers/base/power/main.c
+++ work/drivers/base/power/main.c
@@ -31,8 +31,8 @@ DECLARE_MUTEX(dpm_list_sem);
/**
* device_pm_set_parent - Specify power dependency.
- * @dev: Device who needs power.
- * @parent: Device that supplies power.
+ * @dpm: Object who needs power.
+ * @parent: Object that supplies power.
*
* This function is used to manually describe a power-dependency
* relationship. It may be used to specify a transversal relationship
@@ -42,26 +42,63 @@ DECLARE_MUTEX(dpm_list_sem);
* before the power dependent.
*/
-void device_pm_set_parent(struct device * dev, struct device * parent)
+void dpm_set_parent(struct dev_pm_info *dpm, struct dev_pm_info *parent)
{
- put_device(dev->power.pm_parent);
- dev->power.pm_parent = get_device(parent);
+ if (dpm->pm_parent)
+ kobject_put(dpm->pm_parent->pm_object);
+ if (parent)
+ kobject_get(parent->pm_object);
+ dpm->pm_parent = parent;
}
-EXPORT_SYMBOL_GPL(device_pm_set_parent);
+EXPORT_SYMBOL_GPL(dpm_set_parent);
-int device_pm_add(struct device * dev)
+int dpm_add_object(struct dev_pm_info *dpm)
{
+ down(&dpm_list_sem);
+ list_add_tail(&dpm->entry, &dpm_active);
+ up(&dpm_list_sem);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dpm_add_object);
+
+void dpm_delete_object(struct dev_pm_info *dpm)
+{
+ down(&dpm_list_sem);
+ dpm_set_parent(dpm, NULL);
+ list_del_init(&dpm->entry);
+ up(&dpm_list_sem);
+}
+EXPORT_SYMBOL_GPL(dpm_delete_object);
+
+static const struct dev_pm_ops device_pm_ops = {
+ .suspend = suspend_device,
+ .suspend_late = suspend_device_late,
+ .resume = resume_device,
+ .resume_early = resume_device_early,
+};
+
+int device_pm_add(struct device *dev)
+{
+ struct dev_pm_info *dpm = &dev->power;
int error;
pr_debug("PM: Adding info for %s:%s\n",
dev->bus ? dev->bus->name : "No Bus",
kobject_name(&dev->kobj));
- down(&dpm_list_sem);
- list_add_tail(&dev->power.entry, &dpm_active);
- device_pm_set_parent(dev, dev->parent);
- if ((error = dpm_sysfs_add(dev)))
- list_del(&dev->power.entry);
- up(&dpm_list_sem);
+
+ dpm->pm_object = &dev->kobj;
+ dpm->pm_ops = &device_pm_ops;
+ if (dev->parent)
+ dpm_set_parent(dpm, &dev->parent->power);
+ error = dpm_add_object(dpm);
+ if (error)
+ return error;
+
+ error = dpm_sysfs_add(dev);
+ if (error)
+ dpm_delete_object(dpm);
+
return error;
}
@@ -70,11 +107,9 @@ void device_pm_remove(struct device * de
pr_debug("PM: Removing info for %s:%s\n",
dev->bus ? dev->bus->name : "No Bus",
kobject_name(&dev->kobj));
- down(&dpm_list_sem);
+
dpm_sysfs_remove(dev);
- put_device(dev->power.pm_parent);
- list_del_init(&dev->power.entry);
- up(&dpm_list_sem);
+ dpm_delete_object(&dev->power);
}
Index: work/drivers/base/power/resume.c
===================================================================
--- work.orig/drivers/base/power/resume.c
+++ work/drivers/base/power/resume.c
@@ -20,26 +20,28 @@
*
*/
-int resume_device(struct device * dev)
+int resume_device(struct dev_pm_info *dpm)
{
int error = 0;
+ struct device *dev = container_of(dpm, struct device, power);
+ struct dev_pm_info *pm_parent = dpm->pm_parent;
- TRACE_DEVICE(dev);
+ TRACE_DEVICE(dpm);
TRACE_RESUME(0);
down(&dev->sem);
- if (dev->power.pm_parent
- && dev->power.pm_parent->power.power_state.event) {
- dev_err(dev, "PM: resume from %d, parent %s still %d\n",
- dev->power.power_state.event,
- dev->power.pm_parent->bus_id,
- dev->power.pm_parent->power.power_state.event);
+
+ if (pm_parent && pm_parent->power_state.event) {
+ pm_err(dpm, "resume from %d, parent %s still %d\n",
+ dpm->power_state.event,
+ kobject_name(pm_parent->pm_object),
+ pm_parent->power_state.event);
}
if (dev->bus && dev->bus->resume) {
- dev_dbg(dev,"resuming\n");
+ pm_dbg(dpm, "resuming\n");
error = dev->bus->resume(dev);
}
if (dev->class && dev->class->resume) {
- dev_dbg(dev,"class resume\n");
+ pm_dbg(dpm, "class resume\n");
error = dev->class->resume(dev);
}
up(&dev->sem);
@@ -47,12 +49,12 @@ int resume_device(struct device * dev)
return error;
}
-
-static int resume_device_early(struct device * dev)
+int resume_device_early(struct dev_pm_info *dpm)
{
int error = 0;
+ struct device *dev = container_of(dpm, struct device, power);
- TRACE_DEVICE(dev);
+ TRACE_DEVICE(dpm);
TRACE_RESUME(0);
if (dev->bus && dev->bus->resume_early) {
dev_dbg(dev,"EARLY resume\n");
@@ -62,6 +64,7 @@ static int resume_device_early(struct de
return error;
}
+
/*
* Resume the devices that have either not gone through
* the late suspend, or that did go through it but also
@@ -69,19 +72,21 @@ static int resume_device_early(struct de
*/
void dpm_resume(void)
{
- down(&dpm_list_sem);
- while(!list_empty(&dpm_off)) {
- struct list_head * entry = dpm_off.next;
- struct device * dev = to_device(entry);
+ struct dev_pm_info *dpm;
- get_device(dev);
- list_move_tail(entry, &dpm_active);
+ down(&dpm_list_sem);
+ while (!list_empty(&dpm_off)) {
+ dpm = list_entry(dpm_off.next, struct dev_pm_info, entry);
+ kobject_get(dpm->pm_object);
+ list_move_tail(&dpm->entry, &dpm_active);
up(&dpm_list_sem);
- if (!dev->power.prev_state.event)
- resume_device(dev);
+
+ if (!dpm->prev_state.event)
+ dpm->pm_ops->resume(dpm);
+
down(&dpm_list_sem);
- put_device(dev);
+ kobject_put(dpm->pm_object);
}
up(&dpm_list_sem);
}
@@ -118,12 +123,14 @@ EXPORT_SYMBOL_GPL(device_resume);
void dpm_power_up(void)
{
- while(!list_empty(&dpm_off_irq)) {
- struct list_head * entry = dpm_off_irq.next;
- struct device * dev = to_device(entry);
+ struct dev_pm_info *dpm;
+
+ while (!list_empty(&dpm_off_irq)) {
+ dpm = list_entry(dpm_off_irq.next, struct dev_pm_info, entry);
- list_move_tail(entry, &dpm_off);
- resume_device_early(dev);
+ list_move_tail(&dpm->entry, &dpm_off);
+ if (dpm->pm_ops->resume_early)
+ dpm->pm_ops->resume_early(dpm);
}
}
Index: work/drivers/base/power/suspend.c
===================================================================
--- work.orig/drivers/base/power/suspend.c
+++ work/drivers/base/power/suspend.c
@@ -46,28 +46,29 @@ static inline char *suspend_verb(u32 eve
* @state: Power state device is entering.
*/
-int suspend_device(struct device * dev, pm_message_t state)
+int suspend_device(struct dev_pm_info *dpm, pm_message_t state)
{
int error = 0;
+ struct device *dev = container_of(dpm, struct device, power);
+ struct dev_pm_info *dpm_parent = dpm->pm_parent;
down(&dev->sem);
- if (dev->power.power_state.event) {
- dev_dbg(dev, "PM: suspend %d-->%d\n",
- dev->power.power_state.event, state.event);
- }
- if (dev->power.pm_parent
- && dev->power.pm_parent->power.power_state.event) {
- dev_err(dev,
- "PM: suspend %d->%d, parent %s already %d\n",
- dev->power.power_state.event, state.event,
- dev->power.pm_parent->bus_id,
- dev->power.pm_parent->power.power_state.event);
- }
- dev->power.prev_state = dev->power.power_state;
+ if (dpm->power_state.event)
+ pm_dbg(dpm, "suspend %d-->%d\n",
+ dpm->power_state.event, state.event);
+
+ if (dpm_parent && dpm_parent->power_state.event)
+ pm_err(dpm,
+ "suspend %d->%d, parent %s already %d\n",
+ dpm->power_state.event, state.event,
+ kobject_name(dpm_parent->pm_object),
+ dpm_parent->power_state.event);
+
+ dpm->prev_state = dpm->power_state;
- if (dev->class && dev->class->suspend && !dev->power.power_state.event) {
- dev_dbg(dev, "class %s%s\n",
+ if (dev->class && dev->class->suspend && !dpm->power_state.event) {
+ pm_dbg(dpm, "class %s%s\n",
suspend_verb(state.event),
((state.event == PM_EVENT_SUSPEND)
&& device_may_wakeup(dev))
@@ -78,8 +79,8 @@ int suspend_device(struct device * dev,
suspend_report_result(dev->class->suspend, error);
}
- if (!error && dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
- dev_dbg(dev, "%s%s\n",
+ if (!error && dev->bus && dev->bus->suspend && !dpm->power_state.event) {
+ pm_dbg(dpm, "%s%s\n",
suspend_verb(state.event),
((state.event == PM_EVENT_SUSPEND)
&& device_may_wakeup(dev))
@@ -89,22 +90,23 @@ int suspend_device(struct device * dev,
error = dev->bus->suspend(dev, state);
suspend_report_result(dev->bus->suspend, error);
}
+
up(&dev->sem);
return error;
}
-
/*
* This is called with interrupts off, only a single CPU
* running. We can't do down() on a semaphore (and we don't
* need the protection)
*/
-static int suspend_device_late(struct device *dev, pm_message_t state)
+int suspend_device_late(struct dev_pm_info *dpm, pm_message_t state)
{
int error = 0;
+ struct device *dev = container_of(dpm, struct device, power);
if (dev->bus && dev->bus->suspend_late && !dev->power.power_state.event) {
- dev_dbg(dev, "LATE %s%s\n",
+ pm_dbg(dpm, "LATE %s%s\n",
suspend_verb(state.event),
((state.event == PM_EVENT_SUSPEND)
&& device_may_wakeup(dev))
@@ -139,34 +141,40 @@ static int suspend_device_late(struct de
int device_suspend(pm_message_t state)
{
int error = 0;
+ struct dev_pm_info *dpm;
might_sleep();
+
down(&dpm_sem);
down(&dpm_list_sem);
+
while (!list_empty(&dpm_active) && error == 0) {
- struct list_head * entry = dpm_active.prev;
- struct device * dev = to_device(entry);
- get_device(dev);
+ dpm = list_entry(dpm_active.prev, struct dev_pm_info, entry);
+ kobject_get(dpm->pm_object);
+
up(&dpm_list_sem);
- error = suspend_device(dev, state);
+ error = dpm->pm_ops->suspend(dpm, state);
down(&dpm_list_sem);
/* Check if the device got removed */
- if (!list_empty(&dev->power.entry)) {
+ if (!list_empty(&dpm->entry)) {
/* Move it to the dpm_off list */
if (!error)
- list_move(&dev->power.entry, &dpm_off);
+ list_move(&dpm->entry, &dpm_off);
}
+
if (error)
printk(KERN_ERR "Could not suspend device %s: "
"error %d%s\n",
- kobject_name(&dev->kobj), error,
+ kobject_name(dpm->pm_object), error,
error == -EAGAIN ? " (please convert to suspend_late)" : "");
- put_device(dev);
+
+ kobject_put(dpm->pm_object);
}
+
up(&dpm_list_sem);
if (error)
dpm_resume();
@@ -189,16 +197,19 @@ EXPORT_SYMBOL_GPL(device_suspend);
int device_power_down(pm_message_t state)
{
int error = 0;
- struct device * dev;
+ struct dev_pm_info *dpm;
while (!list_empty(&dpm_off)) {
- struct list_head * entry = dpm_off.prev;
- dev = to_device(entry);
- error = suspend_device_late(dev, state);
- if (error)
- goto Error;
- list_move(&dev->power.entry, &dpm_off_irq);
+ dpm = list_entry(dpm_off.prev, struct dev_pm_info, entry);
+
+ if (dpm->pm_ops->suspend_late) {
+ error = dpm->pm_ops->suspend_late(dpm, state);
+ if (error)
+ goto Error;
+ }
+
+ list_move(&dpm->entry, &dpm_off_irq);
}
error = sysdev_suspend(state);
@@ -206,7 +217,7 @@ int device_power_down(pm_message_t state
return error;
Error:
printk(KERN_ERR "Could not power down device %s: "
- "error %d\n", kobject_name(&dev->kobj), error);
+ "error %d\n", kobject_name(dpm->pm_object), error);
dpm_power_up();
goto Done;
}
Index: work/include/linux/pm.h
===================================================================
--- work.orig/include/linux/pm.h
+++ work/include/linux/pm.h
@@ -206,12 +206,21 @@ struct dev_pm_info {
#ifdef CONFIG_PM
unsigned should_wakeup:1;
pm_message_t prev_state;
- void * saved_state;
- struct device * pm_parent;
+ void *saved_state;
+ struct kobject *pm_object;
+ struct dev_pm_info *pm_parent;
struct list_head entry;
+ const struct dev_pm_ops *pm_ops;
#endif
};
+struct dev_pm_ops {
+ int (*suspend)(struct dev_pm_info *dpm, pm_message_t state);
+ int (*suspend_late)(struct dev_pm_info *dpm, pm_message_t state);
+ int (*resume_early)(struct dev_pm_info *dpm);
+ int (*resume)(struct dev_pm_info *dpm);
+};
+
extern void device_pm_set_parent(struct device * dev, struct device * parent);
extern int device_power_down(pm_message_t state);
@@ -229,8 +238,8 @@ extern int device_prepare_suspend(pm_mes
#define device_may_wakeup(dev) \
(device_can_wakeup(dev) && (dev)->power.should_wakeup)
-extern int dpm_runtime_suspend(struct device *, pm_message_t);
-extern void dpm_runtime_resume(struct device *);
+extern int dpm_runtime_suspend(struct dev_pm_info *, pm_message_t);
+extern void dpm_runtime_resume(struct dev_pm_info *);
extern void __suspend_report_result(const char *function, void *fn, int ret);
#define suspend_report_result(fn, ret) \
@@ -248,12 +257,12 @@ static inline int device_suspend(pm_mess
#define device_set_wakeup_enable(dev,val) do{}while(0)
#define device_may_wakeup(dev) (0)
-static inline int dpm_runtime_suspend(struct device * dev, pm_message_t state)
+static inline int dpm_runtime_suspend(struct dev_pm_indo *dpm, pm_message_t state)
{
return 0;
}
-static inline void dpm_runtime_resume(struct device * dev)
+static inline void dpm_runtime_resume(struct dev_pm_info *dpm)
{
}
Index: work/drivers/base/power/power.h
===================================================================
--- work.orig/drivers/base/power/power.h
+++ work/drivers/base/power/power.h
@@ -7,6 +7,23 @@ extern void device_shutdown(void);
#ifdef CONFIG_PM
+#ifdef DEBUG
+#define pm_dbg(dpm, format, arg...) \
+ printk(KERN_DEBUG "%s PM: " format, \
+ kobject_name(dpm->pm_object), ## arg)
+#else
+#define pm_dbg(dpm, format, arg...) do { (void)(dpm); } while (0)
+#endif
+
+#define pm_err(dpm, format, arg...) \
+ printk(KERN_ERR "%s PM: " format, \
+ kobject_name(dpm->pm_object), ## arg)
+
+#define pm_info(dpm, format, arg...) \
+ printk(KERN_INFO, "%s PM: " format, \
+ kobject_name(dpm->pm_object), ## arg)
+
+
/*
* main.c
*/
@@ -55,12 +72,14 @@ extern void dpm_sysfs_remove(struct devi
extern void dpm_resume(void);
extern void dpm_power_up(void);
-extern int resume_device(struct device *);
+extern int resume_device(struct dev_pm_info *);
+extern int resume_device_early(struct dev_pm_info *);
/*
* suspend.c
*/
-extern int suspend_device(struct device *, pm_message_t);
+extern int suspend_device(struct dev_pm_info *, pm_message_t);
+extern int suspend_device_late(struct dev_pm_info *, pm_message_t);
/*
Index: work/drivers/base/power/runtime.c
===================================================================
--- work.orig/drivers/base/power/runtime.c
+++ work/drivers/base/power/runtime.c
@@ -10,13 +10,15 @@
#include "power.h"
-static void runtime_resume(struct device * dev)
+static void runtime_resume(struct dev_pm_info *dpm)
{
- dev_dbg(dev, "resuming\n");
- if (!dev->power.power_state.event)
+ pm_dbg(dpm, "resuming\n");
+
+ if (!dpm->power_state.event)
return;
- if (!resume_device(dev))
- dev->power.power_state = PMSG_ON;
+
+ if (dpm->pm_ops->resume(dpm) == 0)
+ dpm->power_state = PMSG_ON;
}
@@ -30,10 +32,10 @@ static void runtime_resume(struct device
* FIXME: We need to handle devices that are in an unknown state.
*/
-void dpm_runtime_resume(struct device * dev)
+void dpm_runtime_resume(struct dev_pm_info *dpm)
{
down(&dpm_sem);
- runtime_resume(dev);
+ runtime_resume(dpm);
up(&dpm_sem);
}
EXPORT_SYMBOL(dpm_runtime_resume);
@@ -45,19 +47,21 @@ EXPORT_SYMBOL(dpm_runtime_resume);
* @state: State to enter.
*/
-int dpm_runtime_suspend(struct device * dev, pm_message_t state)
+int dpm_runtime_suspend(struct dev_pm_info *dpm, pm_message_t state)
{
int error = 0;
down(&dpm_sem);
- if (dev->power.power_state.event == state.event)
+
+ if (dpm->power_state.event == state.event)
goto Done;
- if (dev->power.power_state.event)
- runtime_resume(dev);
+ if (dpm->power_state.event)
+ runtime_resume(dpm);
- if (!(error = suspend_device(dev, state)))
- dev->power.power_state = state;
+ error = dpm->pm_ops->suspend(dpm, state);
+ if (!error)
+ dpm->power_state = state;
Done:
up(&dpm_sem);
return error;
Index: work/drivers/pcmcia/ds.c
===================================================================
--- work.orig/drivers/pcmcia/ds.c
+++ work/drivers/pcmcia/ds.c
@@ -968,9 +968,9 @@ static ssize_t pcmcia_store_pm_state(str
return -EINVAL;
if ((!p_dev->suspended) && !strncmp(buf, "off", 3))
- ret = dpm_runtime_suspend(dev, PMSG_SUSPEND);
+ ret = dpm_runtime_suspend(&dev->power, PMSG_SUSPEND);
else if (p_dev->suspended && !strncmp(buf, "on", 2))
- dpm_runtime_resume(dev);
+ dpm_runtime_resume(&dev->power);
return ret ? ret : count;
}
@@ -1095,7 +1095,7 @@ static int pcmcia_bus_suspend_callback(s
if (p_dev->socket != skt)
return 0;
- return dpm_runtime_suspend(dev, PMSG_SUSPEND);
+ return dpm_runtime_suspend(&dev->power, PMSG_SUSPEND);
}
static int pcmcia_bus_resume_callback(struct device *dev, void * _data)
@@ -1106,7 +1106,7 @@ static int pcmcia_bus_resume_callback(st
if (p_dev->socket != skt)
return 0;
- dpm_runtime_resume(dev);
+ dpm_runtime_resume(&dev->power);
return 0;
}
Index: work/drivers/base/power/trace.c
===================================================================
--- work.orig/drivers/base/power/trace.c
+++ work/drivers/base/power/trace.c
@@ -138,9 +138,10 @@ static unsigned int hash_string(unsigned
return seed % mod;
}
-void set_trace_device(struct device *dev)
+void set_trace_device(struct dev_pm_info *dpm)
{
- dev_hash_value = hash_string(DEVSEED, dev->bus_id, DEVHASH);
+ dev_hash_value = hash_string(DEVSEED, kobject_name(dpm->pm_object),
+ DEVHASH);
}
/*
@@ -185,13 +186,17 @@ static int show_file_hash(unsigned int v
static int show_dev_hash(unsigned int value)
{
int match = 0;
- struct list_head * entry = dpm_active.prev;
+ struct list_head *entry = dpm_active.prev;
+ struct dev_pm_info *dpm;
+ unsigned int hash;
while (entry != &dpm_active) {
- struct device * dev = to_device(entry);
- unsigned int hash = hash_string(DEVSEED, dev->bus_id, DEVHASH);
+ dpm = container_of(entry, struct dev_pm_info, entry);
+ hash = hash_string(DEVSEED, kobject_name(dpm->pm_object),
+ DEVHASH);
if (hash == value) {
- printk(" hash matches device %s\n", dev->bus_id);
+ printk(" hash matches device %s\n",
+ kobject_name(dpm->pm_object));
match++;
}
entry = entry->prev;
Index: work/include/linux/resume-trace.h
===================================================================
--- work.orig/include/linux/resume-trace.h
+++ work/include/linux/resume-trace.h
@@ -5,11 +5,11 @@
extern int pm_trace_enabled;
-struct device;
-extern void set_trace_device(struct device *);
+struct dev_pm_info;
+extern void set_trace_device(struct dev_pm_info *);
extern void generate_resume_trace(void *tracedata, unsigned int user);
-#define TRACE_DEVICE(dev) set_trace_device(dev)
+#define TRACE_DEVICE(dpm) set_trace_device(dpm)
#define TRACE_RESUME(user) do { \
if (pm_trace_enabled) { \
void *tracedata; \
@@ -26,8 +26,8 @@ extern void generate_resume_trace(void *
#else
-#define TRACE_DEVICE(dev) do { } while (0)
-#define TRACE_RESUME(dev) do { } while (0)
+#define TRACE_DEVICE(dpm) do { } while (0)
+#define TRACE_RESUME(user) do { } while (0)
#endif
^ permalink raw reply [flat|nested] 8+ messages in thread* [RFC/PATCH 3/3] add class_devices to power management list
2006-10-11 6:01 [RFC/PATCH 0/3] Integrating struct class_device into power management framework Dmitry Torokhov
2006-10-11 6:01 ` [RFC/PATCH 1/3] Use kobject_name() to access kobject names Dmitry Torokhov
2006-10-11 6:01 ` [RFC/PATCH 2/3] Allow objects other than "struct device" in pm list Dmitry Torokhov
@ 2006-10-11 6:01 ` Dmitry Torokhov
2 siblings, 0 replies; 8+ messages in thread
From: Dmitry Torokhov @ 2006-10-11 6:01 UTC (permalink / raw)
To: linux-pm; +Cc: Greg KH, linux-kernel, Andrew Morton, pavel, Kay Sievers
[-- Attachment #1: class_device-to-dpm-tree.patch --]
[-- Type: text/plain, Size: 7439 bytes --]
PM: add class_devices to power management list
pause() and restart() methods are added to struct class. These
methods are called for each class_device that belongs to class
that has them defined. pause() is called at suspend time and
restart() at resume time.
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---
drivers/base/class.c | 9 +++++++++
drivers/base/power/main.c | 33 +++++++++++++++++++++++++++++++++
drivers/base/power/power.h | 14 ++++++++++++++
drivers/base/power/resume.c | 31 +++++++++++++++++++++++++++++++
drivers/base/power/suspend.c | 42 ++++++++++++++++++++++++++++++++++++++++++
include/linux/device.h | 6 ++++++
6 files changed, 135 insertions(+)
Index: work/drivers/base/class.c
===================================================================
--- work.orig/drivers/base/class.c
+++ work/drivers/base/class.c
@@ -18,6 +18,7 @@
#include <linux/err.h>
#include <linux/slab.h>
#include "base.h"
+#include "power/power.h"
extern struct subsystem devices_subsys;
@@ -612,6 +613,10 @@ int class_device_add(struct class_device
if (error)
goto out8;
+ error = class_device_pm_add(class_dev);
+ if (error)
+ goto out9;
+
kobject_uevent(&class_dev->kobj, KOBJ_ADD);
/* notify any interfaces this device is now here */
@@ -625,6 +630,8 @@ int class_device_add(struct class_device
goto out1;
+ out9:
+ class_device_remove_groups(class_dev);
out8:
if (class_dev->dev)
sysfs_remove_link(&class_dev->kobj, class_name);
@@ -734,6 +741,8 @@ void class_device_del(struct class_devic
up(&parent_class->sem);
}
+ class_device_pm_remove(class_dev);
+
if (class_dev->dev) {
class_name = make_class_name(class_dev->class->name,
&class_dev->kobj);
Index: work/drivers/base/power/main.c
===================================================================
--- work.orig/drivers/base/power/main.c
+++ work/drivers/base/power/main.c
@@ -112,4 +112,37 @@ void device_pm_remove(struct device * de
dpm_delete_object(&dev->power);
}
+static const struct dev_pm_ops class_device_pm_ops = {
+ .suspend = suspend_class_device,
+ .resume = resume_class_device,
+};
+
+int class_device_pm_add(struct class_device *dev)
+{
+ int error;
+
+ pr_debug("PM: Adding info for %s:%s\n",
+ dev->class ? dev->class->name : "No Class",
+ kobject_name(&dev->kobj));
+
+ dev->power.pm_object = &dev->kobj;
+ dev->power.pm_ops = &class_device_pm_ops;
+ if (dev->parent)
+ dpm_set_parent(&dev->power, &dev->parent->power);
+ error = dpm_add_object(&dev->power);
+ if (error)
+ return error;
+
+ return error;
+}
+
+void class_device_pm_remove(struct class_device * dev)
+{
+ pr_debug("PM: Removing info for %s:%s\n",
+ dev->class ? dev->class->name : "No Class",
+ kobject_name(&dev->kobj));
+
+ dpm_delete_object(&dev->power);
+}
+
Index: work/drivers/base/power/power.h
===================================================================
--- work.orig/drivers/base/power/power.h
+++ work/drivers/base/power/power.h
@@ -59,6 +59,9 @@ static inline struct device * to_device(
extern int device_pm_add(struct device *);
extern void device_pm_remove(struct device *);
+extern int class_device_pm_add(struct class_device *);
+extern void class_device_pm_remove(struct class_device *);
+
/*
* sysfs.c
*/
@@ -74,12 +77,14 @@ extern void dpm_resume(void);
extern void dpm_power_up(void);
extern int resume_device(struct dev_pm_info *);
extern int resume_device_early(struct dev_pm_info *);
+extern int resume_class_device(struct dev_pm_info *);
/*
* suspend.c
*/
extern int suspend_device(struct dev_pm_info *, pm_message_t);
extern int suspend_device_late(struct dev_pm_info *, pm_message_t);
+extern int suspend_class_device(struct dev_pm_info *, pm_message_t);
/*
@@ -98,4 +103,13 @@ static inline void device_pm_remove(stru
}
+static inline int class_device_pm_add(struct class_device * dev)
+{
+ return 0;
+}
+static inline void class_device_pm_remove(struct class_device * dev)
+{
+
+}
+
#endif
Index: work/drivers/base/power/suspend.c
===================================================================
--- work.orig/drivers/base/power/suspend.c
+++ work/drivers/base/power/suspend.c
@@ -120,6 +120,48 @@ int suspend_device_late(struct dev_pm_in
}
/**
+ * suspend_class_device - Save state of one device.
+ * @dev: Device.
+ * @state: Power state device is entering.
+ */
+
+int suspend_class_device(struct dev_pm_info *dpm, pm_message_t state)
+{
+ int error = 0;
+ struct class_device *dev = container_of(dpm, struct class_device,
+ power);
+ struct dev_pm_info *dpm_parent;
+
+ if (dpm->power_state.event)
+ pm_dbg(dpm, "PM: suspend %d-->%d\n",
+ dpm->power_state.event, state.event);
+
+ dpm_parent = dev->power.pm_parent;
+ if (dpm_parent && dpm_parent->power_state.event)
+ pm_err(dpm, "PM: suspend %d->%d, parent %s already %d\n",
+ dpm->power_state.event, state.event,
+ kobject_name(dpm_parent->pm_object),
+ dpm_parent->power_state.event);
+
+ dpm->prev_state = dpm->power_state;
+
+ if (dev->class && dev->class->pause && !dpm->power_state.event) {
+ pm_dbg(dpm, "%s%s\n",
+ suspend_verb(state.event),
+ ((state.event == PM_EVENT_SUSPEND)
+ && device_may_wakeup(dev))
+ ? ", may wakeup"
+ : ""
+ );
+ error = dev->class->pause(dev);
+ suspend_report_result(dev->class->pause, error);
+ }
+
+ return error;
+}
+
+
+/**
* device_suspend - Save state and stop all devices in system.
* @state: Power state to put each device in.
*
Index: work/include/linux/device.h
===================================================================
--- work.orig/include/linux/device.h
+++ work/include/linux/device.h
@@ -171,6 +171,9 @@ struct class {
int (*suspend)(struct device *, pm_message_t state);
int (*resume)(struct device *);
+
+ int (*pause)(struct class_device *dev);
+ int (*restart)(struct class_device *dev);
};
extern int __must_check class_register(struct class *);
@@ -241,9 +244,12 @@ struct class_device {
struct class_device *parent; /* parent of this child device, if there is one */
struct attribute_group ** groups; /* optional groups */
+ struct dev_pm_info power;
+
void (*release)(struct class_device *dev);
int (*uevent)(struct class_device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);
+
char class_id[BUS_ID_SIZE]; /* unique to this class */
};
Index: work/drivers/base/power/resume.c
===================================================================
--- work.orig/drivers/base/power/resume.c
+++ work/drivers/base/power/resume.c
@@ -64,6 +64,37 @@ int resume_device_early(struct dev_pm_in
return error;
}
+/**
+ * resume_class_device - Restore state for one device.
+ * @dev: Device.
+ *
+ */
+
+int resume_class_device(struct dev_pm_info *dpm)
+{
+ int error = 0;
+ struct class_device *dev = container_of(dpm, struct class_device,
+ power);
+ struct dev_pm_info *pm_parent = dpm->pm_parent;
+
+ TRACE_DEVICE(dpm);
+ TRACE_RESUME(0);
+
+ if (pm_parent && pm_parent->power_state.event) {
+ pm_err(dpm, "resume from %d, parent %s still %d\n",
+ dpm->power_state.event,
+ kobject_name(pm_parent->pm_object),
+ pm_parent->power_state.event);
+ }
+
+ if (dev->class && dev->class->restart) {
+ pm_dbg(dpm, "class restart\n");
+ error = dev->class->restart(dev);
+ }
+
+ TRACE_RESUME(error);
+ return error;
+}
/*
* Resume the devices that have either not gone through
^ permalink raw reply [flat|nested] 8+ messages in thread