aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/base/power/resume.c28
-rw-r--r--drivers/base/power/suspend.c122
-rw-r--r--include/linux/device.h11
-rw-r--r--include/linux/pm.h1
-rw-r--r--kernel/power/main.c4
5 files changed, 130 insertions, 36 deletions
diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c
index 826093ef4c7e..48e3d49d7b65 100644
--- a/drivers/base/power/resume.c
+++ b/drivers/base/power/resume.c
@@ -38,13 +38,35 @@ int resume_device(struct device * dev)
38 dev_dbg(dev,"resuming\n"); 38 dev_dbg(dev,"resuming\n");
39 error = dev->bus->resume(dev); 39 error = dev->bus->resume(dev);
40 } 40 }
41 if (dev->class && dev->class->resume) {
42 dev_dbg(dev,"class resume\n");
43 error = dev->class->resume(dev);
44 }
41 up(&dev->sem); 45 up(&dev->sem);
42 TRACE_RESUME(error); 46 TRACE_RESUME(error);
43 return error; 47 return error;
44} 48}
45 49
46 50
51static int resume_device_early(struct device * dev)
52{
53 int error = 0;
47 54
55 TRACE_DEVICE(dev);
56 TRACE_RESUME(0);
57 if (dev->bus && dev->bus->resume_early) {
58 dev_dbg(dev,"EARLY resume\n");
59 error = dev->bus->resume_early(dev);
60 }
61 TRACE_RESUME(error);
62 return error;
63}
64
65/*
66 * Resume the devices that have either not gone through
67 * the late suspend, or that did go through it but also
68 * went through the early resume
69 */
48void dpm_resume(void) 70void dpm_resume(void)
49{ 71{
50 down(&dpm_list_sem); 72 down(&dpm_list_sem);
@@ -99,10 +121,8 @@ void dpm_power_up(void)
99 struct list_head * entry = dpm_off_irq.next; 121 struct list_head * entry = dpm_off_irq.next;
100 struct device * dev = to_device(entry); 122 struct device * dev = to_device(entry);
101 123
102 get_device(dev); 124 list_move_tail(entry, &dpm_off);
103 list_move_tail(entry, &dpm_active); 125 resume_device_early(dev);
104 resume_device(dev);
105 put_device(dev);
106 } 126 }
107} 127}
108 128
diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c
index 69509e02f703..10e8032aee1a 100644
--- a/drivers/base/power/suspend.c
+++ b/drivers/base/power/suspend.c
@@ -65,7 +65,19 @@ int suspend_device(struct device * dev, pm_message_t state)
65 65
66 dev->power.prev_state = dev->power.power_state; 66 dev->power.prev_state = dev->power.power_state;
67 67
68 if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) { 68 if (dev->class && dev->class->suspend && !dev->power.power_state.event) {
69 dev_dbg(dev, "class %s%s\n",
70 suspend_verb(state.event),
71 ((state.event == PM_EVENT_SUSPEND)
72 && device_may_wakeup(dev))
73 ? ", may wakeup"
74 : ""
75 );
76 error = dev->class->suspend(dev, state);
77 suspend_report_result(dev->class->suspend, error);
78 }
79
80 if (!error && dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
69 dev_dbg(dev, "%s%s\n", 81 dev_dbg(dev, "%s%s\n",
70 suspend_verb(state.event), 82 suspend_verb(state.event),
71 ((state.event == PM_EVENT_SUSPEND) 83 ((state.event == PM_EVENT_SUSPEND)
@@ -81,15 +93,74 @@ int suspend_device(struct device * dev, pm_message_t state)
81} 93}
82 94
83 95
96/*
97 * This is called with interrupts off, only a single CPU
98 * running. We can't do down() on a semaphore (and we don't
99 * need the protection)
100 */
101static int suspend_device_late(struct device *dev, pm_message_t state)
102{
103 int error = 0;
104
105 if (dev->power.power_state.event) {
106 dev_dbg(dev, "PM: suspend_late %d-->%d\n",
107 dev->power.power_state.event, state.event);
108 }
109
110 if (dev->bus && dev->bus->suspend_late && !dev->power.power_state.event) {
111 dev_dbg(dev, "LATE %s%s\n",
112 suspend_verb(state.event),
113 ((state.event == PM_EVENT_SUSPEND)
114 && device_may_wakeup(dev))
115 ? ", may wakeup"
116 : ""
117 );
118 error = dev->bus->suspend_late(dev, state);
119 suspend_report_result(dev->bus->suspend_late, error);
120 }
121 return error;
122}
123
124/**
125 * device_prepare_suspend - save state and prepare to suspend
126 *
127 * NOTE! Devices cannot detach at this point - not only do we
128 * hold the device list semaphores over the whole prepare, but
129 * the whole point is to do non-invasive preparatory work, not
130 * the actual suspend.
131 */
132int device_prepare_suspend(pm_message_t state)
133{
134 int error = 0;
135 struct device * dev;
136
137 down(&dpm_sem);
138 down(&dpm_list_sem);
139 list_for_each_entry_reverse(dev, &dpm_active, power.entry) {
140 if (!dev->bus || !dev->bus->suspend_prepare)
141 continue;
142 error = dev->bus->suspend_prepare(dev, state);
143 if (error)
144 break;
145 }
146 up(&dpm_list_sem);
147 up(&dpm_sem);
148 return error;
149}
150
84/** 151/**
85 * device_suspend - Save state and stop all devices in system. 152 * device_suspend - Save state and stop all devices in system.
86 * @state: Power state to put each device in. 153 * @state: Power state to put each device in.
87 * 154 *
88 * Walk the dpm_active list, call ->suspend() for each device, and move 155 * Walk the dpm_active list, call ->suspend() for each device, and move
89 * it to dpm_off. 156 * it to the dpm_off list.
90 * Check the return value for each. If it returns 0, then we move the 157 *
91 * the device to the dpm_off list. If it returns -EAGAIN, we move it to 158 * (For historical reasons, if it returns -EAGAIN, that used to mean
92 * the dpm_off_irq list. If we get a different error, try and back out. 159 * that the device would be called again with interrupts disabled.
160 * These days, we use the "suspend_late()" callback for that, so we
161 * print a warning and consider it an error).
162 *
163 * If we get a different error, try and back out.
93 * 164 *
94 * If we hit a failure with any of the devices, call device_resume() 165 * If we hit a failure with any of the devices, call device_resume()
95 * above to bring the suspended devices back to life. 166 * above to bring the suspended devices back to life.
@@ -115,39 +186,27 @@ int device_suspend(pm_message_t state)
115 186
116 /* Check if the device got removed */ 187 /* Check if the device got removed */
117 if (!list_empty(&dev->power.entry)) { 188 if (!list_empty(&dev->power.entry)) {
118 /* Move it to the dpm_off or dpm_off_irq list */ 189 /* Move it to the dpm_off list */
119 if (!error) 190 if (!error)
120 list_move(&dev->power.entry, &dpm_off); 191 list_move(&dev->power.entry, &dpm_off);
121 else if (error == -EAGAIN) {
122 list_move(&dev->power.entry, &dpm_off_irq);
123 error = 0;
124 }
125 } 192 }
126 if (error) 193 if (error)
127 printk(KERN_ERR "Could not suspend device %s: " 194 printk(KERN_ERR "Could not suspend device %s: "
128 "error %d\n", kobject_name(&dev->kobj), error); 195 "error %d%s\n",
196 kobject_name(&dev->kobj), error,
197 error == -EAGAIN ? " (please convert to suspend_late)" : "");
129 put_device(dev); 198 put_device(dev);
130 } 199 }
131 up(&dpm_list_sem); 200 up(&dpm_list_sem);
132 if (error) { 201 if (error)
133 /* we failed... before resuming, bring back devices from
134 * dpm_off_irq list back to main dpm_off list, we do want
135 * to call resume() on them, in case they partially suspended
136 * despite returning -EAGAIN
137 */
138 while (!list_empty(&dpm_off_irq)) {
139 struct list_head * entry = dpm_off_irq.next;
140 list_move(entry, &dpm_off);
141 }
142 dpm_resume(); 202 dpm_resume();
143 } 203
144 up(&dpm_sem); 204 up(&dpm_sem);
145 return error; 205 return error;
146} 206}
147 207
148EXPORT_SYMBOL_GPL(device_suspend); 208EXPORT_SYMBOL_GPL(device_suspend);
149 209
150
151/** 210/**
152 * device_power_down - Shut down special devices. 211 * device_power_down - Shut down special devices.
153 * @state: Power state to enter. 212 * @state: Power state to enter.
@@ -162,14 +221,17 @@ int device_power_down(pm_message_t state)
162 int error = 0; 221 int error = 0;
163 struct device * dev; 222 struct device * dev;
164 223
165 list_for_each_entry_reverse(dev, &dpm_off_irq, power.entry) { 224 while (!list_empty(&dpm_off)) {
166 if ((error = suspend_device(dev, state))) 225 struct list_head * entry = dpm_off.prev;
167 break; 226
227 dev = to_device(entry);
228 error = suspend_device_late(dev, state);
229 if (error)
230 goto Error;
231 list_move(&dev->power.entry, &dpm_off_irq);
168 } 232 }
169 if (error) 233
170 goto Error; 234 error = sysdev_suspend(state);
171 if ((error = sysdev_suspend(state)))
172 goto Error;
173 Done: 235 Done:
174 return error; 236 return error;
175 Error: 237 Error:
diff --git a/include/linux/device.h b/include/linux/device.h
index 8a648cd94fa9..b40be6fca6fa 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -51,8 +51,12 @@ struct bus_type {
51 int (*probe)(struct device * dev); 51 int (*probe)(struct device * dev);
52 int (*remove)(struct device * dev); 52 int (*remove)(struct device * dev);
53 void (*shutdown)(struct device * dev); 53 void (*shutdown)(struct device * dev);
54 int (*suspend)(struct device * dev, pm_message_t state); 54
55 int (*resume)(struct device * dev); 55 int (*suspend_prepare)(struct device * dev, pm_message_t state);
56 int (*suspend)(struct device * dev, pm_message_t state);
57 int (*suspend_late)(struct device * dev, pm_message_t state);
58 int (*resume_early)(struct device * dev);
59 int (*resume)(struct device * dev);
56}; 60};
57 61
58extern int bus_register(struct bus_type * bus); 62extern int bus_register(struct bus_type * bus);
@@ -154,6 +158,9 @@ struct class {
154 158
155 void (*release)(struct class_device *dev); 159 void (*release)(struct class_device *dev);
156 void (*class_release)(struct class *class); 160 void (*class_release)(struct class *class);
161
162 int (*suspend)(struct device *, pm_message_t state);
163 int (*resume)(struct device *);
157}; 164};
158 165
159extern int class_register(struct class *); 166extern int class_register(struct class *);
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 658c1b93d5bb..096fb6f754cf 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -190,6 +190,7 @@ extern void device_resume(void);
190extern suspend_disk_method_t pm_disk_mode; 190extern suspend_disk_method_t pm_disk_mode;
191 191
192extern int device_suspend(pm_message_t state); 192extern int device_suspend(pm_message_t state);
193extern int device_prepare_suspend(pm_message_t state);
193 194
194#define device_set_wakeup_enable(dev,val) \ 195#define device_set_wakeup_enable(dev,val) \
195 ((dev)->power.should_wakeup = !!(val)) 196 ((dev)->power.should_wakeup = !!(val))
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 6d295c776794..0c3ed6ac938e 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -57,6 +57,10 @@ static int suspend_prepare(suspend_state_t state)
57 if (!pm_ops || !pm_ops->enter) 57 if (!pm_ops || !pm_ops->enter)
58 return -EPERM; 58 return -EPERM;
59 59
60 error = device_prepare_suspend(PMSG_SUSPEND);
61 if (error)
62 return error;
63
60 pm_prepare_console(); 64 pm_prepare_console();
61 65
62 disable_nonboot_cpus(); 66 disable_nonboot_cpus();