diff options
Diffstat (limited to 'kernel/power/disk.c')
-rw-r--r-- | kernel/power/disk.c | 156 |
1 files changed, 130 insertions, 26 deletions
diff --git a/kernel/power/disk.c b/kernel/power/disk.c index eb72255b5c86..8b15f777010a 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c | |||
@@ -45,17 +45,18 @@ enum { | |||
45 | 45 | ||
46 | static int hibernation_mode = HIBERNATION_SHUTDOWN; | 46 | static int hibernation_mode = HIBERNATION_SHUTDOWN; |
47 | 47 | ||
48 | static struct hibernation_ops *hibernation_ops; | 48 | static struct platform_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 |
52 | * @ops: the hibernation operations to use in subsequent hibernation transitions | 52 | * @ops: the hibernation operations to use in subsequent hibernation transitions |
53 | */ | 53 | */ |
54 | 54 | ||
55 | void hibernation_set_ops(struct hibernation_ops *ops) | 55 | void hibernation_set_ops(struct platform_hibernation_ops *ops) |
56 | { | 56 | { |
57 | if (ops && !(ops->prepare && ops->enter && ops->finish | 57 | if (ops && !(ops->start && ops->pre_snapshot && ops->finish |
58 | && ops->pre_restore && ops->restore_cleanup)) { | 58 | && ops->prepare && ops->enter && ops->pre_restore |
59 | && ops->restore_cleanup)) { | ||
59 | WARN_ON(1); | 60 | WARN_ON(1); |
60 | return; | 61 | return; |
61 | } | 62 | } |
@@ -69,16 +70,37 @@ void hibernation_set_ops(struct hibernation_ops *ops) | |||
69 | mutex_unlock(&pm_mutex); | 70 | mutex_unlock(&pm_mutex); |
70 | } | 71 | } |
71 | 72 | ||
73 | /** | ||
74 | * platform_start - tell the platform driver that we're starting | ||
75 | * hibernation | ||
76 | */ | ||
77 | |||
78 | static int platform_start(int platform_mode) | ||
79 | { | ||
80 | return (platform_mode && hibernation_ops) ? | ||
81 | hibernation_ops->start() : 0; | ||
82 | } | ||
72 | 83 | ||
73 | /** | 84 | /** |
74 | * platform_prepare - prepare the machine for hibernation using the | 85 | * platform_pre_snapshot - prepare the machine for hibernation using the |
75 | * platform driver if so configured and return an error code if it fails | 86 | * platform driver if so configured and return an error code if it fails |
76 | */ | 87 | */ |
77 | 88 | ||
78 | static int platform_prepare(int platform_mode) | 89 | static int platform_pre_snapshot(int platform_mode) |
79 | { | 90 | { |
80 | return (platform_mode && hibernation_ops) ? | 91 | return (platform_mode && hibernation_ops) ? |
81 | hibernation_ops->prepare() : 0; | 92 | hibernation_ops->pre_snapshot() : 0; |
93 | } | ||
94 | |||
95 | /** | ||
96 | * platform_leave - prepare the machine for switching to the normal mode | ||
97 | * of operation using the platform driver (called with interrupts disabled) | ||
98 | */ | ||
99 | |||
100 | static void platform_leave(int platform_mode) | ||
101 | { | ||
102 | if (platform_mode && hibernation_ops) | ||
103 | hibernation_ops->leave(); | ||
82 | } | 104 | } |
83 | 105 | ||
84 | /** | 106 | /** |
@@ -118,6 +140,51 @@ static void platform_restore_cleanup(int platform_mode) | |||
118 | } | 140 | } |
119 | 141 | ||
120 | /** | 142 | /** |
143 | * create_image - freeze devices that need to be frozen with interrupts | ||
144 | * off, create the hibernation image and thaw those devices. Control | ||
145 | * reappears in this routine after a restore. | ||
146 | */ | ||
147 | |||
148 | int create_image(int platform_mode) | ||
149 | { | ||
150 | int error; | ||
151 | |||
152 | error = arch_prepare_suspend(); | ||
153 | if (error) | ||
154 | return error; | ||
155 | |||
156 | local_irq_disable(); | ||
157 | /* At this point, device_suspend() has been called, but *not* | ||
158 | * device_power_down(). We *must* call device_power_down() now. | ||
159 | * Otherwise, drivers for some devices (e.g. interrupt controllers) | ||
160 | * become desynchronized with the actual state of the hardware | ||
161 | * at resume time, and evil weirdness ensues. | ||
162 | */ | ||
163 | error = device_power_down(PMSG_FREEZE); | ||
164 | if (error) { | ||
165 | printk(KERN_ERR "Some devices failed to power down, " | ||
166 | KERN_ERR "aborting suspend\n"); | ||
167 | goto Enable_irqs; | ||
168 | } | ||
169 | |||
170 | save_processor_state(); | ||
171 | error = swsusp_arch_suspend(); | ||
172 | if (error) | ||
173 | printk(KERN_ERR "Error %d while creating the image\n", error); | ||
174 | /* Restore control flow magically appears here */ | ||
175 | restore_processor_state(); | ||
176 | if (!in_suspend) | ||
177 | platform_leave(platform_mode); | ||
178 | /* NOTE: device_power_up() is just a resume() for devices | ||
179 | * that suspended with irqs off ... no overall powerup. | ||
180 | */ | ||
181 | device_power_up(); | ||
182 | Enable_irqs: | ||
183 | local_irq_enable(); | ||
184 | return error; | ||
185 | } | ||
186 | |||
187 | /** | ||
121 | * hibernation_snapshot - quiesce devices and create the hibernation | 188 | * hibernation_snapshot - quiesce devices and create the hibernation |
122 | * snapshot image. | 189 | * snapshot image. |
123 | * @platform_mode - if set, use the platform driver, if available, to | 190 | * @platform_mode - if set, use the platform driver, if available, to |
@@ -135,12 +202,16 @@ int hibernation_snapshot(int platform_mode) | |||
135 | if (error) | 202 | if (error) |
136 | return error; | 203 | return error; |
137 | 204 | ||
205 | error = platform_start(platform_mode); | ||
206 | if (error) | ||
207 | return error; | ||
208 | |||
138 | suspend_console(); | 209 | suspend_console(); |
139 | error = device_suspend(PMSG_FREEZE); | 210 | error = device_suspend(PMSG_FREEZE); |
140 | if (error) | 211 | if (error) |
141 | goto Resume_console; | 212 | goto Resume_console; |
142 | 213 | ||
143 | error = platform_prepare(platform_mode); | 214 | error = platform_pre_snapshot(platform_mode); |
144 | if (error) | 215 | if (error) |
145 | goto Resume_devices; | 216 | goto Resume_devices; |
146 | 217 | ||
@@ -148,7 +219,7 @@ int hibernation_snapshot(int platform_mode) | |||
148 | if (!error) { | 219 | if (!error) { |
149 | if (hibernation_mode != HIBERNATION_TEST) { | 220 | if (hibernation_mode != HIBERNATION_TEST) { |
150 | in_suspend = 1; | 221 | in_suspend = 1; |
151 | error = swsusp_suspend(); | 222 | error = create_image(platform_mode); |
152 | /* Control returns here after successful restore */ | 223 | /* Control returns here after successful restore */ |
153 | } else { | 224 | } else { |
154 | printk("swsusp debug: Waiting for 5 seconds.\n"); | 225 | printk("swsusp debug: Waiting for 5 seconds.\n"); |
@@ -207,21 +278,50 @@ int hibernation_platform_enter(void) | |||
207 | { | 278 | { |
208 | int error; | 279 | int error; |
209 | 280 | ||
210 | if (hibernation_ops) { | 281 | if (!hibernation_ops) |
211 | kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); | 282 | return -ENOSYS; |
212 | /* | 283 | |
213 | * We have cancelled the power transition by running | 284 | /* |
214 | * hibernation_ops->finish() before saving the image, so we | 285 | * We have cancelled the power transition by running |
215 | * should let the firmware know that we're going to enter the | 286 | * hibernation_ops->finish() before saving the image, so we should let |
216 | * sleep state after all | 287 | * the firmware know that we're going to enter the sleep state after all |
217 | */ | 288 | */ |
218 | error = hibernation_ops->prepare(); | 289 | error = hibernation_ops->start(); |
219 | sysdev_shutdown(); | 290 | if (error) |
220 | if (!error) | 291 | return error; |
221 | error = hibernation_ops->enter(); | 292 | |
222 | } else { | 293 | suspend_console(); |
223 | error = -ENOSYS; | 294 | error = device_suspend(PMSG_SUSPEND); |
295 | if (error) | ||
296 | goto Resume_console; | ||
297 | |||
298 | error = hibernation_ops->prepare(); | ||
299 | if (error) | ||
300 | goto Resume_devices; | ||
301 | |||
302 | error = disable_nonboot_cpus(); | ||
303 | if (error) | ||
304 | goto Finish; | ||
305 | |||
306 | local_irq_disable(); | ||
307 | error = device_power_down(PMSG_SUSPEND); | ||
308 | if (!error) { | ||
309 | hibernation_ops->enter(); | ||
310 | /* We should never get here */ | ||
311 | while (1); | ||
224 | } | 312 | } |
313 | local_irq_enable(); | ||
314 | |||
315 | /* | ||
316 | * We don't need to reenable the nonboot CPUs or resume consoles, since | ||
317 | * the system is going to be halted anyway. | ||
318 | */ | ||
319 | Finish: | ||
320 | hibernation_ops->finish(); | ||
321 | Resume_devices: | ||
322 | device_resume(); | ||
323 | Resume_console: | ||
324 | resume_console(); | ||
225 | return error; | 325 | return error; |
226 | } | 326 | } |
227 | 327 | ||
@@ -238,14 +338,14 @@ static void power_down(void) | |||
238 | case HIBERNATION_TEST: | 338 | case HIBERNATION_TEST: |
239 | case HIBERNATION_TESTPROC: | 339 | case HIBERNATION_TESTPROC: |
240 | break; | 340 | break; |
241 | case HIBERNATION_SHUTDOWN: | ||
242 | kernel_power_off(); | ||
243 | break; | ||
244 | case HIBERNATION_REBOOT: | 341 | case HIBERNATION_REBOOT: |
245 | kernel_restart(NULL); | 342 | kernel_restart(NULL); |
246 | break; | 343 | break; |
247 | case HIBERNATION_PLATFORM: | 344 | case HIBERNATION_PLATFORM: |
248 | hibernation_platform_enter(); | 345 | hibernation_platform_enter(); |
346 | case HIBERNATION_SHUTDOWN: | ||
347 | kernel_power_off(); | ||
348 | break; | ||
249 | } | 349 | } |
250 | kernel_halt(); | 350 | kernel_halt(); |
251 | /* | 351 | /* |
@@ -298,6 +398,10 @@ int hibernate(void) | |||
298 | if (error) | 398 | if (error) |
299 | goto Exit; | 399 | goto Exit; |
300 | 400 | ||
401 | printk("Syncing filesystems ... "); | ||
402 | sys_sync(); | ||
403 | printk("done.\n"); | ||
404 | |||
301 | error = prepare_processes(); | 405 | error = prepare_processes(); |
302 | if (error) | 406 | if (error) |
303 | goto Finish; | 407 | goto Finish; |