aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/power
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2012-01-29 14:38:29 -0500
committerRafael J. Wysocki <rjw@sisk.pl>2012-01-29 14:38:29 -0500
commitcf579dfb82550e34de7ccf3ef090d8b834ccd3a9 (patch)
tree764ed72670c18c86d3eb9650025c56d661a31315 /kernel/power
parent181e9bdef37bfcaa41f3ab6c948a2a0d60a268b5 (diff)
PM / Sleep: Introduce "late suspend" and "early resume" of devices
The current device suspend/resume phases during system-wide power transitions appear to be insufficient for some platforms that want to use the same callback routines for saving device states and related operations during runtime suspend/resume as well as during system suspend/resume. In principle, they could point their .suspend_noirq() and .resume_noirq() to the same callback routines as their .runtime_suspend() and .runtime_resume(), respectively, but at least some of them require device interrupts to be enabled while the code in those routines is running. It also makes sense to have device suspend-resume callbacks that will be executed with runtime PM disabled and with device interrupts enabled in case someone needs to run some special code in that context during system-wide power transitions. Apart from this, .suspend_noirq() and .resume_noirq() were introduced as a workaround for drivers using shared interrupts and failing to prevent their interrupt handlers from accessing suspended hardware. It appears to be better not to use them for other porposes, or we may have to deal with some serious confusion (which seems to be happening already). For the above reasons, introduce new device suspend/resume phases, "late suspend" and "early resume" (and analogously for hibernation) whose callback will be executed with runtime PM disabled and with device interrupts enabled and whose callback pointers generally may point to runtime suspend/resume routines. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Reviewed-by: Kevin Hilman <khilman@ti.com>
Diffstat (limited to 'kernel/power')
-rw-r--r--kernel/power/hibernate.c24
-rw-r--r--kernel/power/main.c8
-rw-r--r--kernel/power/suspend.c4
3 files changed, 20 insertions, 16 deletions
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index 6d6d28870335..a5d4cf0aa03e 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -245,8 +245,8 @@ void swsusp_show_speed(struct timeval *start, struct timeval *stop,
245 * create_image - Create a hibernation image. 245 * create_image - Create a hibernation image.
246 * @platform_mode: Whether or not to use the platform driver. 246 * @platform_mode: Whether or not to use the platform driver.
247 * 247 *
248 * Execute device drivers' .freeze_noirq() callbacks, create a hibernation image 248 * Execute device drivers' "late" and "noirq" freeze callbacks, create a
249 * and execute the drivers' .thaw_noirq() callbacks. 249 * hibernation image and run the drivers' "noirq" and "early" thaw callbacks.
250 * 250 *
251 * Control reappears in this routine after the subsequent restore. 251 * Control reappears in this routine after the subsequent restore.
252 */ 252 */
@@ -254,7 +254,7 @@ static int create_image(int platform_mode)
254{ 254{
255 int error; 255 int error;
256 256
257 error = dpm_suspend_noirq(PMSG_FREEZE); 257 error = dpm_suspend_end(PMSG_FREEZE);
258 if (error) { 258 if (error) {
259 printk(KERN_ERR "PM: Some devices failed to power down, " 259 printk(KERN_ERR "PM: Some devices failed to power down, "
260 "aborting hibernation\n"); 260 "aborting hibernation\n");
@@ -306,7 +306,7 @@ static int create_image(int platform_mode)
306 Platform_finish: 306 Platform_finish:
307 platform_finish(platform_mode); 307 platform_finish(platform_mode);
308 308
309 dpm_resume_noirq(in_suspend ? 309 dpm_resume_start(in_suspend ?
310 (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); 310 (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
311 311
312 return error; 312 return error;
@@ -394,16 +394,16 @@ int hibernation_snapshot(int platform_mode)
394 * resume_target_kernel - Restore system state from a hibernation image. 394 * resume_target_kernel - Restore system state from a hibernation image.
395 * @platform_mode: Whether or not to use the platform driver. 395 * @platform_mode: Whether or not to use the platform driver.
396 * 396 *
397 * Execute device drivers' .freeze_noirq() callbacks, restore the contents of 397 * Execute device drivers' "noirq" and "late" freeze callbacks, restore the
398 * highmem that have not been restored yet from the image and run the low-level 398 * contents of highmem that have not been restored yet from the image and run
399 * code that will restore the remaining contents of memory and switch to the 399 * the low-level code that will restore the remaining contents of memory and
400 * just restored target kernel. 400 * switch to the just restored target kernel.
401 */ 401 */
402static int resume_target_kernel(bool platform_mode) 402static int resume_target_kernel(bool platform_mode)
403{ 403{
404 int error; 404 int error;
405 405
406 error = dpm_suspend_noirq(PMSG_QUIESCE); 406 error = dpm_suspend_end(PMSG_QUIESCE);
407 if (error) { 407 if (error) {
408 printk(KERN_ERR "PM: Some devices failed to power down, " 408 printk(KERN_ERR "PM: Some devices failed to power down, "
409 "aborting resume\n"); 409 "aborting resume\n");
@@ -460,7 +460,7 @@ static int resume_target_kernel(bool platform_mode)
460 Cleanup: 460 Cleanup:
461 platform_restore_cleanup(platform_mode); 461 platform_restore_cleanup(platform_mode);
462 462
463 dpm_resume_noirq(PMSG_RECOVER); 463 dpm_resume_start(PMSG_RECOVER);
464 464
465 return error; 465 return error;
466} 466}
@@ -518,7 +518,7 @@ int hibernation_platform_enter(void)
518 goto Resume_devices; 518 goto Resume_devices;
519 } 519 }
520 520
521 error = dpm_suspend_noirq(PMSG_HIBERNATE); 521 error = dpm_suspend_end(PMSG_HIBERNATE);
522 if (error) 522 if (error)
523 goto Resume_devices; 523 goto Resume_devices;
524 524
@@ -549,7 +549,7 @@ int hibernation_platform_enter(void)
549 Platform_finish: 549 Platform_finish:
550 hibernation_ops->finish(); 550 hibernation_ops->finish();
551 551
552 dpm_resume_noirq(PMSG_RESTORE); 552 dpm_resume_start(PMSG_RESTORE);
553 553
554 Resume_devices: 554 Resume_devices:
555 entering_platform_hibernation = false; 555 entering_platform_hibernation = false;
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 9824b41e5a18..8c5014a4e052 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -165,16 +165,20 @@ static int suspend_stats_show(struct seq_file *s, void *unused)
165 last_errno %= REC_FAILED_NUM; 165 last_errno %= REC_FAILED_NUM;
166 last_step = suspend_stats.last_failed_step + REC_FAILED_NUM - 1; 166 last_step = suspend_stats.last_failed_step + REC_FAILED_NUM - 1;
167 last_step %= REC_FAILED_NUM; 167 last_step %= REC_FAILED_NUM;
168 seq_printf(s, "%s: %d\n%s: %d\n%s: %d\n%s: %d\n" 168 seq_printf(s, "%s: %d\n%s: %d\n%s: %d\n%s: %d\n%s: %d\n"
169 "%s: %d\n%s: %d\n%s: %d\n%s: %d\n", 169 "%s: %d\n%s: %d\n%s: %d\n%s: %d\n%s: %d\n",
170 "success", suspend_stats.success, 170 "success", suspend_stats.success,
171 "fail", suspend_stats.fail, 171 "fail", suspend_stats.fail,
172 "failed_freeze", suspend_stats.failed_freeze, 172 "failed_freeze", suspend_stats.failed_freeze,
173 "failed_prepare", suspend_stats.failed_prepare, 173 "failed_prepare", suspend_stats.failed_prepare,
174 "failed_suspend", suspend_stats.failed_suspend, 174 "failed_suspend", suspend_stats.failed_suspend,
175 "failed_suspend_late",
176 suspend_stats.failed_suspend_late,
175 "failed_suspend_noirq", 177 "failed_suspend_noirq",
176 suspend_stats.failed_suspend_noirq, 178 suspend_stats.failed_suspend_noirq,
177 "failed_resume", suspend_stats.failed_resume, 179 "failed_resume", suspend_stats.failed_resume,
180 "failed_resume_early",
181 suspend_stats.failed_resume_early,
178 "failed_resume_noirq", 182 "failed_resume_noirq",
179 suspend_stats.failed_resume_noirq); 183 suspend_stats.failed_resume_noirq);
180 seq_printf(s, "failures:\n last_failed_dev:\t%-s\n", 184 seq_printf(s, "failures:\n last_failed_dev:\t%-s\n",
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 4fd51beed879..560a639614a1 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -147,7 +147,7 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
147 goto Platform_finish; 147 goto Platform_finish;
148 } 148 }
149 149
150 error = dpm_suspend_noirq(PMSG_SUSPEND); 150 error = dpm_suspend_end(PMSG_SUSPEND);
151 if (error) { 151 if (error) {
152 printk(KERN_ERR "PM: Some devices failed to power down\n"); 152 printk(KERN_ERR "PM: Some devices failed to power down\n");
153 goto Platform_finish; 153 goto Platform_finish;
@@ -189,7 +189,7 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
189 if (suspend_ops->wake) 189 if (suspend_ops->wake)
190 suspend_ops->wake(); 190 suspend_ops->wake();
191 191
192 dpm_resume_noirq(PMSG_RESUME); 192 dpm_resume_start(PMSG_RESUME);
193 193
194 Platform_finish: 194 Platform_finish:
195 if (suspend_ops->finish) 195 if (suspend_ops->finish)