Line data Source code
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_ops_list); 15 : static DEFINE_MUTEX(syscore_ops_lock); 16 : 17 : /** 18 : * register_syscore_ops - Register a set of system core operations. 19 : * @ops: System core operations to register. 20 : */ 21 3 : void register_syscore_ops(struct syscore_ops *ops) 22 : { 23 3 : mutex_lock(&syscore_ops_lock); 24 6 : list_add_tail(&ops->node, &syscore_ops_list); 25 3 : mutex_unlock(&syscore_ops_lock); 26 3 : } 27 : EXPORT_SYMBOL_GPL(register_syscore_ops); 28 : 29 : /** 30 : * unregister_syscore_ops - Unregister a set of system core operations. 31 : * @ops: System core operations to unregister. 32 : */ 33 0 : void unregister_syscore_ops(struct syscore_ops *ops) 34 : { 35 0 : mutex_lock(&syscore_ops_lock); 36 0 : list_del(&ops->node); 37 0 : mutex_unlock(&syscore_ops_lock); 38 0 : } 39 : EXPORT_SYMBOL_GPL(unregister_syscore_ops); 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 0 : int syscore_suspend(void) 48 : { 49 : struct syscore_ops *ops; 50 0 : int ret = 0; 51 : 52 0 : 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 0 : if (pm_wakeup_pending()) 57 : return -EBUSY; 58 : 59 0 : WARN_ONCE(!irqs_disabled(), 60 : "Interrupts enabled before system core suspend.\n"); 61 : 62 0 : list_for_each_entry_reverse(ops, &syscore_ops_list, node) 63 0 : if (ops->suspend) { 64 : pm_pr_dbg("Calling %pS\n", ops->suspend); 65 0 : ret = ops->suspend(); 66 0 : if (ret) 67 : goto err_out; 68 0 : WARN_ONCE(!irqs_disabled(), 69 : "Interrupts enabled after %pS\n", ops->suspend); 70 : } 71 : 72 : trace_suspend_resume(TPS("syscore_suspend"), 0, false); 73 : return 0; 74 : 75 : err_out: 76 0 : pr_err("PM: System core suspend callback %pS failed.\n", ops->suspend); 77 : 78 0 : list_for_each_entry_continue(ops, &syscore_ops_list, node) 79 0 : if (ops->resume) 80 0 : ops->resume(); 81 : 82 : return ret; 83 : } 84 : EXPORT_SYMBOL_GPL(syscore_suspend); 85 : 86 : /** 87 : * syscore_resume - Execute all the registered system core resume callbacks. 88 : * 89 : * This function is executed with one CPU on-line and disabled interrupts. 90 : */ 91 0 : void syscore_resume(void) 92 : { 93 : struct syscore_ops *ops; 94 : 95 0 : trace_suspend_resume(TPS("syscore_resume"), 0, true); 96 0 : WARN_ONCE(!irqs_disabled(), 97 : "Interrupts enabled before system core resume.\n"); 98 : 99 0 : list_for_each_entry(ops, &syscore_ops_list, node) 100 0 : if (ops->resume) { 101 : pm_pr_dbg("Calling %pS\n", ops->resume); 102 0 : ops->resume(); 103 0 : WARN_ONCE(!irqs_disabled(), 104 : "Interrupts enabled after %pS\n", ops->resume); 105 : } 106 0 : trace_suspend_resume(TPS("syscore_resume"), 0, false); 107 0 : } 108 : EXPORT_SYMBOL_GPL(syscore_resume); 109 : #endif /* CONFIG_PM_SLEEP */ 110 : 111 : /** 112 : * syscore_shutdown - Execute all the registered system core shutdown callbacks. 113 : */ 114 1 : void syscore_shutdown(void) 115 : { 116 : struct syscore_ops *ops; 117 : 118 1 : mutex_lock(&syscore_ops_lock); 119 : 120 4 : list_for_each_entry_reverse(ops, &syscore_ops_list, node) 121 3 : if (ops->shutdown) { 122 0 : if (initcall_debug) 123 0 : pr_info("PM: Calling %pS\n", ops->shutdown); 124 0 : ops->shutdown(); 125 : } 126 : 127 1 : mutex_unlock(&syscore_ops_lock); 128 1 : }