xref: /openbmc/linux/drivers/base/syscore.c (revision cbecf716ca618fd44feda6bd9a64a8179d031fc5)
1989d42e8SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
240dc166cSRafael J. Wysocki /*
340dc166cSRafael J. Wysocki  *  syscore.c - Execution of system core operations.
440dc166cSRafael J. Wysocki  *
540dc166cSRafael J. Wysocki  *  Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
640dc166cSRafael J. Wysocki  */
740dc166cSRafael J. Wysocki 
840dc166cSRafael J. Wysocki #include <linux/syscore_ops.h>
940dc166cSRafael J. Wysocki #include <linux/mutex.h>
1040dc166cSRafael J. Wysocki #include <linux/module.h>
119ce7a258SThomas Gleixner #include <linux/suspend.h>
12bb3632c6STodd E Brandt #include <trace/events/power.h>
1340dc166cSRafael J. Wysocki 
1440dc166cSRafael J. Wysocki static LIST_HEAD(syscore_ops_list);
1540dc166cSRafael J. Wysocki static DEFINE_MUTEX(syscore_ops_lock);
1640dc166cSRafael J. Wysocki 
1740dc166cSRafael J. Wysocki /**
1840dc166cSRafael J. Wysocki  * register_syscore_ops - Register a set of system core operations.
1940dc166cSRafael J. Wysocki  * @ops: System core operations to register.
2040dc166cSRafael J. Wysocki  */
register_syscore_ops(struct syscore_ops * ops)2140dc166cSRafael J. Wysocki void register_syscore_ops(struct syscore_ops *ops)
2240dc166cSRafael J. Wysocki {
2340dc166cSRafael J. Wysocki 	mutex_lock(&syscore_ops_lock);
2440dc166cSRafael J. Wysocki 	list_add_tail(&ops->node, &syscore_ops_list);
2540dc166cSRafael J. Wysocki 	mutex_unlock(&syscore_ops_lock);
2640dc166cSRafael J. Wysocki }
2740dc166cSRafael J. Wysocki EXPORT_SYMBOL_GPL(register_syscore_ops);
2840dc166cSRafael J. Wysocki 
2940dc166cSRafael J. Wysocki /**
3040dc166cSRafael J. Wysocki  * unregister_syscore_ops - Unregister a set of system core operations.
3140dc166cSRafael J. Wysocki  * @ops: System core operations to unregister.
3240dc166cSRafael J. Wysocki  */
unregister_syscore_ops(struct syscore_ops * ops)3340dc166cSRafael J. Wysocki void unregister_syscore_ops(struct syscore_ops *ops)
3440dc166cSRafael J. Wysocki {
3540dc166cSRafael J. Wysocki 	mutex_lock(&syscore_ops_lock);
3640dc166cSRafael J. Wysocki 	list_del(&ops->node);
3740dc166cSRafael J. Wysocki 	mutex_unlock(&syscore_ops_lock);
3840dc166cSRafael J. Wysocki }
3940dc166cSRafael J. Wysocki EXPORT_SYMBOL_GPL(unregister_syscore_ops);
4040dc166cSRafael J. Wysocki 
4140dc166cSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
4240dc166cSRafael J. Wysocki /**
4340dc166cSRafael J. Wysocki  * syscore_suspend - Execute all the registered system core suspend callbacks.
4440dc166cSRafael J. Wysocki  *
4540dc166cSRafael J. Wysocki  * This function is executed with one CPU on-line and disabled interrupts.
4640dc166cSRafael J. Wysocki  */
syscore_suspend(void)4740dc166cSRafael J. Wysocki int syscore_suspend(void)
4840dc166cSRafael J. Wysocki {
4940dc166cSRafael J. Wysocki 	struct syscore_ops *ops;
5040dc166cSRafael J. Wysocki 	int ret = 0;
5140dc166cSRafael J. Wysocki 
52bb3632c6STodd E Brandt 	trace_suspend_resume(TPS("syscore_suspend"), 0, true);
53*81b14224SStephen Boyd 	pm_pr_dbg("Checking wakeup interrupts\n");
5488759622SColin Cross 
5588759622SColin Cross 	/* Return error code if there are any wakeup interrupts pending. */
569ce7a258SThomas Gleixner 	if (pm_wakeup_pending())
579ce7a258SThomas Gleixner 		return -EBUSY;
5888759622SColin Cross 
5940dc166cSRafael J. Wysocki 	WARN_ONCE(!irqs_disabled(),
6040dc166cSRafael J. Wysocki 		"Interrupts enabled before system core suspend.\n");
6140dc166cSRafael J. Wysocki 
6240dc166cSRafael J. Wysocki 	list_for_each_entry_reverse(ops, &syscore_ops_list, node)
6340dc166cSRafael J. Wysocki 		if (ops->suspend) {
64*81b14224SStephen Boyd 			pm_pr_dbg("Calling %pS\n", ops->suspend);
6540dc166cSRafael J. Wysocki 			ret = ops->suspend();
6640dc166cSRafael J. Wysocki 			if (ret)
6740dc166cSRafael J. Wysocki 				goto err_out;
6840dc166cSRafael J. Wysocki 			WARN_ONCE(!irqs_disabled(),
69d75f773cSSakari Ailus 				"Interrupts enabled after %pS\n", ops->suspend);
7040dc166cSRafael J. Wysocki 		}
7140dc166cSRafael J. Wysocki 
72bb3632c6STodd E Brandt 	trace_suspend_resume(TPS("syscore_suspend"), 0, false);
7340dc166cSRafael J. Wysocki 	return 0;
7440dc166cSRafael J. Wysocki 
7540dc166cSRafael J. Wysocki  err_out:
76d75f773cSSakari Ailus 	pr_err("PM: System core suspend callback %pS failed.\n", ops->suspend);
7740dc166cSRafael J. Wysocki 
7840dc166cSRafael J. Wysocki 	list_for_each_entry_continue(ops, &syscore_ops_list, node)
7940dc166cSRafael J. Wysocki 		if (ops->resume)
8040dc166cSRafael J. Wysocki 			ops->resume();
8140dc166cSRafael J. Wysocki 
8240dc166cSRafael J. Wysocki 	return ret;
8340dc166cSRafael J. Wysocki }
8419234c08SRafael J. Wysocki EXPORT_SYMBOL_GPL(syscore_suspend);
8540dc166cSRafael J. Wysocki 
8640dc166cSRafael J. Wysocki /**
8740dc166cSRafael J. Wysocki  * syscore_resume - Execute all the registered system core resume callbacks.
8840dc166cSRafael J. Wysocki  *
8940dc166cSRafael J. Wysocki  * This function is executed with one CPU on-line and disabled interrupts.
9040dc166cSRafael J. Wysocki  */
syscore_resume(void)9140dc166cSRafael J. Wysocki void syscore_resume(void)
9240dc166cSRafael J. Wysocki {
9340dc166cSRafael J. Wysocki 	struct syscore_ops *ops;
9440dc166cSRafael J. Wysocki 
95bb3632c6STodd E Brandt 	trace_suspend_resume(TPS("syscore_resume"), 0, true);
9640dc166cSRafael J. Wysocki 	WARN_ONCE(!irqs_disabled(),
9740dc166cSRafael J. Wysocki 		"Interrupts enabled before system core resume.\n");
9840dc166cSRafael J. Wysocki 
9940dc166cSRafael J. Wysocki 	list_for_each_entry(ops, &syscore_ops_list, node)
10040dc166cSRafael J. Wysocki 		if (ops->resume) {
101*81b14224SStephen Boyd 			pm_pr_dbg("Calling %pS\n", ops->resume);
10240dc166cSRafael J. Wysocki 			ops->resume();
10340dc166cSRafael J. Wysocki 			WARN_ONCE(!irqs_disabled(),
104d75f773cSSakari Ailus 				"Interrupts enabled after %pS\n", ops->resume);
10540dc166cSRafael J. Wysocki 		}
106bb3632c6STodd E Brandt 	trace_suspend_resume(TPS("syscore_resume"), 0, false);
10740dc166cSRafael J. Wysocki }
10819234c08SRafael J. Wysocki EXPORT_SYMBOL_GPL(syscore_resume);
10940dc166cSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */
11040dc166cSRafael J. Wysocki 
11140dc166cSRafael J. Wysocki /**
11240dc166cSRafael J. Wysocki  * syscore_shutdown - Execute all the registered system core shutdown callbacks.
11340dc166cSRafael J. Wysocki  */
syscore_shutdown(void)11440dc166cSRafael J. Wysocki void syscore_shutdown(void)
11540dc166cSRafael J. Wysocki {
11640dc166cSRafael J. Wysocki 	struct syscore_ops *ops;
11740dc166cSRafael J. Wysocki 
11840dc166cSRafael J. Wysocki 	mutex_lock(&syscore_ops_lock);
11940dc166cSRafael J. Wysocki 
12040dc166cSRafael J. Wysocki 	list_for_each_entry_reverse(ops, &syscore_ops_list, node)
12140dc166cSRafael J. Wysocki 		if (ops->shutdown) {
12240dc166cSRafael J. Wysocki 			if (initcall_debug)
123d75f773cSSakari Ailus 				pr_info("PM: Calling %pS\n", ops->shutdown);
12440dc166cSRafael J. Wysocki 			ops->shutdown();
12540dc166cSRafael J. Wysocki 		}
12640dc166cSRafael J. Wysocki 
12740dc166cSRafael J. Wysocki 	mutex_unlock(&syscore_ops_lock);
12840dc166cSRafael J. Wysocki }
129