diff options
Diffstat (limited to 'kernel/power/disk.c')
-rw-r--r-- | kernel/power/disk.c | 184 |
1 files changed, 104 insertions, 80 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); |