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