11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * kernel/power/main.c - PM subsystem core functionality. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (c) 2003 Patrick Mochel 51da177e4SLinus Torvalds * Copyright (c) 2003 Open Source Development Lab 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * This file is released under the GPLv2 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds */ 101da177e4SLinus Torvalds 114cf30348SRalf Baechle #include <linux/module.h> 121da177e4SLinus Torvalds #include <linux/suspend.h> 131da177e4SLinus Torvalds #include <linux/kobject.h> 141da177e4SLinus Torvalds #include <linux/string.h> 151da177e4SLinus Torvalds #include <linux/delay.h> 161da177e4SLinus Torvalds #include <linux/errno.h> 171da177e4SLinus Torvalds #include <linux/init.h> 186cc07191SAndrew Morton #include <linux/console.h> 19e3920fb4SRafael J. Wysocki #include <linux/cpu.h> 20c5c6ba4eSRafael J. Wysocki #include <linux/resume-trace.h> 217dfb7103SNigel Cunningham #include <linux/freezer.h> 2296177299SChristoph Lameter #include <linux/vmstat.h> 231da177e4SLinus Torvalds 241da177e4SLinus Torvalds #include "power.h" 251da177e4SLinus Torvalds 26b10d9117SRafael J. Wysocki BLOCKING_NOTIFIER_HEAD(pm_chain_head); 27b10d9117SRafael J. Wysocki 28296699deSRafael J. Wysocki DEFINE_MUTEX(pm_mutex); 29296699deSRafael J. Wysocki 30296699deSRafael J. Wysocki #ifdef CONFIG_SUSPEND 31296699deSRafael J. Wysocki 325ae947ecSDavid Shaohua Li /* This is just an arbitrary number */ 335ae947ecSDavid Shaohua Li #define FREE_PAGE_NUMBER (100) 345ae947ecSDavid Shaohua Li 35123d3c13SPavel Machek struct pm_ops *pm_ops; 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds /** 381da177e4SLinus Torvalds * pm_set_ops - Set the global power method table. 391da177e4SLinus Torvalds * @ops: Pointer to ops structure. 401da177e4SLinus Torvalds */ 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds void pm_set_ops(struct pm_ops * ops) 431da177e4SLinus Torvalds { 44a6d70980SStephen Hemminger mutex_lock(&pm_mutex); 451da177e4SLinus Torvalds pm_ops = ops; 46a6d70980SStephen Hemminger mutex_unlock(&pm_mutex); 471da177e4SLinus Torvalds } 481da177e4SLinus Torvalds 49e8c9c502SJohannes Berg /** 50e8c9c502SJohannes Berg * pm_valid_only_mem - generic memory-only valid callback 51e8c9c502SJohannes Berg * 52e8c9c502SJohannes Berg * pm_ops drivers that implement mem suspend only and only need 53e8c9c502SJohannes Berg * to check for that in their .valid callback can use this instead 54e8c9c502SJohannes Berg * of rolling their own .valid callback. 55e8c9c502SJohannes Berg */ 56e8c9c502SJohannes Berg int pm_valid_only_mem(suspend_state_t state) 57e8c9c502SJohannes Berg { 58e8c9c502SJohannes Berg return state == PM_SUSPEND_MEM; 59e8c9c502SJohannes Berg } 60e8c9c502SJohannes Berg 61e8c9c502SJohannes Berg 62e3c7db62SRafael J. Wysocki static inline void pm_finish(suspend_state_t state) 63e3c7db62SRafael J. Wysocki { 64e3c7db62SRafael J. Wysocki if (pm_ops->finish) 65e3c7db62SRafael J. Wysocki pm_ops->finish(state); 66e3c7db62SRafael J. Wysocki } 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds /** 691da177e4SLinus Torvalds * suspend_prepare - Do prep work before entering low-power state. 701da177e4SLinus Torvalds * 716c961dfbSRafael J. Wysocki * This is common code that is called for each state that we're entering. 726c961dfbSRafael J. Wysocki * Run suspend notifiers, allocate a console and stop all processes. 731da177e4SLinus Torvalds */ 746c961dfbSRafael J. Wysocki static int suspend_prepare(void) 751da177e4SLinus Torvalds { 76e3920fb4SRafael J. Wysocki int error; 775ae947ecSDavid Shaohua Li unsigned int free_pages; 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds if (!pm_ops || !pm_ops->enter) 801da177e4SLinus Torvalds return -EPERM; 811da177e4SLinus Torvalds 82b10d9117SRafael J. Wysocki error = pm_notifier_call_chain(PM_SUSPEND_PREPARE); 83b10d9117SRafael J. Wysocki if (error) 84b10d9117SRafael J. Wysocki goto Finish; 85b10d9117SRafael J. Wysocki 861da177e4SLinus Torvalds pm_prepare_console(); 871da177e4SLinus Torvalds 881da177e4SLinus Torvalds if (freeze_processes()) { 891da177e4SLinus Torvalds error = -EAGAIN; 901da177e4SLinus Torvalds goto Thaw; 911da177e4SLinus Torvalds } 921da177e4SLinus Torvalds 936c961dfbSRafael J. Wysocki free_pages = global_page_state(NR_FREE_PAGES); 946c961dfbSRafael J. Wysocki if (free_pages < FREE_PAGE_NUMBER) { 955ae947ecSDavid Shaohua Li pr_debug("PM: free some memory\n"); 965ae947ecSDavid Shaohua Li shrink_all_memory(FREE_PAGE_NUMBER - free_pages); 975ae947ecSDavid Shaohua Li if (nr_free_pages() < FREE_PAGE_NUMBER) { 985ae947ecSDavid Shaohua Li error = -ENOMEM; 995ae947ecSDavid Shaohua Li printk(KERN_ERR "PM: No enough memory\n"); 1005ae947ecSDavid Shaohua Li } 1015ae947ecSDavid Shaohua Li } 102e3c7db62SRafael J. Wysocki if (!error) 1031da177e4SLinus Torvalds return 0; 104e3c7db62SRafael J. Wysocki 1051da177e4SLinus Torvalds Thaw: 1061da177e4SLinus Torvalds thaw_processes(); 1071da177e4SLinus Torvalds pm_restore_console(); 108b10d9117SRafael J. Wysocki Finish: 109b10d9117SRafael J. Wysocki pm_notifier_call_chain(PM_POST_SUSPEND); 1101da177e4SLinus Torvalds return error; 1111da177e4SLinus Torvalds } 1121da177e4SLinus Torvalds 113a53c46dcSJohannes Berg /* default implementation */ 114a53c46dcSJohannes Berg void __attribute__ ((weak)) arch_suspend_disable_irqs(void) 115a53c46dcSJohannes Berg { 116a53c46dcSJohannes Berg local_irq_disable(); 117a53c46dcSJohannes Berg } 118a53c46dcSJohannes Berg 119a53c46dcSJohannes Berg /* default implementation */ 120a53c46dcSJohannes Berg void __attribute__ ((weak)) arch_suspend_enable_irqs(void) 121a53c46dcSJohannes Berg { 122a53c46dcSJohannes Berg local_irq_enable(); 123a53c46dcSJohannes Berg } 1241da177e4SLinus Torvalds 1256c961dfbSRafael J. Wysocki /** 1266c961dfbSRafael J. Wysocki * suspend_enter - enter the desired system sleep state. 1276c961dfbSRafael J. Wysocki * @state: state to enter 1286c961dfbSRafael J. Wysocki * 1296c961dfbSRafael J. Wysocki * This function should be called after devices have been suspended. 1306c961dfbSRafael J. Wysocki */ 131*a065c86eSAdrian Bunk static int suspend_enter(suspend_state_t state) 1321da177e4SLinus Torvalds { 1331da177e4SLinus Torvalds int error = 0; 1341da177e4SLinus Torvalds 135a53c46dcSJohannes Berg arch_suspend_disable_irqs(); 136a53c46dcSJohannes Berg BUG_ON(!irqs_disabled()); 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds if ((error = device_power_down(PMSG_SUSPEND))) { 1391da177e4SLinus Torvalds printk(KERN_ERR "Some devices failed to power down\n"); 1401da177e4SLinus Torvalds goto Done; 1411da177e4SLinus Torvalds } 1421da177e4SLinus Torvalds error = pm_ops->enter(state); 1431da177e4SLinus Torvalds device_power_up(); 1441da177e4SLinus Torvalds Done: 145a53c46dcSJohannes Berg arch_suspend_enable_irqs(); 146a53c46dcSJohannes Berg BUG_ON(irqs_disabled()); 1471da177e4SLinus Torvalds return error; 1481da177e4SLinus Torvalds } 1491da177e4SLinus Torvalds 1506c961dfbSRafael J. Wysocki /** 1516c961dfbSRafael J. Wysocki * suspend_devices_and_enter - suspend devices and enter the desired system sleep 1526c961dfbSRafael J. Wysocki * state. 1536c961dfbSRafael J. Wysocki * @state: state to enter 1546c961dfbSRafael J. Wysocki */ 1556c961dfbSRafael J. Wysocki int suspend_devices_and_enter(suspend_state_t state) 1566c961dfbSRafael J. Wysocki { 1576c961dfbSRafael J. Wysocki int error; 1586c961dfbSRafael J. Wysocki 1596c961dfbSRafael J. Wysocki if (!pm_ops) 1606c961dfbSRafael J. Wysocki return -ENOSYS; 1616c961dfbSRafael J. Wysocki 1626c961dfbSRafael J. Wysocki if (pm_ops->set_target) { 1636c961dfbSRafael J. Wysocki error = pm_ops->set_target(state); 1646c961dfbSRafael J. Wysocki if (error) 1656c961dfbSRafael J. Wysocki return error; 1666c961dfbSRafael J. Wysocki } 1676c961dfbSRafael J. Wysocki suspend_console(); 1686c961dfbSRafael J. Wysocki error = device_suspend(PMSG_SUSPEND); 1696c961dfbSRafael J. Wysocki if (error) { 1706c961dfbSRafael J. Wysocki printk(KERN_ERR "Some devices failed to suspend\n"); 1716c961dfbSRafael J. Wysocki goto Resume_console; 1726c961dfbSRafael J. Wysocki } 1736c961dfbSRafael J. Wysocki if (pm_ops->prepare) { 1746c961dfbSRafael J. Wysocki error = pm_ops->prepare(state); 1756c961dfbSRafael J. Wysocki if (error) 1766c961dfbSRafael J. Wysocki goto Resume_devices; 1776c961dfbSRafael J. Wysocki } 1786c961dfbSRafael J. Wysocki error = disable_nonboot_cpus(); 1796c961dfbSRafael J. Wysocki if (!error) 1806c961dfbSRafael J. Wysocki suspend_enter(state); 1816c961dfbSRafael J. Wysocki 1826c961dfbSRafael J. Wysocki enable_nonboot_cpus(); 1836c961dfbSRafael J. Wysocki pm_finish(state); 1846c961dfbSRafael J. Wysocki Resume_devices: 1856c961dfbSRafael J. Wysocki device_resume(); 1866c961dfbSRafael J. Wysocki Resume_console: 1876c961dfbSRafael J. Wysocki resume_console(); 1886c961dfbSRafael J. Wysocki return error; 1896c961dfbSRafael J. Wysocki } 1901da177e4SLinus Torvalds 1911da177e4SLinus Torvalds /** 1921da177e4SLinus Torvalds * suspend_finish - Do final work before exiting suspend sequence. 1931da177e4SLinus Torvalds * 1941da177e4SLinus Torvalds * Call platform code to clean up, restart processes, and free the 1951da177e4SLinus Torvalds * console that we've allocated. This is not called for suspend-to-disk. 1961da177e4SLinus Torvalds */ 1976c961dfbSRafael J. Wysocki static void suspend_finish(void) 1981da177e4SLinus Torvalds { 1991da177e4SLinus Torvalds thaw_processes(); 2001da177e4SLinus Torvalds pm_restore_console(); 201b10d9117SRafael J. Wysocki pm_notifier_call_chain(PM_POST_SUSPEND); 2021da177e4SLinus Torvalds } 2031da177e4SLinus Torvalds 2041da177e4SLinus Torvalds 2051da177e4SLinus Torvalds 2061da177e4SLinus Torvalds 2073b364b8dSAndreas Mohr static const char * const pm_states[PM_SUSPEND_MAX] = { 2081da177e4SLinus Torvalds [PM_SUSPEND_STANDBY] = "standby", 2091da177e4SLinus Torvalds [PM_SUSPEND_MEM] = "mem", 2101da177e4SLinus Torvalds }; 2111da177e4SLinus Torvalds 212123d3c13SPavel Machek static inline int valid_state(suspend_state_t state) 213123d3c13SPavel Machek { 214a3d25c27SRafael J. Wysocki /* All states need lowlevel support and need to be valid 215a3d25c27SRafael J. Wysocki * to the lowlevel implementation, no valid callback 2169684e51cSJohannes Berg * implies that none are valid. */ 2179684e51cSJohannes Berg if (!pm_ops || !pm_ops->valid || !pm_ops->valid(state)) 218123d3c13SPavel Machek return 0; 219123d3c13SPavel Machek return 1; 220123d3c13SPavel Machek } 221123d3c13SPavel Machek 2221da177e4SLinus Torvalds 2231da177e4SLinus Torvalds /** 2241da177e4SLinus Torvalds * enter_state - Do common work of entering low-power state. 2251da177e4SLinus Torvalds * @state: pm_state structure for state we're entering. 2261da177e4SLinus Torvalds * 2271da177e4SLinus Torvalds * Make sure we're the only ones trying to enter a sleep state. Fail 2281da177e4SLinus Torvalds * if someone has beat us to it, since we don't want anything weird to 2291da177e4SLinus Torvalds * happen when we wake up. 2301da177e4SLinus Torvalds * Then, do the setup for suspend, enter the state, and cleaup (after 2311da177e4SLinus Torvalds * we've woken up). 2321da177e4SLinus Torvalds */ 2331da177e4SLinus Torvalds static int enter_state(suspend_state_t state) 2341da177e4SLinus Torvalds { 2351da177e4SLinus Torvalds int error; 2361da177e4SLinus Torvalds 237123d3c13SPavel Machek if (!valid_state(state)) 238eb9289ebSShaohua Li return -ENODEV; 239a6d70980SStephen Hemminger if (!mutex_trylock(&pm_mutex)) 2401da177e4SLinus Torvalds return -EBUSY; 2411da177e4SLinus Torvalds 24282428b62SDavid Brownell pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); 2436c961dfbSRafael J. Wysocki if ((error = suspend_prepare())) 2441da177e4SLinus Torvalds goto Unlock; 2451da177e4SLinus Torvalds 24682428b62SDavid Brownell pr_debug("PM: Entering %s sleep\n", pm_states[state]); 2476c961dfbSRafael J. Wysocki error = suspend_devices_and_enter(state); 2481da177e4SLinus Torvalds 24982428b62SDavid Brownell pr_debug("PM: Finishing wakeup.\n"); 2506c961dfbSRafael J. Wysocki suspend_finish(); 2511da177e4SLinus Torvalds Unlock: 252a6d70980SStephen Hemminger mutex_unlock(&pm_mutex); 2531da177e4SLinus Torvalds return error; 2541da177e4SLinus Torvalds } 2551da177e4SLinus Torvalds 2561da177e4SLinus Torvalds 2571da177e4SLinus Torvalds /** 2581da177e4SLinus Torvalds * pm_suspend - Externally visible function for suspending system. 259a3d25c27SRafael J. Wysocki * @state: Enumerated value of state to enter. 2601da177e4SLinus Torvalds * 2611da177e4SLinus Torvalds * Determine whether or not value is within range, get state 2621da177e4SLinus Torvalds * structure, and enter (above). 2631da177e4SLinus Torvalds */ 2641da177e4SLinus Torvalds 2651da177e4SLinus Torvalds int pm_suspend(suspend_state_t state) 2661da177e4SLinus Torvalds { 267e2a5b420SAlexey Starikovskiy if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX) 2681da177e4SLinus Torvalds return enter_state(state); 2691da177e4SLinus Torvalds return -EINVAL; 2701da177e4SLinus Torvalds } 2711da177e4SLinus Torvalds 2724cf30348SRalf Baechle EXPORT_SYMBOL(pm_suspend); 2731da177e4SLinus Torvalds 274296699deSRafael J. Wysocki #endif /* CONFIG_SUSPEND */ 275296699deSRafael J. Wysocki 2761da177e4SLinus Torvalds decl_subsys(power,NULL,NULL); 2771da177e4SLinus Torvalds 2781da177e4SLinus Torvalds 2791da177e4SLinus Torvalds /** 2801da177e4SLinus Torvalds * state - control system power state. 2811da177e4SLinus Torvalds * 2821da177e4SLinus Torvalds * show() returns what states are supported, which is hard-coded to 2831da177e4SLinus Torvalds * 'standby' (Power-On Suspend), 'mem' (Suspend-to-RAM), and 2841da177e4SLinus Torvalds * 'disk' (Suspend-to-Disk). 2851da177e4SLinus Torvalds * 2861da177e4SLinus Torvalds * store() accepts one of those strings, translates it into the 2871da177e4SLinus Torvalds * proper enumerated value, and initiates a suspend transition. 2881da177e4SLinus Torvalds */ 2891da177e4SLinus Torvalds 290823bccfcSGreg Kroah-Hartman static ssize_t state_show(struct kset *kset, char *buf) 2911da177e4SLinus Torvalds { 2921da177e4SLinus Torvalds char *s = buf; 293296699deSRafael J. Wysocki #ifdef CONFIG_SUSPEND 294296699deSRafael J. Wysocki int i; 2951da177e4SLinus Torvalds 2961da177e4SLinus Torvalds for (i = 0; i < PM_SUSPEND_MAX; i++) { 297123d3c13SPavel Machek if (pm_states[i] && valid_state(i)) 2981da177e4SLinus Torvalds s += sprintf(s,"%s ", pm_states[i]); 2991da177e4SLinus Torvalds } 300296699deSRafael J. Wysocki #endif 301b0cb1a19SRafael J. Wysocki #ifdef CONFIG_HIBERNATION 302a3d25c27SRafael J. Wysocki s += sprintf(s, "%s\n", "disk"); 303a3d25c27SRafael J. Wysocki #else 304a3d25c27SRafael J. Wysocki if (s != buf) 305a3d25c27SRafael J. Wysocki /* convert the last space to a newline */ 306a3d25c27SRafael J. Wysocki *(s-1) = '\n'; 307a3d25c27SRafael J. Wysocki #endif 3081da177e4SLinus Torvalds return (s - buf); 3091da177e4SLinus Torvalds } 3101da177e4SLinus Torvalds 311823bccfcSGreg Kroah-Hartman static ssize_t state_store(struct kset *kset, const char *buf, size_t n) 3121da177e4SLinus Torvalds { 313296699deSRafael J. Wysocki #ifdef CONFIG_SUSPEND 3141da177e4SLinus Torvalds suspend_state_t state = PM_SUSPEND_STANDBY; 3153b364b8dSAndreas Mohr const char * const *s; 316296699deSRafael J. Wysocki #endif 3171da177e4SLinus Torvalds char *p; 3181da177e4SLinus Torvalds int len; 319296699deSRafael J. Wysocki int error = -EINVAL; 3201da177e4SLinus Torvalds 3211da177e4SLinus Torvalds p = memchr(buf, '\n', n); 3221da177e4SLinus Torvalds len = p ? p - buf : n; 3231da177e4SLinus Torvalds 324a3d25c27SRafael J. Wysocki /* First, check if we are requested to hibernate */ 3258d98a690SRafael J. Wysocki if (len == 4 && !strncmp(buf, "disk", len)) { 326a3d25c27SRafael J. Wysocki error = hibernate(); 327296699deSRafael J. Wysocki goto Exit; 328a3d25c27SRafael J. Wysocki } 329a3d25c27SRafael J. Wysocki 330296699deSRafael J. Wysocki #ifdef CONFIG_SUSPEND 3311da177e4SLinus Torvalds for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) { 3328d98a690SRafael J. Wysocki if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) 3331da177e4SLinus Torvalds break; 3341da177e4SLinus Torvalds } 33547bb7899Sdean gaudet if (state < PM_SUSPEND_MAX && *s) 3361da177e4SLinus Torvalds error = enter_state(state); 337296699deSRafael J. Wysocki #endif 338296699deSRafael J. Wysocki 339296699deSRafael J. Wysocki Exit: 3401da177e4SLinus Torvalds return error ? error : n; 3411da177e4SLinus Torvalds } 3421da177e4SLinus Torvalds 3431da177e4SLinus Torvalds power_attr(state); 3441da177e4SLinus Torvalds 345c5c6ba4eSRafael J. Wysocki #ifdef CONFIG_PM_TRACE 346c5c6ba4eSRafael J. Wysocki int pm_trace_enabled; 347c5c6ba4eSRafael J. Wysocki 348823bccfcSGreg Kroah-Hartman static ssize_t pm_trace_show(struct kset *kset, char *buf) 349c5c6ba4eSRafael J. Wysocki { 350c5c6ba4eSRafael J. Wysocki return sprintf(buf, "%d\n", pm_trace_enabled); 351c5c6ba4eSRafael J. Wysocki } 352c5c6ba4eSRafael J. Wysocki 353c5c6ba4eSRafael J. Wysocki static ssize_t 354823bccfcSGreg Kroah-Hartman pm_trace_store(struct kset *kset, const char *buf, size_t n) 355c5c6ba4eSRafael J. Wysocki { 356c5c6ba4eSRafael J. Wysocki int val; 357c5c6ba4eSRafael J. Wysocki 358c5c6ba4eSRafael J. Wysocki if (sscanf(buf, "%d", &val) == 1) { 359c5c6ba4eSRafael J. Wysocki pm_trace_enabled = !!val; 360c5c6ba4eSRafael J. Wysocki return n; 361c5c6ba4eSRafael J. Wysocki } 362c5c6ba4eSRafael J. Wysocki return -EINVAL; 363c5c6ba4eSRafael J. Wysocki } 364c5c6ba4eSRafael J. Wysocki 365c5c6ba4eSRafael J. Wysocki power_attr(pm_trace); 366c5c6ba4eSRafael J. Wysocki 367c5c6ba4eSRafael J. Wysocki static struct attribute * g[] = { 368c5c6ba4eSRafael J. Wysocki &state_attr.attr, 369c5c6ba4eSRafael J. Wysocki &pm_trace_attr.attr, 370c5c6ba4eSRafael J. Wysocki NULL, 371c5c6ba4eSRafael J. Wysocki }; 372c5c6ba4eSRafael J. Wysocki #else 3731da177e4SLinus Torvalds static struct attribute * g[] = { 3741da177e4SLinus Torvalds &state_attr.attr, 3751da177e4SLinus Torvalds NULL, 3761da177e4SLinus Torvalds }; 377c5c6ba4eSRafael J. Wysocki #endif /* CONFIG_PM_TRACE */ 3781da177e4SLinus Torvalds 3791da177e4SLinus Torvalds static struct attribute_group attr_group = { 3801da177e4SLinus Torvalds .attrs = g, 3811da177e4SLinus Torvalds }; 3821da177e4SLinus Torvalds 3831da177e4SLinus Torvalds 3841da177e4SLinus Torvalds static int __init pm_init(void) 3851da177e4SLinus Torvalds { 3861da177e4SLinus Torvalds int error = subsystem_register(&power_subsys); 3871da177e4SLinus Torvalds if (!error) 388823bccfcSGreg Kroah-Hartman error = sysfs_create_group(&power_subsys.kobj,&attr_group); 3891da177e4SLinus Torvalds return error; 3901da177e4SLinus Torvalds } 3911da177e4SLinus Torvalds 3921da177e4SLinus Torvalds core_initcall(pm_init); 393