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#include <linux/wakeup_reason.h> 15 16static LIST_HEAD(syscore_ops_list); 17static DEFINE_MUTEX(syscore_ops_lock); 18 19/** 20 * register_syscore_ops - Register a set of system core operations. 21 * @ops: System core operations to register. 22 */ 23void register_syscore_ops(struct syscore_ops *ops) 24{ 25 mutex_lock(&syscore_ops_lock); 26 list_add_tail(&ops->node, &syscore_ops_list); 27 mutex_unlock(&syscore_ops_lock); 28} 29EXPORT_SYMBOL_GPL(register_syscore_ops); 30 31/** 32 * unregister_syscore_ops - Unregister a set of system core operations. 33 * @ops: System core operations to unregister. 34 */ 35void unregister_syscore_ops(struct syscore_ops *ops) 36{ 37 mutex_lock(&syscore_ops_lock); 38 list_del(&ops->node); 39 mutex_unlock(&syscore_ops_lock); 40} 41EXPORT_SYMBOL_GPL(unregister_syscore_ops); 42 43#ifdef CONFIG_PM_SLEEP 44/** 45 * syscore_suspend - Execute all the registered system core suspend callbacks. 46 * 47 * This function is executed with one CPU on-line and disabled interrupts. 48 */ 49int syscore_suspend(void) 50{ 51 struct syscore_ops *ops; 52 int ret = 0; 53 54 trace_suspend_resume(TPS("syscore_suspend"), 0, true); 55 pr_debug("Checking wakeup interrupts\n"); 56 57 /* Return error code if there are any wakeup interrupts pending. */ 58 if (pm_wakeup_pending()) 59 return -EBUSY; 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 log_suspend_abort_reason("System core suspend callback %pF failed", 80 ops->suspend); 81 pr_err("PM: System core suspend callback %pF failed.\n", ops->suspend); 82 83 list_for_each_entry_continue(ops, &syscore_ops_list, node) 84 if (ops->resume) 85 ops->resume(); 86 87 return ret; 88} 89EXPORT_SYMBOL_GPL(syscore_suspend); 90 91/** 92 * syscore_resume - Execute all the registered system core resume callbacks. 93 * 94 * This function is executed with one CPU on-line and disabled interrupts. 95 */ 96void syscore_resume(void) 97{ 98 struct syscore_ops *ops; 99 100 trace_suspend_resume(TPS("syscore_resume"), 0, true); 101 WARN_ONCE(!irqs_disabled(), 102 "Interrupts enabled before system core resume.\n"); 103 104 list_for_each_entry(ops, &syscore_ops_list, node) 105 if (ops->resume) { 106 if (initcall_debug) 107 pr_info("PM: Calling %pF\n", ops->resume); 108 ops->resume(); 109 WARN_ONCE(!irqs_disabled(), 110 "Interrupts enabled after %pF\n", ops->resume); 111 } 112 trace_suspend_resume(TPS("syscore_resume"), 0, false); 113} 114EXPORT_SYMBOL_GPL(syscore_resume); 115#endif /* CONFIG_PM_SLEEP */ 116 117/** 118 * syscore_shutdown - Execute all the registered system core shutdown callbacks. 119 */ 120void syscore_shutdown(void) 121{ 122 struct syscore_ops *ops; 123 124 mutex_lock(&syscore_ops_lock); 125 126 list_for_each_entry_reverse(ops, &syscore_ops_list, node) 127 if (ops->shutdown) { 128 if (initcall_debug) 129 pr_info("PM: Calling %pF\n", ops->shutdown); 130 ops->shutdown(); 131 } 132 133 mutex_unlock(&syscore_ops_lock); 134} 135