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