aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/power/disk.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/power/disk.c')
-rw-r--r--kernel/power/disk.c156
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
46static int hibernation_mode = HIBERNATION_SHUTDOWN; 46static int hibernation_mode = HIBERNATION_SHUTDOWN;
47 47
48static struct hibernation_ops *hibernation_ops; 48static 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
55void hibernation_set_ops(struct hibernation_ops *ops) 55void 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
78static 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
78static int platform_prepare(int platform_mode) 89static 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
100static 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
148int 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;