1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * syscore.c - Execution of system core operations. 4 * 5 * Copyright (C) 2011 Rafael J. Wysocki <[email protected]>, Novell Inc. 6 * 7 * This file is released under the GPLv2. 8 */ 9 10 #include <linux/syscore_ops.h> 11 #include <linux/mutex.h> 12 #include <linux/module.h> 13 #include <linux/suspend.h> 14 #include <trace/events/power.h> 15 16 static LIST_HEAD(syscore_ops_list); 17 static DEFINE_MUTEX(syscore_ops_lock); 18 19 /** 20 * register_syscore_ops - Register a set of system core operations. 21 * @ops: System core operations to register. 22 */ 23 void register_syscore_ops(struct syscore_ops *ops) 24 { 25 mutex_lock(&syscore_ops_lock); 26 list_add_tail(&ops->node, &syscore_ops_list); 27 mutex_unlock(&syscore_ops_lock); 28 } 29 EXPORT_SYMBOL_GPL(register_syscore_ops); 30 31 /** 32 * unregister_syscore_ops - Unregister a set of system core operations. 33 * @ops: System core operations to unregister. 34 */ 35 void unregister_syscore_ops(struct syscore_ops *ops) 36 { 37 mutex_lock(&syscore_ops_lock); 38 list_del(&ops->node); 39 mutex_unlock(&syscore_ops_lock); 40 } 41 EXPORT_SYMBOL_GPL(unregister_syscore_ops); 42 43 #ifdef CONFIG_PM_SLEEP 44 /** 45 * syscore_suspend - Execute all the registered system core suspend callbacks. 46 * 47 * This function is executed with one CPU on-line and disabled interrupts. 48 */ 49 int syscore_suspend(void) 50 { 51 struct syscore_ops *ops; 52 int ret = 0; 53 54 trace_suspend_resume(TPS("syscore_suspend"), 0, true); 55 pr_debug("Checking wakeup interrupts\n"); 56 57 /* Return error code if there are any wakeup interrupts pending. */ 58 if (pm_wakeup_pending()) 59 return -EBUSY; 60 61 WARN_ONCE(!irqs_disabled(), 62 "Interrupts enabled before system core suspend.\n"); 63 64 list_for_each_entry_reverse(ops, &syscore_ops_list, node) 65 if (ops->suspend) { 66 if (initcall_debug) 67 pr_info("PM: Calling %pF\n", ops->suspend); 68 ret = ops->suspend(); 69 if (ret) 70 goto err_out; 71 WARN_ONCE(!irqs_disabled(), 72 "Interrupts enabled after %pF\n", ops->suspend); 73 } 74 75 trace_suspend_resume(TPS("syscore_suspend"), 0, false); 76 return 0; 77 78 err_out: 79 pr_err("PM: System core suspend callback %pF failed.\n", ops->suspend); 80 81 list_for_each_entry_continue(ops, &syscore_ops_list, node) 82 if (ops->resume) 83 ops->resume(); 84 85 return ret; 86 } 87 EXPORT_SYMBOL_GPL(syscore_suspend); 88 89 /** 90 * syscore_resume - Execute all the registered system core resume callbacks. 91 * 92 * This function is executed with one CPU on-line and disabled interrupts. 93 */ 94 void syscore_resume(void) 95 { 96 struct syscore_ops *ops; 97 98 trace_suspend_resume(TPS("syscore_resume"), 0, true); 99 WARN_ONCE(!irqs_disabled(), 100 "Interrupts enabled before system core resume.\n"); 101 102 list_for_each_entry(ops, &syscore_ops_list, node) 103 if (ops->resume) { 104 if (initcall_debug) 105 pr_info("PM: Calling %pF\n", ops->resume); 106 ops->resume(); 107 WARN_ONCE(!irqs_disabled(), 108 "Interrupts enabled after %pF\n", ops->resume); 109 } 110 trace_suspend_resume(TPS("syscore_resume"), 0, false); 111 } 112 EXPORT_SYMBOL_GPL(syscore_resume); 113 #endif /* CONFIG_PM_SLEEP */ 114 115 /** 116 * syscore_shutdown - Execute all the registered system core shutdown callbacks. 117 */ 118 void syscore_shutdown(void) 119 { 120 struct syscore_ops *ops; 121 122 mutex_lock(&syscore_ops_lock); 123 124 list_for_each_entry_reverse(ops, &syscore_ops_list, node) 125 if (ops->shutdown) { 126 if (initcall_debug) 127 pr_info("PM: Calling %pF\n", ops->shutdown); 128 ops->shutdown(); 129 } 130 131 mutex_unlock(&syscore_ops_lock); 132 } 133