0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/syscore_ops.h>
0009 #include <linux/mutex.h>
0010 #include <linux/module.h>
0011 #include <linux/suspend.h>
0012 #include <trace/events/power.h>
0013
0014 static LIST_HEAD(syscore_ops_list);
0015 static DEFINE_MUTEX(syscore_ops_lock);
0016
0017
0018
0019
0020
0021 void register_syscore_ops(struct syscore_ops *ops)
0022 {
0023 mutex_lock(&syscore_ops_lock);
0024 list_add_tail(&ops->node, &syscore_ops_list);
0025 mutex_unlock(&syscore_ops_lock);
0026 }
0027 EXPORT_SYMBOL_GPL(register_syscore_ops);
0028
0029
0030
0031
0032
0033 void unregister_syscore_ops(struct syscore_ops *ops)
0034 {
0035 mutex_lock(&syscore_ops_lock);
0036 list_del(&ops->node);
0037 mutex_unlock(&syscore_ops_lock);
0038 }
0039 EXPORT_SYMBOL_GPL(unregister_syscore_ops);
0040
0041 #ifdef CONFIG_PM_SLEEP
0042
0043
0044
0045
0046
0047 int syscore_suspend(void)
0048 {
0049 struct syscore_ops *ops;
0050 int ret = 0;
0051
0052 trace_suspend_resume(TPS("syscore_suspend"), 0, true);
0053 pm_pr_dbg("Checking wakeup interrupts\n");
0054
0055
0056 if (pm_wakeup_pending())
0057 return -EBUSY;
0058
0059 WARN_ONCE(!irqs_disabled(),
0060 "Interrupts enabled before system core suspend.\n");
0061
0062 list_for_each_entry_reverse(ops, &syscore_ops_list, node)
0063 if (ops->suspend) {
0064 pm_pr_dbg("Calling %pS\n", ops->suspend);
0065 ret = ops->suspend();
0066 if (ret)
0067 goto err_out;
0068 WARN_ONCE(!irqs_disabled(),
0069 "Interrupts enabled after %pS\n", ops->suspend);
0070 }
0071
0072 trace_suspend_resume(TPS("syscore_suspend"), 0, false);
0073 return 0;
0074
0075 err_out:
0076 pr_err("PM: System core suspend callback %pS failed.\n", ops->suspend);
0077
0078 list_for_each_entry_continue(ops, &syscore_ops_list, node)
0079 if (ops->resume)
0080 ops->resume();
0081
0082 return ret;
0083 }
0084 EXPORT_SYMBOL_GPL(syscore_suspend);
0085
0086
0087
0088
0089
0090
0091 void syscore_resume(void)
0092 {
0093 struct syscore_ops *ops;
0094
0095 trace_suspend_resume(TPS("syscore_resume"), 0, true);
0096 WARN_ONCE(!irqs_disabled(),
0097 "Interrupts enabled before system core resume.\n");
0098
0099 list_for_each_entry(ops, &syscore_ops_list, node)
0100 if (ops->resume) {
0101 pm_pr_dbg("Calling %pS\n", ops->resume);
0102 ops->resume();
0103 WARN_ONCE(!irqs_disabled(),
0104 "Interrupts enabled after %pS\n", ops->resume);
0105 }
0106 trace_suspend_resume(TPS("syscore_resume"), 0, false);
0107 }
0108 EXPORT_SYMBOL_GPL(syscore_resume);
0109 #endif
0110
0111
0112
0113
0114 void syscore_shutdown(void)
0115 {
0116 struct syscore_ops *ops;
0117
0118 mutex_lock(&syscore_ops_lock);
0119
0120 list_for_each_entry_reverse(ops, &syscore_ops_list, node)
0121 if (ops->shutdown) {
0122 if (initcall_debug)
0123 pr_info("PM: Calling %pS\n", ops->shutdown);
0124 ops->shutdown();
0125 }
0126
0127 mutex_unlock(&syscore_ops_lock);
0128 }