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 at 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