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