diff options
Diffstat (limited to 'kernel/power')
-rw-r--r-- | kernel/power/disk.c | 184 | ||||
-rw-r--r-- | kernel/power/power.h | 5 | ||||
-rw-r--r-- | kernel/power/user.c | 96 |
3 files changed, 115 insertions, 170 deletions
diff --git a/kernel/power/disk.c b/kernel/power/disk.c index f445b9cd60fb..47882bfa610e 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c | |||
@@ -45,7 +45,7 @@ enum { | |||
45 | 45 | ||
46 | static int hibernation_mode = HIBERNATION_SHUTDOWN; | 46 | static int hibernation_mode = HIBERNATION_SHUTDOWN; |
47 | 47 | ||
48 | struct hibernation_ops *hibernation_ops; | 48 | static struct hibernation_ops *hibernation_ops; |
49 | 49 | ||
50 | /** | 50 | /** |
51 | * hibernation_set_ops - set the global hibernate operations | 51 | * hibernation_set_ops - set the global hibernate operations |
@@ -74,9 +74,9 @@ void hibernation_set_ops(struct hibernation_ops *ops) | |||
74 | * platform driver if so configured and return an error code if it fails | 74 | * platform driver if so configured and return an error code if it fails |
75 | */ | 75 | */ |
76 | 76 | ||
77 | static int platform_prepare(void) | 77 | static int platform_prepare(int platform_mode) |
78 | { | 78 | { |
79 | return (hibernation_mode == HIBERNATION_PLATFORM && hibernation_ops) ? | 79 | return (platform_mode && hibernation_ops) ? |
80 | hibernation_ops->prepare() : 0; | 80 | hibernation_ops->prepare() : 0; |
81 | } | 81 | } |
82 | 82 | ||
@@ -85,13 +85,104 @@ static int platform_prepare(void) | |||
85 | * using the platform driver (must be called after platform_prepare()) | 85 | * using the platform driver (must be called after platform_prepare()) |
86 | */ | 86 | */ |
87 | 87 | ||
88 | static void platform_finish(void) | 88 | static void platform_finish(int platform_mode) |
89 | { | 89 | { |
90 | if (hibernation_mode == HIBERNATION_PLATFORM && hibernation_ops) | 90 | if (platform_mode && hibernation_ops) |
91 | hibernation_ops->finish(); | 91 | hibernation_ops->finish(); |
92 | } | 92 | } |
93 | 93 | ||
94 | /** | 94 | /** |
95 | * hibernation_snapshot - quiesce devices and create the hibernation | ||
96 | * snapshot image. | ||
97 | * @platform_mode - if set, use the platform driver, if available, to | ||
98 | * prepare the platform frimware for the power transition. | ||
99 | * | ||
100 | * Must be called with pm_mutex held | ||
101 | */ | ||
102 | |||
103 | int hibernation_snapshot(int platform_mode) | ||
104 | { | ||
105 | int error; | ||
106 | |||
107 | /* Free memory before shutting down devices. */ | ||
108 | error = swsusp_shrink_memory(); | ||
109 | if (error) | ||
110 | goto Finish; | ||
111 | |||
112 | error = platform_prepare(platform_mode); | ||
113 | if (error) | ||
114 | goto Finish; | ||
115 | |||
116 | suspend_console(); | ||
117 | error = device_suspend(PMSG_FREEZE); | ||
118 | if (error) | ||
119 | goto Resume_devices; | ||
120 | |||
121 | error = disable_nonboot_cpus(); | ||
122 | if (!error) { | ||
123 | if (hibernation_mode != HIBERNATION_TEST) { | ||
124 | in_suspend = 1; | ||
125 | error = swsusp_suspend(); | ||
126 | /* Control returns here after successful restore */ | ||
127 | } else { | ||
128 | printk("swsusp debug: Waiting for 5 seconds.\n"); | ||
129 | mdelay(5000); | ||
130 | } | ||
131 | } | ||
132 | enable_nonboot_cpus(); | ||
133 | Resume_devices: | ||
134 | platform_finish(platform_mode); | ||
135 | device_resume(); | ||
136 | resume_console(); | ||
137 | Finish: | ||
138 | return error; | ||
139 | } | ||
140 | |||
141 | /** | ||
142 | * hibernation_restore - quiesce devices and restore the hibernation | ||
143 | * snapshot image. If successful, control returns in hibernation_snaphot() | ||
144 | * | ||
145 | * Must be called with pm_mutex held | ||
146 | */ | ||
147 | |||
148 | int hibernation_restore(void) | ||
149 | { | ||
150 | int error; | ||
151 | |||
152 | pm_prepare_console(); | ||
153 | suspend_console(); | ||
154 | error = device_suspend(PMSG_PRETHAW); | ||
155 | if (error) | ||
156 | goto Finish; | ||
157 | |||
158 | error = disable_nonboot_cpus(); | ||
159 | if (!error) | ||
160 | error = swsusp_resume(); | ||
161 | |||
162 | enable_nonboot_cpus(); | ||
163 | Finish: | ||
164 | device_resume(); | ||
165 | resume_console(); | ||
166 | pm_restore_console(); | ||
167 | return error; | ||
168 | } | ||
169 | |||
170 | /** | ||
171 | * hibernation_platform_enter - enter the hibernation state using the | ||
172 | * platform driver (if available) | ||
173 | */ | ||
174 | |||
175 | int hibernation_platform_enter(void) | ||
176 | { | ||
177 | if (hibernation_ops) { | ||
178 | kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); | ||
179 | return hibernation_ops->enter(); | ||
180 | } else { | ||
181 | return -ENOSYS; | ||
182 | } | ||
183 | } | ||
184 | |||
185 | /** | ||
95 | * power_down - Shut the machine down for hibernation. | 186 | * power_down - Shut the machine down for hibernation. |
96 | * | 187 | * |
97 | * Use the platform driver, if configured so; otherwise try | 188 | * Use the platform driver, if configured so; otherwise try |
@@ -111,11 +202,7 @@ static void power_down(void) | |||
111 | kernel_restart(NULL); | 202 | kernel_restart(NULL); |
112 | break; | 203 | break; |
113 | case HIBERNATION_PLATFORM: | 204 | case HIBERNATION_PLATFORM: |
114 | if (hibernation_ops) { | 205 | hibernation_platform_enter(); |
115 | kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); | ||
116 | hibernation_ops->enter(); | ||
117 | break; | ||
118 | } | ||
119 | } | 206 | } |
120 | kernel_halt(); | 207 | kernel_halt(); |
121 | /* | 208 | /* |
@@ -171,62 +258,17 @@ int hibernate(void) | |||
171 | mdelay(5000); | 258 | mdelay(5000); |
172 | goto Thaw; | 259 | goto Thaw; |
173 | } | 260 | } |
174 | 261 | error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM); | |
175 | /* Free memory before shutting down devices. */ | 262 | if (in_suspend && !error) { |
176 | error = swsusp_shrink_memory(); | ||
177 | if (error) | ||
178 | goto Thaw; | ||
179 | |||
180 | error = platform_prepare(); | ||
181 | if (error) | ||
182 | goto Thaw; | ||
183 | |||
184 | suspend_console(); | ||
185 | error = device_suspend(PMSG_FREEZE); | ||
186 | if (error) { | ||
187 | printk(KERN_ERR "PM: Some devices failed to suspend\n"); | ||
188 | goto Resume_devices; | ||
189 | } | ||
190 | error = disable_nonboot_cpus(); | ||
191 | if (error) | ||
192 | goto Enable_cpus; | ||
193 | |||
194 | if (hibernation_mode == HIBERNATION_TEST) { | ||
195 | printk("swsusp debug: Waiting for 5 seconds.\n"); | ||
196 | mdelay(5000); | ||
197 | goto Enable_cpus; | ||
198 | } | ||
199 | |||
200 | pr_debug("PM: snapshotting memory.\n"); | ||
201 | in_suspend = 1; | ||
202 | error = swsusp_suspend(); | ||
203 | if (error) | ||
204 | goto Enable_cpus; | ||
205 | |||
206 | if (in_suspend) { | ||
207 | enable_nonboot_cpus(); | ||
208 | platform_finish(); | ||
209 | device_resume(); | ||
210 | resume_console(); | ||
211 | pr_debug("PM: writing image.\n"); | 263 | pr_debug("PM: writing image.\n"); |
212 | error = swsusp_write(); | 264 | error = swsusp_write(); |
265 | swsusp_free(); | ||
213 | if (!error) | 266 | if (!error) |
214 | power_down(); | 267 | power_down(); |
215 | else { | ||
216 | swsusp_free(); | ||
217 | goto Thaw; | ||
218 | } | ||
219 | } else { | 268 | } else { |
220 | pr_debug("PM: Image restored successfully.\n"); | 269 | pr_debug("PM: Image restored successfully.\n"); |
270 | swsusp_free(); | ||
221 | } | 271 | } |
222 | |||
223 | swsusp_free(); | ||
224 | Enable_cpus: | ||
225 | enable_nonboot_cpus(); | ||
226 | Resume_devices: | ||
227 | platform_finish(); | ||
228 | device_resume(); | ||
229 | resume_console(); | ||
230 | Thaw: | 272 | Thaw: |
231 | mutex_unlock(&pm_mutex); | 273 | mutex_unlock(&pm_mutex); |
232 | unprepare_processes(); | 274 | unprepare_processes(); |
@@ -301,29 +343,11 @@ static int software_resume(void) | |||
301 | pr_debug("PM: Reading swsusp image.\n"); | 343 | pr_debug("PM: Reading swsusp image.\n"); |
302 | 344 | ||
303 | error = swsusp_read(); | 345 | error = swsusp_read(); |
304 | if (error) { | ||
305 | swsusp_free(); | ||
306 | goto Thaw; | ||
307 | } | ||
308 | |||
309 | pr_debug("PM: Preparing devices for restore.\n"); | ||
310 | |||
311 | suspend_console(); | ||
312 | error = device_suspend(PMSG_PRETHAW); | ||
313 | if (error) | ||
314 | goto Free; | ||
315 | |||
316 | error = disable_nonboot_cpus(); | ||
317 | if (!error) | 346 | if (!error) |
318 | swsusp_resume(); | 347 | hibernation_restore(); |
319 | 348 | ||
320 | enable_nonboot_cpus(); | ||
321 | Free: | ||
322 | swsusp_free(); | ||
323 | device_resume(); | ||
324 | resume_console(); | ||
325 | Thaw: | ||
326 | printk(KERN_ERR "PM: Restore failed, recovering.\n"); | 349 | printk(KERN_ERR "PM: Restore failed, recovering.\n"); |
350 | swsusp_free(); | ||
327 | unprepare_processes(); | 351 | unprepare_processes(); |
328 | Done: | 352 | Done: |
329 | free_basic_memory_bitmaps(); | 353 | free_basic_memory_bitmaps(); |
@@ -333,7 +357,7 @@ static int software_resume(void) | |||
333 | Unlock: | 357 | Unlock: |
334 | mutex_unlock(&pm_mutex); | 358 | mutex_unlock(&pm_mutex); |
335 | pr_debug("PM: Resume from disk failed.\n"); | 359 | pr_debug("PM: Resume from disk failed.\n"); |
336 | return 0; | 360 | return error; |
337 | } | 361 | } |
338 | 362 | ||
339 | late_initcall(software_resume); | 363 | late_initcall(software_resume); |
diff --git a/kernel/power/power.h b/kernel/power/power.h index 51381487103f..70c378b3f85a 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h | |||
@@ -25,7 +25,10 @@ struct swsusp_info { | |||
25 | */ | 25 | */ |
26 | #define SPARE_PAGES ((1024 * 1024) >> PAGE_SHIFT) | 26 | #define SPARE_PAGES ((1024 * 1024) >> PAGE_SHIFT) |
27 | 27 | ||
28 | extern struct hibernation_ops *hibernation_ops; | 28 | /* kernel/power/disk.c */ |
29 | extern int hibernation_snapshot(int platform_mode); | ||
30 | extern int hibernation_restore(void); | ||
31 | extern int hibernation_platform_enter(void); | ||
29 | #endif | 32 | #endif |
30 | 33 | ||
31 | extern int pfn_is_nosave(unsigned long); | 34 | extern int pfn_is_nosave(unsigned long); |
diff --git a/kernel/power/user.c b/kernel/power/user.c index 09468ec61124..bfed3b924093 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c | |||
@@ -128,83 +128,6 @@ static ssize_t snapshot_write(struct file *filp, const char __user *buf, | |||
128 | return res; | 128 | return res; |
129 | } | 129 | } |
130 | 130 | ||
131 | static inline int platform_prepare(void) | ||
132 | { | ||
133 | int error = 0; | ||
134 | |||
135 | if (hibernation_ops) | ||
136 | error = hibernation_ops->prepare(); | ||
137 | |||
138 | return error; | ||
139 | } | ||
140 | |||
141 | static inline void platform_finish(void) | ||
142 | { | ||
143 | if (hibernation_ops) | ||
144 | hibernation_ops->finish(); | ||
145 | } | ||
146 | |||
147 | static inline int snapshot_suspend(int platform_suspend) | ||
148 | { | ||
149 | int error; | ||
150 | |||
151 | mutex_lock(&pm_mutex); | ||
152 | /* Free memory before shutting down devices. */ | ||
153 | error = swsusp_shrink_memory(); | ||
154 | if (error) | ||
155 | goto Finish; | ||
156 | |||
157 | if (platform_suspend) { | ||
158 | error = platform_prepare(); | ||
159 | if (error) | ||
160 | goto Finish; | ||
161 | } | ||
162 | suspend_console(); | ||
163 | error = device_suspend(PMSG_FREEZE); | ||
164 | if (error) | ||
165 | goto Resume_devices; | ||
166 | |||
167 | error = disable_nonboot_cpus(); | ||
168 | if (!error) { | ||
169 | in_suspend = 1; | ||
170 | error = swsusp_suspend(); | ||
171 | } | ||
172 | enable_nonboot_cpus(); | ||
173 | Resume_devices: | ||
174 | if (platform_suspend) | ||
175 | platform_finish(); | ||
176 | |||
177 | device_resume(); | ||
178 | resume_console(); | ||
179 | Finish: | ||
180 | mutex_unlock(&pm_mutex); | ||
181 | return error; | ||
182 | } | ||
183 | |||
184 | static inline int snapshot_restore(void) | ||
185 | { | ||
186 | int error; | ||
187 | |||
188 | mutex_lock(&pm_mutex); | ||
189 | pm_prepare_console(); | ||
190 | suspend_console(); | ||
191 | error = device_suspend(PMSG_PRETHAW); | ||
192 | if (error) | ||
193 | goto Finish; | ||
194 | |||
195 | error = disable_nonboot_cpus(); | ||
196 | if (!error) | ||
197 | error = swsusp_resume(); | ||
198 | |||
199 | enable_nonboot_cpus(); | ||
200 | Finish: | ||
201 | device_resume(); | ||
202 | resume_console(); | ||
203 | pm_restore_console(); | ||
204 | mutex_unlock(&pm_mutex); | ||
205 | return error; | ||
206 | } | ||
207 | |||
208 | static int snapshot_ioctl(struct inode *inode, struct file *filp, | 131 | static int snapshot_ioctl(struct inode *inode, struct file *filp, |
209 | unsigned int cmd, unsigned long arg) | 132 | unsigned int cmd, unsigned long arg) |
210 | { | 133 | { |
@@ -251,7 +174,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp, | |||
251 | error = -EPERM; | 174 | error = -EPERM; |
252 | break; | 175 | break; |
253 | } | 176 | } |
254 | error = snapshot_suspend(data->platform_suspend); | 177 | error = hibernation_snapshot(data->platform_suspend); |
255 | if (!error) | 178 | if (!error) |
256 | error = put_user(in_suspend, (unsigned int __user *)arg); | 179 | error = put_user(in_suspend, (unsigned int __user *)arg); |
257 | if (!error) | 180 | if (!error) |
@@ -265,7 +188,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp, | |||
265 | error = -EPERM; | 188 | error = -EPERM; |
266 | break; | 189 | break; |
267 | } | 190 | } |
268 | error = snapshot_restore(); | 191 | error = hibernation_restore(); |
269 | break; | 192 | break; |
270 | 193 | ||
271 | case SNAPSHOT_FREE: | 194 | case SNAPSHOT_FREE: |
@@ -377,19 +300,14 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp, | |||
377 | switch (arg) { | 300 | switch (arg) { |
378 | 301 | ||
379 | case PMOPS_PREPARE: | 302 | case PMOPS_PREPARE: |
380 | if (hibernation_ops) { | 303 | data->platform_suspend = 1; |
381 | data->platform_suspend = 1; | 304 | error = 0; |
382 | error = 0; | ||
383 | } else { | ||
384 | error = -ENOSYS; | ||
385 | } | ||
386 | break; | 305 | break; |
387 | 306 | ||
388 | case PMOPS_ENTER: | 307 | case PMOPS_ENTER: |
389 | if (data->platform_suspend) { | 308 | if (data->platform_suspend) |
390 | kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); | 309 | error = hibernation_platform_enter(); |
391 | error = hibernation_ops->enter(); | 310 | |
392 | } | ||
393 | break; | 311 | break; |
394 | 312 | ||
395 | case PMOPS_FINISH: | 313 | case PMOPS_FINISH: |