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 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 void register_syscore_ops(struct syscore_ops *ops) 22 { 23 mutex_lock(&syscore_ops_lock); 24 list_add_tail(&ops->node, &syscore_ops_list); 25 mutex_unlock(&syscore_ops_lock); 26 } 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 void unregister_syscore_ops(struct syscore_ops *ops) 34 { 35 mutex_lock(&syscore_ops_lock); 36 list_del(&ops->node); 37 mutex_unlock(&syscore_ops_lock); 38 } 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 int syscore_suspend(void) 48 { 49 struct syscore_ops *ops; 50 int ret = 0; 51 52 pr_debug("Checking wakeup interrupts\n"); 53 54 /* Return error code if there are any wakeup interrupts pending. */ 55 ret = check_wakeup_irqs(); 56 if (ret) 57 return ret; 58 59 WARN_ONCE(!irqs_disabled(), 60 "Interrupts enabled before system core suspend.\n"); 61 62 list_for_each_entry_reverse(ops, &syscore_ops_list, node) 63 if (ops->suspend) { 64 if (initcall_debug) 65 pr_info("PM: Calling %pF\n", ops->suspend); 66 ret = ops->suspend(); 67 if (ret) 68 goto err_out; 69 WARN_ONCE(!irqs_disabled(), 70 "Interrupts enabled after %pF\n", ops->suspend); 71 } 72 73 return 0; 74 75 err_out: 76 pr_err("PM: System core suspend callback %pF failed.\n", ops->suspend); 77 78 list_for_each_entry_continue(ops, &syscore_ops_list, node) 79 if (ops->resume) 80 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 void syscore_resume(void) 92 { 93 struct syscore_ops *ops; 94 95 WARN_ONCE(!irqs_disabled(), 96 "Interrupts enabled before system core resume.\n"); 97 98 list_for_each_entry(ops, &syscore_ops_list, node) 99 if (ops->resume) { 100 if (initcall_debug) 101 pr_info("PM: Calling %pF\n", ops->resume); 102 ops->resume(); 103 WARN_ONCE(!irqs_disabled(), 104 "Interrupts enabled after %pF\n", ops->resume); 105 } 106 } 107 EXPORT_SYMBOL_GPL(syscore_resume); 108 #endif /* CONFIG_PM_SLEEP */ 109 110 /** 111 * syscore_shutdown - Execute all the registered system core shutdown callbacks. 112 */ 113 void syscore_shutdown(void) 114 { 115 struct syscore_ops *ops; 116 117 mutex_lock(&syscore_ops_lock); 118 119 list_for_each_entry_reverse(ops, &syscore_ops_list, node) 120 if (ops->shutdown) { 121 if (initcall_debug) 122 pr_info("PM: Calling %pF\n", ops->shutdown); 123 ops->shutdown(); 124 } 125 126 mutex_unlock(&syscore_ops_lock); 127 } 128