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