diff options
-rw-r--r-- | drivers/base/syscore.c | 73 | ||||
-rw-r--r-- | include/linux/syscore_ops.h | 4 |
2 files changed, 77 insertions, 0 deletions
diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c index 0240f01714a1..64465edb5e53 100644 --- a/drivers/base/syscore.c +++ b/drivers/base/syscore.c @@ -109,6 +109,79 @@ void syscore_resume(void) } } EXPORT_SYMBOL_GPL(syscore_resume); + +/** + * syscore_save - Execute all the registered system core save callbacks. + * + * This function is executed when going into deep idle state to save system + * context. + */ +int syscore_save(void) +{ + struct syscore_ops *ops; + int ret = 0; + + pr_debug("Checking wakeup interrupts\n"); + + /* Return error code if there are any wakeup interrupts pending. */ + ret = check_wakeup_irqs(); + if (ret) + return ret; + + WARN_ONCE(!irqs_disabled(), + "Interrupts enabled before system core suspend.\n"); + + list_for_each_entry_reverse(ops, &syscore_ops_list, node) + if (ops->save) { + if (initcall_debug) + pr_info("PM: Calling %pF\n", ops->save); + ret = ops->save(); + if (ret) + goto err_out; + WARN_ONCE(!irqs_disabled(), + "Interrupts enabled after %pF\n", ops->save); + } + + return 0; + + err_out: + pr_err("PM: System core save callback %pF failed.\n", ops->save); + + list_for_each_entry_continue(ops, &syscore_ops_list, node) + if (ops->restore) + ops->restore(); + + return ret; +} +EXPORT_SYMBOL_GPL(syscore_save); + +/** + * syscore_restore - Execute all the registered system core restore callbacks. + * + * This function is executed after resuming from deep idle state to restore + * system context. + */ +void syscore_restore(void) +{ + struct syscore_ops *ops; + + WARN_ONCE(!irqs_disabled(), + "Interrupts enabled before system core resume.\n"); + + list_for_each_entry(ops, &syscore_ops_list, node) + if (ops->restore) { + ops->restore(); + WARN_ONCE(!irqs_disabled(), + "Interrupts enabled after %pF\n", ops->restore); + } + if (initcall_debug) { + list_for_each_entry(ops, &syscore_ops_list, node) + if (ops->restore) { + pr_info("PM: Called %pF\n", ops->restore); + } + } +} +EXPORT_SYMBOL_GPL(syscore_restore); #endif /* CONFIG_PM_SLEEP */ /** diff --git a/include/linux/syscore_ops.h b/include/linux/syscore_ops.h index 27b3b0bc41a9..8b4a10f9393b 100644 --- a/include/linux/syscore_ops.h +++ b/include/linux/syscore_ops.h @@ -16,6 +16,8 @@ struct syscore_ops { int (*suspend)(void); void (*resume)(void); void (*shutdown)(void); + int (*save)(void); + void (*restore)(void); }; extern void register_syscore_ops(struct syscore_ops *ops); @@ -23,6 +25,8 @@ extern void unregister_syscore_ops(struct syscore_ops *ops); #ifdef CONFIG_PM_SLEEP extern int syscore_suspend(void); extern void syscore_resume(void); +extern int syscore_save(void); +extern void syscore_restore(void); #endif extern void syscore_shutdown(void); |