summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Mochel <mochel@segfault.osdl.org>2002-03-25 22:26:46 -0800
committerPatrick Mochel <mochel@segfault.osdl.org>2002-03-25 22:26:46 -0800
commita0df92a42472b636998d39ce0255d5efaf56b61b (patch)
tree153a2105c7abed262bb59400f91314982d349a28
parentfba46407ee71baf4f4d0ebc3156920bc0318cd1d (diff)
Add device_{suspend,resume,shutdown} calls.
-rw-r--r--drivers/base/Makefile4
-rw-r--r--drivers/base/power.c122
-rw-r--r--include/linux/device.h5
3 files changed, 129 insertions, 2 deletions
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index a6885490ffe0..1995637fa63f 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -1,7 +1,7 @@
O_TARGET := base.o
-obj-y := core.o sys.o interface.o fs.o
+obj-y := core.o sys.o interface.o fs.o power.o
-export-objs := core.o sys.o interface.o fs.o
+export-objs := $(obj-y)
include $(TOPDIR)/Rules.make
diff --git a/drivers/base/power.c b/drivers/base/power.c
index e69de29bb2d1..92633070f254 100644
--- a/drivers/base/power.c
+++ b/drivers/base/power.c
@@ -0,0 +1,122 @@
+/*
+ * power.c - power management functions for the device tree.
+ *
+ * Copyright (c) 2002 Patrick Mochel
+ * 2002 Open Source Development Lab
+ *
+ * Kai Germaschewski contributed to the list walking routines.
+ *
+ * FIXME: The suspend and shutdown walks are identical. The resume walk
+ * is simply walking the list backward. Anyway we can combine these (cleanly)?
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include "base.h"
+
+/**
+ * device_suspend - suspend all devices on the device tree
+ * @state: state we're entering
+ * @level: what stage of the suspend process we're at
+ *
+ * The entries in the global device list are inserted such that they're in a
+ * depth-first ordering. So, simply iterate over the list, and call the driver's
+ * suspend callback for each device.
+ */
+int device_suspend(u32 state, u32 level)
+{
+ struct device * dev;
+ struct device * prev = &device_root;
+ int error = 0;
+
+ get_device(prev);
+
+ spin_lock(&device_lock);
+ dev = g_list_to_dev(prev->g_list.next);
+ while(dev != &device_root && !error) {
+ get_device(dev);
+ spin_unlock(&device_lock);
+ put_device(prev);
+
+ if (dev->driver && dev->driver->suspend)
+ error = dev->driver->suspend(dev,state,level);
+
+ spin_lock(&device_lock);
+ prev = dev;
+ dev = g_list_to_dev(prev->g_list.next);
+ }
+ spin_unlock(&device_root);
+ put_device(prev);
+
+ return error;
+}
+
+/**
+ * device_resume - resume all the devices in the system
+ * @level: stage of resume process we're at
+ *
+ * Similar to device_suspend above, though we want to do a breadth-first
+ * walk of the tree to make sure we wake up parents before children.
+ * So, we iterate over the list backward.
+ */
+void device_resume(u32 level)
+{
+ struct device * dev;
+ struct device * prev = &device_root;
+
+ get_device(prev);
+
+ spin_lock(&device_lock);
+ dev = g_list_to_dev(prev->g_list.prev);
+ while(dev != &device_root) {
+ get_device(dev);
+ spin_unlock(&device_lock);
+ put_device(prev);
+
+ if (dev->driver && dev->driver->resume)
+ dev->driver->resume(dev,level);
+
+ spin_lock(&device_lock);
+ prev = dev;
+ dev = g_list_to_dev(prev->g_list.prev);
+ }
+ spin_unlock(&device_root);
+ put_device(prev);
+}
+
+/**
+ * device_shutdown - queisce all the devices before reboot/shutdown
+ *
+ * Do depth first iteration over device tree, calling ->remove() for each
+ * device. This should ensure the devices are put into a sane state before
+ * we reboot the system.
+ *
+ */
+void device_shutdown(void)
+{
+ struct device * dev;
+ struct device * prev = &device_root;
+
+ get_device(prev);
+
+ spin_lock(&device_lock);
+ dev = g_list_to_dev(prev->g_list.next);
+ while(dev != &device_root) {
+ get_device(dev);
+ spin_unlock(&device_lock);
+ put_device(prev);
+
+ if (dev->driver && dev->driver->remove)
+ dev->driver->remove(dev,REMOVE_FREE_RESOURCES);
+
+ spin_lock(&device_lock);
+ prev = dev;
+ dev = g_list_to_dev(prev->g_list.next);
+ }
+ spin_unlock(&device_root);
+ put_device(prev);
+}
+
+EXPORT_SYMBOL(device_suspend);
+EXPORT_SYMBOL(device_resume);
+EXPORT_SYMBOL(device_shutdown);
diff --git a/include/linux/device.h b/include/linux/device.h
index 69797acdc546..b1858aa6a4ed 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -154,4 +154,9 @@ extern void put_device(struct device * dev);
extern int register_sys_device(struct device * dev);
extern void unregister_sys_device(struct device * dev);
+/* drivers/base/power.c */
+extern int device_suspend(u32 state, u32 level);
+extern void device_resume(u32 level);
+extern void device_shutdown(void);
+
#endif /* _DEVICE_H_ */