140dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki/*
240dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki *  syscore.c - Execution of system core operations.
340dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki *
440dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki *  Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
540dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki *
640dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki *  This file is released under the GPLv2.
740dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki */
840dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki
940dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki#include <linux/syscore_ops.h>
1040dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki#include <linux/mutex.h>
1140dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki#include <linux/module.h>
129ce7a25849e80cfb264f4995f832b932c1987e1aThomas Gleixner#include <linux/suspend.h>
13bb3632c6101b2fad07e6246721466b984b1e0e9dTodd E Brandt#include <trace/events/power.h>
14722c1106fc368dc7a0fd408f17f2576e0094641bRuchi Kandoi#include <linux/wakeup_reason.h>
1540dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki
1640dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysockistatic LIST_HEAD(syscore_ops_list);
1740dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysockistatic DEFINE_MUTEX(syscore_ops_lock);
1840dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki
1940dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki/**
2040dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki * register_syscore_ops - Register a set of system core operations.
2140dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki * @ops: System core operations to register.
2240dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki */
2340dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysockivoid register_syscore_ops(struct syscore_ops *ops)
2440dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki{
2540dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki	mutex_lock(&syscore_ops_lock);
2640dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki	list_add_tail(&ops->node, &syscore_ops_list);
2740dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki	mutex_unlock(&syscore_ops_lock);
2840dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki}
2940dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. WysockiEXPORT_SYMBOL_GPL(register_syscore_ops);
3040dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki
3140dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki/**
3240dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki * unregister_syscore_ops - Unregister a set of system core operations.
3340dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki * @ops: System core operations to unregister.
3440dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki */
3540dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysockivoid unregister_syscore_ops(struct syscore_ops *ops)
3640dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki{
3740dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki	mutex_lock(&syscore_ops_lock);
3840dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki	list_del(&ops->node);
3940dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki	mutex_unlock(&syscore_ops_lock);
4040dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki}
4140dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. WysockiEXPORT_SYMBOL_GPL(unregister_syscore_ops);
4240dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki
4340dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki#ifdef CONFIG_PM_SLEEP
4440dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki/**
4540dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki * syscore_suspend - Execute all the registered system core suspend callbacks.
4640dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki *
4740dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki * This function is executed with one CPU on-line and disabled interrupts.
4840dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki */
4940dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysockiint syscore_suspend(void)
5040dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki{
5140dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki	struct syscore_ops *ops;
5240dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki	int ret = 0;
5340dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki
54bb3632c6101b2fad07e6246721466b984b1e0e9dTodd E Brandt	trace_suspend_resume(TPS("syscore_suspend"), 0, true);
55887596224cca4dc4669c53e4d7a33fcfc9d9e823Colin Cross	pr_debug("Checking wakeup interrupts\n");
56887596224cca4dc4669c53e4d7a33fcfc9d9e823Colin Cross
57887596224cca4dc4669c53e4d7a33fcfc9d9e823Colin Cross	/* Return error code if there are any wakeup interrupts pending. */
589ce7a25849e80cfb264f4995f832b932c1987e1aThomas Gleixner	if (pm_wakeup_pending())
599ce7a25849e80cfb264f4995f832b932c1987e1aThomas Gleixner		return -EBUSY;
60887596224cca4dc4669c53e4d7a33fcfc9d9e823Colin Cross
6140dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki	WARN_ONCE(!irqs_disabled(),
6240dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki		"Interrupts enabled before system core suspend.\n");
6340dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki
6440dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki	list_for_each_entry_reverse(ops, &syscore_ops_list, node)
6540dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki		if (ops->suspend) {
6640dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki			if (initcall_debug)
6740dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki				pr_info("PM: Calling %pF\n", ops->suspend);
6840dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki			ret = ops->suspend();
6940dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki			if (ret)
7040dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki				goto err_out;
7140dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki			WARN_ONCE(!irqs_disabled(),
7240dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki				"Interrupts enabled after %pF\n", ops->suspend);
7340dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki		}
7440dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki
75bb3632c6101b2fad07e6246721466b984b1e0e9dTodd E Brandt	trace_suspend_resume(TPS("syscore_suspend"), 0, false);
7640dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki	return 0;
7740dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki
7840dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki err_out:
79722c1106fc368dc7a0fd408f17f2576e0094641bRuchi Kandoi	log_suspend_abort_reason("System core suspend callback %pF failed",
80722c1106fc368dc7a0fd408f17f2576e0094641bRuchi Kandoi		ops->suspend);
8140dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki	pr_err("PM: System core suspend callback %pF failed.\n", ops->suspend);
8240dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki
8340dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki	list_for_each_entry_continue(ops, &syscore_ops_list, node)
8440dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki		if (ops->resume)
8540dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki			ops->resume();
8640dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki
8740dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki	return ret;
8840dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki}
8919234c0819da0e043a02710488dfd9b242b42ebaRafael J. WysockiEXPORT_SYMBOL_GPL(syscore_suspend);
9040dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki
9140dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki/**
9240dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki * syscore_resume - Execute all the registered system core resume callbacks.
9340dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki *
9440dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki * This function is executed with one CPU on-line and disabled interrupts.
9540dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki */
9640dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysockivoid syscore_resume(void)
9740dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki{
9840dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki	struct syscore_ops *ops;
9940dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki
100bb3632c6101b2fad07e6246721466b984b1e0e9dTodd E Brandt	trace_suspend_resume(TPS("syscore_resume"), 0, true);
10140dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki	WARN_ONCE(!irqs_disabled(),
10240dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki		"Interrupts enabled before system core resume.\n");
10340dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki
10440dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki	list_for_each_entry(ops, &syscore_ops_list, node)
10540dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki		if (ops->resume) {
10640dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki			if (initcall_debug)
10740dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki				pr_info("PM: Calling %pF\n", ops->resume);
10840dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki			ops->resume();
10940dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki			WARN_ONCE(!irqs_disabled(),
11040dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki				"Interrupts enabled after %pF\n", ops->resume);
11140dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki		}
112bb3632c6101b2fad07e6246721466b984b1e0e9dTodd E Brandt	trace_suspend_resume(TPS("syscore_resume"), 0, false);
11340dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki}
11419234c0819da0e043a02710488dfd9b242b42ebaRafael J. WysockiEXPORT_SYMBOL_GPL(syscore_resume);
11540dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki#endif /* CONFIG_PM_SLEEP */
11640dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki
11740dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki/**
11840dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki * syscore_shutdown - Execute all the registered system core shutdown callbacks.
11940dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki */
12040dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysockivoid syscore_shutdown(void)
12140dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki{
12240dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki	struct syscore_ops *ops;
12340dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki
12440dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki	mutex_lock(&syscore_ops_lock);
12540dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki
12640dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki	list_for_each_entry_reverse(ops, &syscore_ops_list, node)
12740dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki		if (ops->shutdown) {
12840dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki			if (initcall_debug)
12940dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki				pr_info("PM: Calling %pF\n", ops->shutdown);
13040dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki			ops->shutdown();
13140dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki		}
13240dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki
13340dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki	mutex_unlock(&syscore_ops_lock);
13440dc166cb5dddbd36aa4ad11c03915ea538f5a61Rafael J. Wysocki}
135