summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/base/syscore.c73
-rw-r--r--include/linux/syscore_ops.h4
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);