aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base/power/suspend.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/base/power/suspend.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/base/power/suspend.c')
-rw-r--r--drivers/base/power/suspend.c144
1 files changed, 144 insertions, 0 deletions
diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c
new file mode 100644
index 000000000000..a0b5cf689e63
--- /dev/null
+++ b/drivers/base/power/suspend.c
@@ -0,0 +1,144 @@
1/*
2 * suspend.c - Functions for putting devices to sleep.
3 *
4 * Copyright (c) 2003 Patrick Mochel
5 * Copyright (c) 2003 Open Source Development Labs
6 *
7 * This file is released under the GPLv2
8 *
9 */
10
11#include <linux/device.h>
12#include "power.h"
13
14extern int sysdev_suspend(pm_message_t state);
15
16/*
17 * The entries in the dpm_active list are in a depth first order, simply
18 * because children are guaranteed to be discovered after parents, and
19 * are inserted at the back of the list on discovery.
20 *
21 * All list on the suspend path are done in reverse order, so we operate
22 * on the leaves of the device tree (or forests, depending on how you want
23 * to look at it ;) first. As nodes are removed from the back of the list,
24 * they are inserted into the front of their destintation lists.
25 *
26 * Things are the reverse on the resume path - iterations are done in
27 * forward order, and nodes are inserted at the back of their destination
28 * lists. This way, the ancestors will be accessed before their descendents.
29 */
30
31
32/**
33 * suspend_device - Save state of one device.
34 * @dev: Device.
35 * @state: Power state device is entering.
36 */
37
38int suspend_device(struct device * dev, pm_message_t state)
39{
40 int error = 0;
41
42 dev_dbg(dev, "suspending\n");
43
44 dev->power.prev_state = dev->power.power_state;
45
46 if (dev->bus && dev->bus->suspend && !dev->power.power_state)
47 error = dev->bus->suspend(dev, state);
48
49 return error;
50}
51
52
53/**
54 * device_suspend - Save state and stop all devices in system.
55 * @state: Power state to put each device in.
56 *
57 * Walk the dpm_active list, call ->suspend() for each device, and move
58 * it to dpm_off.
59 * Check the return value for each. If it returns 0, then we move the
60 * the device to the dpm_off list. If it returns -EAGAIN, we move it to
61 * the dpm_off_irq list. If we get a different error, try and back out.
62 *
63 * If we hit a failure with any of the devices, call device_resume()
64 * above to bring the suspended devices back to life.
65 *
66 */
67
68int device_suspend(pm_message_t state)
69{
70 int error = 0;
71
72 down(&dpm_sem);
73 down(&dpm_list_sem);
74 while (!list_empty(&dpm_active) && error == 0) {
75 struct list_head * entry = dpm_active.prev;
76 struct device * dev = to_device(entry);
77
78 get_device(dev);
79 up(&dpm_list_sem);
80
81 error = suspend_device(dev, state);
82
83 down(&dpm_list_sem);
84
85 /* Check if the device got removed */
86 if (!list_empty(&dev->power.entry)) {
87 /* Move it to the dpm_off or dpm_off_irq list */
88 if (!error) {
89 list_del(&dev->power.entry);
90 list_add(&dev->power.entry, &dpm_off);
91 } else if (error == -EAGAIN) {
92 list_del(&dev->power.entry);
93 list_add(&dev->power.entry, &dpm_off_irq);
94 error = 0;
95 }
96 }
97 if (error)
98 printk(KERN_ERR "Could not suspend device %s: "
99 "error %d\n", kobject_name(&dev->kobj), error);
100 put_device(dev);
101 }
102 up(&dpm_list_sem);
103 if (error)
104 dpm_resume();
105 up(&dpm_sem);
106 return error;
107}
108
109EXPORT_SYMBOL_GPL(device_suspend);
110
111
112/**
113 * device_power_down - Shut down special devices.
114 * @state: Power state to enter.
115 *
116 * Walk the dpm_off_irq list, calling ->power_down() for each device that
117 * couldn't power down the device with interrupts enabled. When we're
118 * done, power down system devices.
119 */
120
121int device_power_down(pm_message_t state)
122{
123 int error = 0;
124 struct device * dev;
125
126 list_for_each_entry_reverse(dev, &dpm_off_irq, power.entry) {
127 if ((error = suspend_device(dev, state)))
128 break;
129 }
130 if (error)
131 goto Error;
132 if ((error = sysdev_suspend(state)))
133 goto Error;
134 Done:
135 return error;
136 Error:
137 printk(KERN_ERR "Could not power down device %s: "
138 "error %d\n", kobject_name(&dev->kobj), error);
139 dpm_power_up();
140 goto Done;
141}
142
143EXPORT_SYMBOL_GPL(device_power_down);
144