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/interrupt.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 ret = check_wakeup_irqs(); 58 if (ret) 59 return ret; 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