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 111da177e4SLinus Torvalds #include <linux/suspend.h> 121da177e4SLinus Torvalds #include <linux/kobject.h> 131da177e4SLinus Torvalds #include <linux/string.h> 141da177e4SLinus Torvalds #include <linux/delay.h> 151da177e4SLinus Torvalds #include <linux/errno.h> 161da177e4SLinus Torvalds #include <linux/init.h> 171da177e4SLinus Torvalds #include <linux/pm.h> 181da177e4SLinus Torvalds 191da177e4SLinus Torvalds 201da177e4SLinus Torvalds #include "power.h" 211da177e4SLinus Torvalds 225ae947ecSDavid Shaohua Li /*This is just an arbitrary number */ 235ae947ecSDavid Shaohua Li #define FREE_PAGE_NUMBER (100) 245ae947ecSDavid Shaohua Li 251da177e4SLinus Torvalds DECLARE_MUTEX(pm_sem); 261da177e4SLinus Torvalds 271da177e4SLinus Torvalds struct pm_ops * pm_ops = NULL; 281da177e4SLinus Torvalds suspend_disk_method_t pm_disk_mode = PM_DISK_SHUTDOWN; 291da177e4SLinus Torvalds 301da177e4SLinus Torvalds /** 311da177e4SLinus Torvalds * pm_set_ops - Set the global power method table. 321da177e4SLinus Torvalds * @ops: Pointer to ops structure. 331da177e4SLinus Torvalds */ 341da177e4SLinus Torvalds 351da177e4SLinus Torvalds void pm_set_ops(struct pm_ops * ops) 361da177e4SLinus Torvalds { 371da177e4SLinus Torvalds down(&pm_sem); 381da177e4SLinus Torvalds pm_ops = ops; 391da177e4SLinus Torvalds up(&pm_sem); 401da177e4SLinus Torvalds } 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds 431da177e4SLinus Torvalds /** 441da177e4SLinus Torvalds * suspend_prepare - Do prep work before entering low-power state. 451da177e4SLinus Torvalds * @state: State we're entering. 461da177e4SLinus Torvalds * 471da177e4SLinus Torvalds * This is common code that is called for each state that we're 481da177e4SLinus Torvalds * entering. Allocate a console, stop all processes, then make sure 491da177e4SLinus Torvalds * the platform can enter the requested state. 501da177e4SLinus Torvalds */ 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds static int suspend_prepare(suspend_state_t state) 531da177e4SLinus Torvalds { 541da177e4SLinus Torvalds int error = 0; 555ae947ecSDavid Shaohua Li unsigned int free_pages; 561da177e4SLinus Torvalds 571da177e4SLinus Torvalds if (!pm_ops || !pm_ops->enter) 581da177e4SLinus Torvalds return -EPERM; 591da177e4SLinus Torvalds 601da177e4SLinus Torvalds pm_prepare_console(); 611da177e4SLinus Torvalds 625a72e04dSLi Shaohua disable_nonboot_cpus(); 635a72e04dSLi Shaohua 645a72e04dSLi Shaohua if (num_online_cpus() != 1) { 655a72e04dSLi Shaohua error = -EPERM; 665a72e04dSLi Shaohua goto Enable_cpu; 675a72e04dSLi Shaohua } 685a72e04dSLi Shaohua 691da177e4SLinus Torvalds if (freeze_processes()) { 701da177e4SLinus Torvalds error = -EAGAIN; 711da177e4SLinus Torvalds goto Thaw; 721da177e4SLinus Torvalds } 731da177e4SLinus Torvalds 745ae947ecSDavid Shaohua Li if ((free_pages = nr_free_pages()) < FREE_PAGE_NUMBER) { 755ae947ecSDavid Shaohua Li pr_debug("PM: free some memory\n"); 765ae947ecSDavid Shaohua Li shrink_all_memory(FREE_PAGE_NUMBER - free_pages); 775ae947ecSDavid Shaohua Li if (nr_free_pages() < FREE_PAGE_NUMBER) { 785ae947ecSDavid Shaohua Li error = -ENOMEM; 795ae947ecSDavid Shaohua Li printk(KERN_ERR "PM: No enough memory\n"); 805ae947ecSDavid Shaohua Li goto Thaw; 815ae947ecSDavid Shaohua Li } 825ae947ecSDavid Shaohua Li } 835ae947ecSDavid Shaohua Li 841da177e4SLinus Torvalds if (pm_ops->prepare) { 851da177e4SLinus Torvalds if ((error = pm_ops->prepare(state))) 861da177e4SLinus Torvalds goto Thaw; 871da177e4SLinus Torvalds } 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds if ((error = device_suspend(PMSG_SUSPEND))) { 901da177e4SLinus Torvalds printk(KERN_ERR "Some devices failed to suspend\n"); 911da177e4SLinus Torvalds goto Finish; 921da177e4SLinus Torvalds } 931da177e4SLinus Torvalds return 0; 941da177e4SLinus Torvalds Finish: 951da177e4SLinus Torvalds if (pm_ops->finish) 961da177e4SLinus Torvalds pm_ops->finish(state); 971da177e4SLinus Torvalds Thaw: 981da177e4SLinus Torvalds thaw_processes(); 995a72e04dSLi Shaohua Enable_cpu: 1005a72e04dSLi Shaohua enable_nonboot_cpus(); 1011da177e4SLinus Torvalds pm_restore_console(); 1021da177e4SLinus Torvalds return error; 1031da177e4SLinus Torvalds } 1041da177e4SLinus Torvalds 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds static int suspend_enter(suspend_state_t state) 1071da177e4SLinus Torvalds { 1081da177e4SLinus Torvalds int error = 0; 1091da177e4SLinus Torvalds unsigned long flags; 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds local_irq_save(flags); 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds if ((error = device_power_down(PMSG_SUSPEND))) { 1141da177e4SLinus Torvalds printk(KERN_ERR "Some devices failed to power down\n"); 1151da177e4SLinus Torvalds goto Done; 1161da177e4SLinus Torvalds } 1171da177e4SLinus Torvalds error = pm_ops->enter(state); 1181da177e4SLinus Torvalds device_power_up(); 1191da177e4SLinus Torvalds Done: 1201da177e4SLinus Torvalds local_irq_restore(flags); 1211da177e4SLinus Torvalds return error; 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds 1251da177e4SLinus Torvalds /** 1261da177e4SLinus Torvalds * suspend_finish - Do final work before exiting suspend sequence. 1271da177e4SLinus Torvalds * @state: State we're coming out of. 1281da177e4SLinus Torvalds * 1291da177e4SLinus Torvalds * Call platform code to clean up, restart processes, and free the 1301da177e4SLinus Torvalds * console that we've allocated. This is not called for suspend-to-disk. 1311da177e4SLinus Torvalds */ 1321da177e4SLinus Torvalds 1331da177e4SLinus Torvalds static void suspend_finish(suspend_state_t state) 1341da177e4SLinus Torvalds { 1351da177e4SLinus Torvalds device_resume(); 1361da177e4SLinus Torvalds if (pm_ops && pm_ops->finish) 1371da177e4SLinus Torvalds pm_ops->finish(state); 1381da177e4SLinus Torvalds thaw_processes(); 1395a72e04dSLi Shaohua enable_nonboot_cpus(); 1401da177e4SLinus Torvalds pm_restore_console(); 1411da177e4SLinus Torvalds } 1421da177e4SLinus Torvalds 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds 1451da177e4SLinus Torvalds 14657c4ce3cSPavel Machek static char *pm_states[PM_SUSPEND_MAX] = { 1471da177e4SLinus Torvalds [PM_SUSPEND_STANDBY] = "standby", 1481da177e4SLinus Torvalds [PM_SUSPEND_MEM] = "mem", 14957c4ce3cSPavel Machek #ifdef CONFIG_SOFTWARE_SUSPEND 1501da177e4SLinus Torvalds [PM_SUSPEND_DISK] = "disk", 15157c4ce3cSPavel Machek #endif 1521da177e4SLinus Torvalds }; 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds 1551da177e4SLinus Torvalds /** 1561da177e4SLinus Torvalds * enter_state - Do common work of entering low-power state. 1571da177e4SLinus Torvalds * @state: pm_state structure for state we're entering. 1581da177e4SLinus Torvalds * 1591da177e4SLinus Torvalds * Make sure we're the only ones trying to enter a sleep state. Fail 1601da177e4SLinus Torvalds * if someone has beat us to it, since we don't want anything weird to 1611da177e4SLinus Torvalds * happen when we wake up. 1621da177e4SLinus Torvalds * Then, do the setup for suspend, enter the state, and cleaup (after 1631da177e4SLinus Torvalds * we've woken up). 1641da177e4SLinus Torvalds */ 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds static int enter_state(suspend_state_t state) 1671da177e4SLinus Torvalds { 1681da177e4SLinus Torvalds int error; 1691da177e4SLinus Torvalds 170*eb9289ebSShaohua Li if (pm_ops->valid && !pm_ops->valid(state)) 171*eb9289ebSShaohua Li return -ENODEV; 1721da177e4SLinus Torvalds if (down_trylock(&pm_sem)) 1731da177e4SLinus Torvalds return -EBUSY; 1741da177e4SLinus Torvalds 1751da177e4SLinus Torvalds if (state == PM_SUSPEND_DISK) { 1761da177e4SLinus Torvalds error = pm_suspend_disk(); 1771da177e4SLinus Torvalds goto Unlock; 1781da177e4SLinus Torvalds } 1791da177e4SLinus Torvalds 18082428b62SDavid Brownell pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); 1811da177e4SLinus Torvalds if ((error = suspend_prepare(state))) 1821da177e4SLinus Torvalds goto Unlock; 1831da177e4SLinus Torvalds 18482428b62SDavid Brownell pr_debug("PM: Entering %s sleep\n", pm_states[state]); 1851da177e4SLinus Torvalds error = suspend_enter(state); 1861da177e4SLinus Torvalds 18782428b62SDavid Brownell pr_debug("PM: Finishing wakeup.\n"); 1881da177e4SLinus Torvalds suspend_finish(state); 1891da177e4SLinus Torvalds Unlock: 1901da177e4SLinus Torvalds up(&pm_sem); 1911da177e4SLinus Torvalds return error; 1921da177e4SLinus Torvalds } 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds /* 1951da177e4SLinus Torvalds * This is main interface to the outside world. It needs to be 1961da177e4SLinus Torvalds * called from process context. 1971da177e4SLinus Torvalds */ 1981da177e4SLinus Torvalds int software_suspend(void) 1991da177e4SLinus Torvalds { 2001da177e4SLinus Torvalds return enter_state(PM_SUSPEND_DISK); 2011da177e4SLinus Torvalds } 2021da177e4SLinus Torvalds 2031da177e4SLinus Torvalds 2041da177e4SLinus Torvalds /** 2051da177e4SLinus Torvalds * pm_suspend - Externally visible function for suspending system. 2061da177e4SLinus Torvalds * @state: Enumarted value of state to enter. 2071da177e4SLinus Torvalds * 2081da177e4SLinus Torvalds * Determine whether or not value is within range, get state 2091da177e4SLinus Torvalds * structure, and enter (above). 2101da177e4SLinus Torvalds */ 2111da177e4SLinus Torvalds 2121da177e4SLinus Torvalds int pm_suspend(suspend_state_t state) 2131da177e4SLinus Torvalds { 214e2a5b420SAlexey Starikovskiy if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX) 2151da177e4SLinus Torvalds return enter_state(state); 2161da177e4SLinus Torvalds return -EINVAL; 2171da177e4SLinus Torvalds } 2181da177e4SLinus Torvalds 2191da177e4SLinus Torvalds 2201da177e4SLinus Torvalds 2211da177e4SLinus Torvalds decl_subsys(power,NULL,NULL); 2221da177e4SLinus Torvalds 2231da177e4SLinus Torvalds 2241da177e4SLinus Torvalds /** 2251da177e4SLinus Torvalds * state - control system power state. 2261da177e4SLinus Torvalds * 2271da177e4SLinus Torvalds * show() returns what states are supported, which is hard-coded to 2281da177e4SLinus Torvalds * 'standby' (Power-On Suspend), 'mem' (Suspend-to-RAM), and 2291da177e4SLinus Torvalds * 'disk' (Suspend-to-Disk). 2301da177e4SLinus Torvalds * 2311da177e4SLinus Torvalds * store() accepts one of those strings, translates it into the 2321da177e4SLinus Torvalds * proper enumerated value, and initiates a suspend transition. 2331da177e4SLinus Torvalds */ 2341da177e4SLinus Torvalds 2351da177e4SLinus Torvalds static ssize_t state_show(struct subsystem * subsys, char * buf) 2361da177e4SLinus Torvalds { 2371da177e4SLinus Torvalds int i; 2381da177e4SLinus Torvalds char * s = buf; 2391da177e4SLinus Torvalds 2401da177e4SLinus Torvalds for (i = 0; i < PM_SUSPEND_MAX; i++) { 241*eb9289ebSShaohua Li if (pm_states[i] && pm_ops && (!pm_ops->valid 242*eb9289ebSShaohua Li ||(pm_ops->valid && pm_ops->valid(i)))) 2431da177e4SLinus Torvalds s += sprintf(s,"%s ",pm_states[i]); 2441da177e4SLinus Torvalds } 2451da177e4SLinus Torvalds s += sprintf(s,"\n"); 2461da177e4SLinus Torvalds return (s - buf); 2471da177e4SLinus Torvalds } 2481da177e4SLinus Torvalds 2491da177e4SLinus Torvalds static ssize_t state_store(struct subsystem * subsys, const char * buf, size_t n) 2501da177e4SLinus Torvalds { 2511da177e4SLinus Torvalds suspend_state_t state = PM_SUSPEND_STANDBY; 2521da177e4SLinus Torvalds char ** s; 2531da177e4SLinus Torvalds char *p; 2541da177e4SLinus Torvalds int error; 2551da177e4SLinus Torvalds int len; 2561da177e4SLinus Torvalds 2571da177e4SLinus Torvalds p = memchr(buf, '\n', n); 2581da177e4SLinus Torvalds len = p ? p - buf : n; 2591da177e4SLinus Torvalds 2601da177e4SLinus Torvalds for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) { 2611da177e4SLinus Torvalds if (*s && !strncmp(buf, *s, len)) 2621da177e4SLinus Torvalds break; 2631da177e4SLinus Torvalds } 2641da177e4SLinus Torvalds if (*s) 2651da177e4SLinus Torvalds error = enter_state(state); 2661da177e4SLinus Torvalds else 2671da177e4SLinus Torvalds error = -EINVAL; 2681da177e4SLinus Torvalds return error ? error : n; 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds 2711da177e4SLinus Torvalds power_attr(state); 2721da177e4SLinus Torvalds 2731da177e4SLinus Torvalds static struct attribute * g[] = { 2741da177e4SLinus Torvalds &state_attr.attr, 2751da177e4SLinus Torvalds NULL, 2761da177e4SLinus Torvalds }; 2771da177e4SLinus Torvalds 2781da177e4SLinus Torvalds static struct attribute_group attr_group = { 2791da177e4SLinus Torvalds .attrs = g, 2801da177e4SLinus Torvalds }; 2811da177e4SLinus Torvalds 2821da177e4SLinus Torvalds 2831da177e4SLinus Torvalds static int __init pm_init(void) 2841da177e4SLinus Torvalds { 2851da177e4SLinus Torvalds int error = subsystem_register(&power_subsys); 2861da177e4SLinus Torvalds if (!error) 2871da177e4SLinus Torvalds error = sysfs_create_group(&power_subsys.kset.kobj,&attr_group); 2881da177e4SLinus Torvalds return error; 2891da177e4SLinus Torvalds } 2901da177e4SLinus Torvalds 2911da177e4SLinus Torvalds core_initcall(pm_init); 292