[linux-pm] [RFC/PATCH 3/3] add class_devices to power management list

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Linux ACPI]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [CPU Freq]     [Kernel Newbies]     [Fedora Kernel]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux