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> 23232b1432SRafael J. Wysocki #include <linux/syscalls.h> 241da177e4SLinus Torvalds 251da177e4SLinus Torvalds #include "power.h" 261da177e4SLinus Torvalds 27b10d9117SRafael J. Wysocki BLOCKING_NOTIFIER_HEAD(pm_chain_head); 28b10d9117SRafael J. Wysocki 29296699deSRafael J. Wysocki DEFINE_MUTEX(pm_mutex); 30296699deSRafael J. Wysocki 319f9adecdSLen Brown unsigned int pm_flags; 329f9adecdSLen Brown EXPORT_SYMBOL(pm_flags); 339f9adecdSLen Brown 34*0e7d56e3SRafael J. Wysocki #ifdef CONFIG_PM_DEBUG 35*0e7d56e3SRafael J. Wysocki int pm_test_level = TEST_NONE; 36*0e7d56e3SRafael J. Wysocki 37*0e7d56e3SRafael J. Wysocki static int suspend_test(int level) 38*0e7d56e3SRafael J. Wysocki { 39*0e7d56e3SRafael J. Wysocki if (pm_test_level == level) { 40*0e7d56e3SRafael J. Wysocki printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n"); 41*0e7d56e3SRafael J. Wysocki mdelay(5000); 42*0e7d56e3SRafael J. Wysocki return 1; 43*0e7d56e3SRafael J. Wysocki } 44*0e7d56e3SRafael J. Wysocki return 0; 45*0e7d56e3SRafael J. Wysocki } 46*0e7d56e3SRafael J. Wysocki 47*0e7d56e3SRafael J. Wysocki static const char * const pm_tests[__TEST_AFTER_LAST] = { 48*0e7d56e3SRafael J. Wysocki [TEST_NONE] = "none", 49*0e7d56e3SRafael J. Wysocki [TEST_CORE] = "core", 50*0e7d56e3SRafael J. Wysocki [TEST_CPUS] = "processors", 51*0e7d56e3SRafael J. Wysocki [TEST_PLATFORM] = "platform", 52*0e7d56e3SRafael J. Wysocki [TEST_DEVICES] = "devices", 53*0e7d56e3SRafael J. Wysocki [TEST_FREEZER] = "freezer", 54*0e7d56e3SRafael J. Wysocki }; 55*0e7d56e3SRafael J. Wysocki 56*0e7d56e3SRafael J. Wysocki static ssize_t pm_test_show(struct kset *kset, char *buf) 57*0e7d56e3SRafael J. Wysocki { 58*0e7d56e3SRafael J. Wysocki char *s = buf; 59*0e7d56e3SRafael J. Wysocki int level; 60*0e7d56e3SRafael J. Wysocki 61*0e7d56e3SRafael J. Wysocki for (level = TEST_FIRST; level <= TEST_MAX; level++) 62*0e7d56e3SRafael J. Wysocki if (pm_tests[level]) { 63*0e7d56e3SRafael J. Wysocki if (level == pm_test_level) 64*0e7d56e3SRafael J. Wysocki s += sprintf(s, "[%s] ", pm_tests[level]); 65*0e7d56e3SRafael J. Wysocki else 66*0e7d56e3SRafael J. Wysocki s += sprintf(s, "%s ", pm_tests[level]); 67*0e7d56e3SRafael J. Wysocki } 68*0e7d56e3SRafael J. Wysocki 69*0e7d56e3SRafael J. Wysocki if (s != buf) 70*0e7d56e3SRafael J. Wysocki /* convert the last space to a newline */ 71*0e7d56e3SRafael J. Wysocki *(s-1) = '\n'; 72*0e7d56e3SRafael J. Wysocki 73*0e7d56e3SRafael J. Wysocki return (s - buf); 74*0e7d56e3SRafael J. Wysocki } 75*0e7d56e3SRafael J. Wysocki 76*0e7d56e3SRafael J. Wysocki static ssize_t pm_test_store(struct kset *kset, const char *buf, size_t n) 77*0e7d56e3SRafael J. Wysocki { 78*0e7d56e3SRafael J. Wysocki const char * const *s; 79*0e7d56e3SRafael J. Wysocki int level; 80*0e7d56e3SRafael J. Wysocki char *p; 81*0e7d56e3SRafael J. Wysocki int len; 82*0e7d56e3SRafael J. Wysocki int error = -EINVAL; 83*0e7d56e3SRafael J. Wysocki 84*0e7d56e3SRafael J. Wysocki p = memchr(buf, '\n', n); 85*0e7d56e3SRafael J. Wysocki len = p ? p - buf : n; 86*0e7d56e3SRafael J. Wysocki 87*0e7d56e3SRafael J. Wysocki mutex_lock(&pm_mutex); 88*0e7d56e3SRafael J. Wysocki 89*0e7d56e3SRafael J. Wysocki level = TEST_FIRST; 90*0e7d56e3SRafael J. Wysocki for (s = &pm_tests[level]; level <= TEST_MAX; s++, level++) 91*0e7d56e3SRafael J. Wysocki if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) { 92*0e7d56e3SRafael J. Wysocki pm_test_level = level; 93*0e7d56e3SRafael J. Wysocki error = 0; 94*0e7d56e3SRafael J. Wysocki break; 95*0e7d56e3SRafael J. Wysocki } 96*0e7d56e3SRafael J. Wysocki 97*0e7d56e3SRafael J. Wysocki mutex_unlock(&pm_mutex); 98*0e7d56e3SRafael J. Wysocki 99*0e7d56e3SRafael J. Wysocki return error ? error : n; 100*0e7d56e3SRafael J. Wysocki } 101*0e7d56e3SRafael J. Wysocki 102*0e7d56e3SRafael J. Wysocki power_attr(pm_test); 103*0e7d56e3SRafael J. Wysocki #else /* !CONFIG_PM_DEBUG */ 104*0e7d56e3SRafael J. Wysocki static inline int suspend_test(int level) { return 0; } 105*0e7d56e3SRafael J. Wysocki #endif /* !CONFIG_PM_DEBUG */ 106*0e7d56e3SRafael J. Wysocki 107296699deSRafael J. Wysocki #ifdef CONFIG_SUSPEND 108296699deSRafael J. Wysocki 1095ae947ecSDavid Shaohua Li /* This is just an arbitrary number */ 1105ae947ecSDavid Shaohua Li #define FREE_PAGE_NUMBER (100) 1115ae947ecSDavid Shaohua Li 112f242d919SRafael J. Wysocki static struct platform_suspend_ops *suspend_ops; 1131da177e4SLinus Torvalds 1141da177e4SLinus Torvalds /** 11526398a70SRafael J. Wysocki * suspend_set_ops - Set the global suspend method table. 1161da177e4SLinus Torvalds * @ops: Pointer to ops structure. 1171da177e4SLinus Torvalds */ 1181da177e4SLinus Torvalds 11926398a70SRafael J. Wysocki void suspend_set_ops(struct platform_suspend_ops *ops) 1201da177e4SLinus Torvalds { 121a6d70980SStephen Hemminger mutex_lock(&pm_mutex); 12226398a70SRafael J. Wysocki suspend_ops = ops; 123a6d70980SStephen Hemminger mutex_unlock(&pm_mutex); 1241da177e4SLinus Torvalds } 1251da177e4SLinus Torvalds 126e8c9c502SJohannes Berg /** 12726398a70SRafael J. Wysocki * suspend_valid_only_mem - generic memory-only valid callback 128e8c9c502SJohannes Berg * 12926398a70SRafael J. Wysocki * Platform drivers that implement mem suspend only and only need 130e8c9c502SJohannes Berg * to check for that in their .valid callback can use this instead 131e8c9c502SJohannes Berg * of rolling their own .valid callback. 132e8c9c502SJohannes Berg */ 13326398a70SRafael J. Wysocki int suspend_valid_only_mem(suspend_state_t state) 134e8c9c502SJohannes Berg { 135e8c9c502SJohannes Berg return state == PM_SUSPEND_MEM; 136e8c9c502SJohannes Berg } 137e8c9c502SJohannes Berg 1381da177e4SLinus Torvalds /** 1391da177e4SLinus Torvalds * suspend_prepare - Do prep work before entering low-power state. 1401da177e4SLinus Torvalds * 1416c961dfbSRafael J. Wysocki * This is common code that is called for each state that we're entering. 1426c961dfbSRafael J. Wysocki * Run suspend notifiers, allocate a console and stop all processes. 1431da177e4SLinus Torvalds */ 1446c961dfbSRafael J. Wysocki static int suspend_prepare(void) 1451da177e4SLinus Torvalds { 146e3920fb4SRafael J. Wysocki int error; 1475ae947ecSDavid Shaohua Li unsigned int free_pages; 1481da177e4SLinus Torvalds 14926398a70SRafael J. Wysocki if (!suspend_ops || !suspend_ops->enter) 1501da177e4SLinus Torvalds return -EPERM; 1511da177e4SLinus Torvalds 152b10d9117SRafael J. Wysocki error = pm_notifier_call_chain(PM_SUSPEND_PREPARE); 153b10d9117SRafael J. Wysocki if (error) 154b10d9117SRafael J. Wysocki goto Finish; 155b10d9117SRafael J. Wysocki 1561da177e4SLinus Torvalds pm_prepare_console(); 1571da177e4SLinus Torvalds 1581da177e4SLinus Torvalds if (freeze_processes()) { 1591da177e4SLinus Torvalds error = -EAGAIN; 1601da177e4SLinus Torvalds goto Thaw; 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds 1636c961dfbSRafael J. Wysocki free_pages = global_page_state(NR_FREE_PAGES); 1646c961dfbSRafael J. Wysocki if (free_pages < FREE_PAGE_NUMBER) { 1655ae947ecSDavid Shaohua Li pr_debug("PM: free some memory\n"); 1665ae947ecSDavid Shaohua Li shrink_all_memory(FREE_PAGE_NUMBER - free_pages); 1675ae947ecSDavid Shaohua Li if (nr_free_pages() < FREE_PAGE_NUMBER) { 1685ae947ecSDavid Shaohua Li error = -ENOMEM; 1695ae947ecSDavid Shaohua Li printk(KERN_ERR "PM: No enough memory\n"); 1705ae947ecSDavid Shaohua Li } 1715ae947ecSDavid Shaohua Li } 172e3c7db62SRafael J. Wysocki if (!error) 1731da177e4SLinus Torvalds return 0; 174e3c7db62SRafael J. Wysocki 1751da177e4SLinus Torvalds Thaw: 1761da177e4SLinus Torvalds thaw_processes(); 1771da177e4SLinus Torvalds pm_restore_console(); 178b10d9117SRafael J. Wysocki Finish: 179b10d9117SRafael J. Wysocki pm_notifier_call_chain(PM_POST_SUSPEND); 1801da177e4SLinus Torvalds return error; 1811da177e4SLinus Torvalds } 1821da177e4SLinus Torvalds 183a53c46dcSJohannes Berg /* default implementation */ 184a53c46dcSJohannes Berg void __attribute__ ((weak)) arch_suspend_disable_irqs(void) 185a53c46dcSJohannes Berg { 186a53c46dcSJohannes Berg local_irq_disable(); 187a53c46dcSJohannes Berg } 188a53c46dcSJohannes Berg 189a53c46dcSJohannes Berg /* default implementation */ 190a53c46dcSJohannes Berg void __attribute__ ((weak)) arch_suspend_enable_irqs(void) 191a53c46dcSJohannes Berg { 192a53c46dcSJohannes Berg local_irq_enable(); 193a53c46dcSJohannes Berg } 1941da177e4SLinus Torvalds 1956c961dfbSRafael J. Wysocki /** 1966c961dfbSRafael J. Wysocki * suspend_enter - enter the desired system sleep state. 1976c961dfbSRafael J. Wysocki * @state: state to enter 1986c961dfbSRafael J. Wysocki * 1996c961dfbSRafael J. Wysocki * This function should be called after devices have been suspended. 2006c961dfbSRafael J. Wysocki */ 201a065c86eSAdrian Bunk static int suspend_enter(suspend_state_t state) 2021da177e4SLinus Torvalds { 2031da177e4SLinus Torvalds int error = 0; 2041da177e4SLinus Torvalds 205a53c46dcSJohannes Berg arch_suspend_disable_irqs(); 206a53c46dcSJohannes Berg BUG_ON(!irqs_disabled()); 2071da177e4SLinus Torvalds 2081da177e4SLinus Torvalds if ((error = device_power_down(PMSG_SUSPEND))) { 2091da177e4SLinus Torvalds printk(KERN_ERR "Some devices failed to power down\n"); 2101da177e4SLinus Torvalds goto Done; 2111da177e4SLinus Torvalds } 212*0e7d56e3SRafael J. Wysocki 213*0e7d56e3SRafael J. Wysocki if (!suspend_test(TEST_CORE)) 21426398a70SRafael J. Wysocki error = suspend_ops->enter(state); 215*0e7d56e3SRafael J. Wysocki 2161da177e4SLinus Torvalds device_power_up(); 2171da177e4SLinus Torvalds Done: 218a53c46dcSJohannes Berg arch_suspend_enable_irqs(); 219a53c46dcSJohannes Berg BUG_ON(irqs_disabled()); 2201da177e4SLinus Torvalds return error; 2211da177e4SLinus Torvalds } 2221da177e4SLinus Torvalds 2236c961dfbSRafael J. Wysocki /** 2246c961dfbSRafael J. Wysocki * suspend_devices_and_enter - suspend devices and enter the desired system sleep 2256c961dfbSRafael J. Wysocki * state. 2266c961dfbSRafael J. Wysocki * @state: state to enter 2276c961dfbSRafael J. Wysocki */ 2286c961dfbSRafael J. Wysocki int suspend_devices_and_enter(suspend_state_t state) 2296c961dfbSRafael J. Wysocki { 2306c961dfbSRafael J. Wysocki int error; 2316c961dfbSRafael J. Wysocki 23226398a70SRafael J. Wysocki if (!suspend_ops) 2336c961dfbSRafael J. Wysocki return -ENOSYS; 2346c961dfbSRafael J. Wysocki 23526398a70SRafael J. Wysocki if (suspend_ops->set_target) { 23626398a70SRafael J. Wysocki error = suspend_ops->set_target(state); 2376c961dfbSRafael J. Wysocki if (error) 2386c961dfbSRafael J. Wysocki return error; 2396c961dfbSRafael J. Wysocki } 2406c961dfbSRafael J. Wysocki suspend_console(); 2416c961dfbSRafael J. Wysocki error = device_suspend(PMSG_SUSPEND); 2426c961dfbSRafael J. Wysocki if (error) { 2436c961dfbSRafael J. Wysocki printk(KERN_ERR "Some devices failed to suspend\n"); 2446c961dfbSRafael J. Wysocki goto Resume_console; 2456c961dfbSRafael J. Wysocki } 246*0e7d56e3SRafael J. Wysocki 247*0e7d56e3SRafael J. Wysocki if (suspend_test(TEST_DEVICES)) 248*0e7d56e3SRafael J. Wysocki goto Resume_devices; 249*0e7d56e3SRafael J. Wysocki 25026398a70SRafael J. Wysocki if (suspend_ops->prepare) { 251e6c5eb95SRafael J. Wysocki error = suspend_ops->prepare(); 2526c961dfbSRafael J. Wysocki if (error) 2536c961dfbSRafael J. Wysocki goto Resume_devices; 2546c961dfbSRafael J. Wysocki } 255*0e7d56e3SRafael J. Wysocki 256*0e7d56e3SRafael J. Wysocki if (suspend_test(TEST_PLATFORM)) 257*0e7d56e3SRafael J. Wysocki goto Finish; 258*0e7d56e3SRafael J. Wysocki 2596c961dfbSRafael J. Wysocki error = disable_nonboot_cpus(); 260*0e7d56e3SRafael J. Wysocki if (!error && !suspend_test(TEST_CPUS)) 2616c961dfbSRafael J. Wysocki suspend_enter(state); 2626c961dfbSRafael J. Wysocki 2636c961dfbSRafael J. Wysocki enable_nonboot_cpus(); 264*0e7d56e3SRafael J. Wysocki Finish: 265e6c5eb95SRafael J. Wysocki if (suspend_ops->finish) 266e6c5eb95SRafael J. Wysocki suspend_ops->finish(); 2676c961dfbSRafael J. Wysocki Resume_devices: 2686c961dfbSRafael J. Wysocki device_resume(); 2696c961dfbSRafael J. Wysocki Resume_console: 2706c961dfbSRafael J. Wysocki resume_console(); 2716c961dfbSRafael J. Wysocki return error; 2726c961dfbSRafael J. Wysocki } 2731da177e4SLinus Torvalds 2741da177e4SLinus Torvalds /** 2751da177e4SLinus Torvalds * suspend_finish - Do final work before exiting suspend sequence. 2761da177e4SLinus Torvalds * 2771da177e4SLinus Torvalds * Call platform code to clean up, restart processes, and free the 2781da177e4SLinus Torvalds * console that we've allocated. This is not called for suspend-to-disk. 2791da177e4SLinus Torvalds */ 2806c961dfbSRafael J. Wysocki static void suspend_finish(void) 2811da177e4SLinus Torvalds { 2821da177e4SLinus Torvalds thaw_processes(); 2831da177e4SLinus Torvalds pm_restore_console(); 284b10d9117SRafael J. Wysocki pm_notifier_call_chain(PM_POST_SUSPEND); 2851da177e4SLinus Torvalds } 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds 2881da177e4SLinus Torvalds 2891da177e4SLinus Torvalds 2903b364b8dSAndreas Mohr static const char * const pm_states[PM_SUSPEND_MAX] = { 2911da177e4SLinus Torvalds [PM_SUSPEND_STANDBY] = "standby", 2921da177e4SLinus Torvalds [PM_SUSPEND_MEM] = "mem", 2931da177e4SLinus Torvalds }; 2941da177e4SLinus Torvalds 295123d3c13SPavel Machek static inline int valid_state(suspend_state_t state) 296123d3c13SPavel Machek { 297a3d25c27SRafael J. Wysocki /* All states need lowlevel support and need to be valid 298a3d25c27SRafael J. Wysocki * to the lowlevel implementation, no valid callback 2999684e51cSJohannes Berg * implies that none are valid. */ 30026398a70SRafael J. Wysocki if (!suspend_ops || !suspend_ops->valid || !suspend_ops->valid(state)) 301123d3c13SPavel Machek return 0; 302123d3c13SPavel Machek return 1; 303123d3c13SPavel Machek } 304123d3c13SPavel Machek 3051da177e4SLinus Torvalds 3061da177e4SLinus Torvalds /** 3071da177e4SLinus Torvalds * enter_state - Do common work of entering low-power state. 3081da177e4SLinus Torvalds * @state: pm_state structure for state we're entering. 3091da177e4SLinus Torvalds * 3101da177e4SLinus Torvalds * Make sure we're the only ones trying to enter a sleep state. Fail 3111da177e4SLinus Torvalds * if someone has beat us to it, since we don't want anything weird to 3121da177e4SLinus Torvalds * happen when we wake up. 3131da177e4SLinus Torvalds * Then, do the setup for suspend, enter the state, and cleaup (after 3141da177e4SLinus Torvalds * we've woken up). 3151da177e4SLinus Torvalds */ 3161da177e4SLinus Torvalds static int enter_state(suspend_state_t state) 3171da177e4SLinus Torvalds { 3181da177e4SLinus Torvalds int error; 3191da177e4SLinus Torvalds 320123d3c13SPavel Machek if (!valid_state(state)) 321eb9289ebSShaohua Li return -ENODEV; 322232b1432SRafael J. Wysocki 323a6d70980SStephen Hemminger if (!mutex_trylock(&pm_mutex)) 3241da177e4SLinus Torvalds return -EBUSY; 3251da177e4SLinus Torvalds 326232b1432SRafael J. Wysocki printk("Syncing filesystems ... "); 327232b1432SRafael J. Wysocki sys_sync(); 328232b1432SRafael J. Wysocki printk("done.\n"); 329232b1432SRafael J. Wysocki 33082428b62SDavid Brownell pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); 331*0e7d56e3SRafael J. Wysocki error = suspend_prepare(); 332*0e7d56e3SRafael J. Wysocki if (error) 3331da177e4SLinus Torvalds goto Unlock; 3341da177e4SLinus Torvalds 335*0e7d56e3SRafael J. Wysocki if (suspend_test(TEST_FREEZER)) 336*0e7d56e3SRafael J. Wysocki goto Finish; 337*0e7d56e3SRafael J. Wysocki 33882428b62SDavid Brownell pr_debug("PM: Entering %s sleep\n", pm_states[state]); 3396c961dfbSRafael J. Wysocki error = suspend_devices_and_enter(state); 3401da177e4SLinus Torvalds 341*0e7d56e3SRafael J. Wysocki Finish: 34282428b62SDavid Brownell pr_debug("PM: Finishing wakeup.\n"); 3436c961dfbSRafael J. Wysocki suspend_finish(); 3441da177e4SLinus Torvalds Unlock: 345a6d70980SStephen Hemminger mutex_unlock(&pm_mutex); 3461da177e4SLinus Torvalds return error; 3471da177e4SLinus Torvalds } 3481da177e4SLinus Torvalds 3491da177e4SLinus Torvalds 3501da177e4SLinus Torvalds /** 3511da177e4SLinus Torvalds * pm_suspend - Externally visible function for suspending system. 352a3d25c27SRafael J. Wysocki * @state: Enumerated value of state to enter. 3531da177e4SLinus Torvalds * 3541da177e4SLinus Torvalds * Determine whether or not value is within range, get state 3551da177e4SLinus Torvalds * structure, and enter (above). 3561da177e4SLinus Torvalds */ 3571da177e4SLinus Torvalds 3581da177e4SLinus Torvalds int pm_suspend(suspend_state_t state) 3591da177e4SLinus Torvalds { 360e2a5b420SAlexey Starikovskiy if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX) 3611da177e4SLinus Torvalds return enter_state(state); 3621da177e4SLinus Torvalds return -EINVAL; 3631da177e4SLinus Torvalds } 3641da177e4SLinus Torvalds 3654cf30348SRalf Baechle EXPORT_SYMBOL(pm_suspend); 3661da177e4SLinus Torvalds 367296699deSRafael J. Wysocki #endif /* CONFIG_SUSPEND */ 368296699deSRafael J. Wysocki 369d76e15fbSGreg Kroah-Hartman struct kobject *power_kobj; 3701da177e4SLinus Torvalds 3711da177e4SLinus Torvalds /** 3721da177e4SLinus Torvalds * state - control system power state. 3731da177e4SLinus Torvalds * 3741da177e4SLinus Torvalds * show() returns what states are supported, which is hard-coded to 3751da177e4SLinus Torvalds * 'standby' (Power-On Suspend), 'mem' (Suspend-to-RAM), and 3761da177e4SLinus Torvalds * 'disk' (Suspend-to-Disk). 3771da177e4SLinus Torvalds * 3781da177e4SLinus Torvalds * store() accepts one of those strings, translates it into the 3791da177e4SLinus Torvalds * proper enumerated value, and initiates a suspend transition. 3801da177e4SLinus Torvalds */ 3811da177e4SLinus Torvalds 382386f275fSKay Sievers static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, 383386f275fSKay Sievers char *buf) 3841da177e4SLinus Torvalds { 3851da177e4SLinus Torvalds char *s = buf; 386296699deSRafael J. Wysocki #ifdef CONFIG_SUSPEND 387296699deSRafael J. Wysocki int i; 3881da177e4SLinus Torvalds 3891da177e4SLinus Torvalds for (i = 0; i < PM_SUSPEND_MAX; i++) { 390123d3c13SPavel Machek if (pm_states[i] && valid_state(i)) 3911da177e4SLinus Torvalds s += sprintf(s,"%s ", pm_states[i]); 3921da177e4SLinus Torvalds } 393296699deSRafael J. Wysocki #endif 394b0cb1a19SRafael J. Wysocki #ifdef CONFIG_HIBERNATION 395a3d25c27SRafael J. Wysocki s += sprintf(s, "%s\n", "disk"); 396a3d25c27SRafael J. Wysocki #else 397a3d25c27SRafael J. Wysocki if (s != buf) 398a3d25c27SRafael J. Wysocki /* convert the last space to a newline */ 399a3d25c27SRafael J. Wysocki *(s-1) = '\n'; 400a3d25c27SRafael J. Wysocki #endif 4011da177e4SLinus Torvalds return (s - buf); 4021da177e4SLinus Torvalds } 4031da177e4SLinus Torvalds 404386f275fSKay Sievers static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, 405386f275fSKay Sievers const char *buf, size_t n) 4061da177e4SLinus Torvalds { 407296699deSRafael J. Wysocki #ifdef CONFIG_SUSPEND 4081da177e4SLinus Torvalds suspend_state_t state = PM_SUSPEND_STANDBY; 4093b364b8dSAndreas Mohr const char * const *s; 410296699deSRafael J. Wysocki #endif 4111da177e4SLinus Torvalds char *p; 4121da177e4SLinus Torvalds int len; 413296699deSRafael J. Wysocki int error = -EINVAL; 4141da177e4SLinus Torvalds 4151da177e4SLinus Torvalds p = memchr(buf, '\n', n); 4161da177e4SLinus Torvalds len = p ? p - buf : n; 4171da177e4SLinus Torvalds 418a3d25c27SRafael J. Wysocki /* First, check if we are requested to hibernate */ 4198d98a690SRafael J. Wysocki if (len == 4 && !strncmp(buf, "disk", len)) { 420a3d25c27SRafael J. Wysocki error = hibernate(); 421296699deSRafael J. Wysocki goto Exit; 422a3d25c27SRafael J. Wysocki } 423a3d25c27SRafael J. Wysocki 424296699deSRafael J. Wysocki #ifdef CONFIG_SUSPEND 4251da177e4SLinus Torvalds for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) { 4268d98a690SRafael J. Wysocki if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) 4271da177e4SLinus Torvalds break; 4281da177e4SLinus Torvalds } 42947bb7899Sdean gaudet if (state < PM_SUSPEND_MAX && *s) 4301da177e4SLinus Torvalds error = enter_state(state); 431296699deSRafael J. Wysocki #endif 432296699deSRafael J. Wysocki 433296699deSRafael J. Wysocki Exit: 4341da177e4SLinus Torvalds return error ? error : n; 4351da177e4SLinus Torvalds } 4361da177e4SLinus Torvalds 4371da177e4SLinus Torvalds power_attr(state); 4381da177e4SLinus Torvalds 439c5c6ba4eSRafael J. Wysocki #ifdef CONFIG_PM_TRACE 440c5c6ba4eSRafael J. Wysocki int pm_trace_enabled; 441c5c6ba4eSRafael J. Wysocki 442386f275fSKay Sievers static ssize_t pm_trace_show(struct kobject *kobj, struct kobj_attribute *attr, 443386f275fSKay Sievers char *buf) 444c5c6ba4eSRafael J. Wysocki { 445c5c6ba4eSRafael J. Wysocki return sprintf(buf, "%d\n", pm_trace_enabled); 446c5c6ba4eSRafael J. Wysocki } 447c5c6ba4eSRafael J. Wysocki 448c5c6ba4eSRafael J. Wysocki static ssize_t 449386f275fSKay Sievers pm_trace_store(struct kobject *kobj, struct kobj_attribute *attr, 450386f275fSKay Sievers const char *buf, size_t n) 451c5c6ba4eSRafael J. Wysocki { 452c5c6ba4eSRafael J. Wysocki int val; 453c5c6ba4eSRafael J. Wysocki 454c5c6ba4eSRafael J. Wysocki if (sscanf(buf, "%d", &val) == 1) { 455c5c6ba4eSRafael J. Wysocki pm_trace_enabled = !!val; 456c5c6ba4eSRafael J. Wysocki return n; 457c5c6ba4eSRafael J. Wysocki } 458c5c6ba4eSRafael J. Wysocki return -EINVAL; 459c5c6ba4eSRafael J. Wysocki } 460c5c6ba4eSRafael J. Wysocki 461c5c6ba4eSRafael J. Wysocki power_attr(pm_trace); 462*0e7d56e3SRafael J. Wysocki #endif /* CONFIG_PM_TRACE */ 463c5c6ba4eSRafael J. Wysocki 464c5c6ba4eSRafael J. Wysocki static struct attribute * g[] = { 465c5c6ba4eSRafael J. Wysocki &state_attr.attr, 466*0e7d56e3SRafael J. Wysocki #ifdef CONFIG_PM_TRACE 467c5c6ba4eSRafael J. Wysocki &pm_trace_attr.attr, 468*0e7d56e3SRafael J. Wysocki #endif 469*0e7d56e3SRafael J. Wysocki #ifdef CONFIG_PM_DEBUG 470*0e7d56e3SRafael J. Wysocki &pm_test_attr.attr, 471*0e7d56e3SRafael J. Wysocki #endif 472c5c6ba4eSRafael J. Wysocki NULL, 473c5c6ba4eSRafael J. Wysocki }; 4741da177e4SLinus Torvalds 4751da177e4SLinus Torvalds static struct attribute_group attr_group = { 4761da177e4SLinus Torvalds .attrs = g, 4771da177e4SLinus Torvalds }; 4781da177e4SLinus Torvalds 4791da177e4SLinus Torvalds 4801da177e4SLinus Torvalds static int __init pm_init(void) 4811da177e4SLinus Torvalds { 482d76e15fbSGreg Kroah-Hartman power_kobj = kobject_create_and_add("power", NULL); 483d76e15fbSGreg Kroah-Hartman if (!power_kobj) 484039a5dcdSGreg Kroah-Hartman return -ENOMEM; 485d76e15fbSGreg Kroah-Hartman return sysfs_create_group(power_kobj, &attr_group); 4861da177e4SLinus Torvalds } 4871da177e4SLinus Torvalds 4881da177e4SLinus Torvalds core_initcall(pm_init); 489