1*1da177e4SLinus Torvalds /* 2*1da177e4SLinus Torvalds * kernel/power/main.c - PM subsystem core functionality. 3*1da177e4SLinus Torvalds * 4*1da177e4SLinus Torvalds * Copyright (c) 2003 Patrick Mochel 5*1da177e4SLinus Torvalds * Copyright (c) 2003 Open Source Development Lab 6*1da177e4SLinus Torvalds * 7*1da177e4SLinus Torvalds * This file is released under the GPLv2 8*1da177e4SLinus Torvalds * 9*1da177e4SLinus Torvalds */ 10*1da177e4SLinus Torvalds 11*1da177e4SLinus Torvalds #include <linux/suspend.h> 12*1da177e4SLinus Torvalds #include <linux/kobject.h> 13*1da177e4SLinus Torvalds #include <linux/string.h> 14*1da177e4SLinus Torvalds #include <linux/delay.h> 15*1da177e4SLinus Torvalds #include <linux/errno.h> 16*1da177e4SLinus Torvalds #include <linux/init.h> 17*1da177e4SLinus Torvalds #include <linux/pm.h> 18*1da177e4SLinus Torvalds 19*1da177e4SLinus Torvalds 20*1da177e4SLinus Torvalds #include "power.h" 21*1da177e4SLinus Torvalds 22*1da177e4SLinus Torvalds DECLARE_MUTEX(pm_sem); 23*1da177e4SLinus Torvalds 24*1da177e4SLinus Torvalds struct pm_ops * pm_ops = NULL; 25*1da177e4SLinus Torvalds suspend_disk_method_t pm_disk_mode = PM_DISK_SHUTDOWN; 26*1da177e4SLinus Torvalds 27*1da177e4SLinus Torvalds /** 28*1da177e4SLinus Torvalds * pm_set_ops - Set the global power method table. 29*1da177e4SLinus Torvalds * @ops: Pointer to ops structure. 30*1da177e4SLinus Torvalds */ 31*1da177e4SLinus Torvalds 32*1da177e4SLinus Torvalds void pm_set_ops(struct pm_ops * ops) 33*1da177e4SLinus Torvalds { 34*1da177e4SLinus Torvalds down(&pm_sem); 35*1da177e4SLinus Torvalds pm_ops = ops; 36*1da177e4SLinus Torvalds up(&pm_sem); 37*1da177e4SLinus Torvalds } 38*1da177e4SLinus Torvalds 39*1da177e4SLinus Torvalds 40*1da177e4SLinus Torvalds /** 41*1da177e4SLinus Torvalds * suspend_prepare - Do prep work before entering low-power state. 42*1da177e4SLinus Torvalds * @state: State we're entering. 43*1da177e4SLinus Torvalds * 44*1da177e4SLinus Torvalds * This is common code that is called for each state that we're 45*1da177e4SLinus Torvalds * entering. Allocate a console, stop all processes, then make sure 46*1da177e4SLinus Torvalds * the platform can enter the requested state. 47*1da177e4SLinus Torvalds */ 48*1da177e4SLinus Torvalds 49*1da177e4SLinus Torvalds static int suspend_prepare(suspend_state_t state) 50*1da177e4SLinus Torvalds { 51*1da177e4SLinus Torvalds int error = 0; 52*1da177e4SLinus Torvalds 53*1da177e4SLinus Torvalds if (!pm_ops || !pm_ops->enter) 54*1da177e4SLinus Torvalds return -EPERM; 55*1da177e4SLinus Torvalds 56*1da177e4SLinus Torvalds pm_prepare_console(); 57*1da177e4SLinus Torvalds 58*1da177e4SLinus Torvalds if (freeze_processes()) { 59*1da177e4SLinus Torvalds error = -EAGAIN; 60*1da177e4SLinus Torvalds goto Thaw; 61*1da177e4SLinus Torvalds } 62*1da177e4SLinus Torvalds 63*1da177e4SLinus Torvalds if (pm_ops->prepare) { 64*1da177e4SLinus Torvalds if ((error = pm_ops->prepare(state))) 65*1da177e4SLinus Torvalds goto Thaw; 66*1da177e4SLinus Torvalds } 67*1da177e4SLinus Torvalds 68*1da177e4SLinus Torvalds if ((error = device_suspend(PMSG_SUSPEND))) { 69*1da177e4SLinus Torvalds printk(KERN_ERR "Some devices failed to suspend\n"); 70*1da177e4SLinus Torvalds goto Finish; 71*1da177e4SLinus Torvalds } 72*1da177e4SLinus Torvalds return 0; 73*1da177e4SLinus Torvalds Finish: 74*1da177e4SLinus Torvalds if (pm_ops->finish) 75*1da177e4SLinus Torvalds pm_ops->finish(state); 76*1da177e4SLinus Torvalds Thaw: 77*1da177e4SLinus Torvalds thaw_processes(); 78*1da177e4SLinus Torvalds pm_restore_console(); 79*1da177e4SLinus Torvalds return error; 80*1da177e4SLinus Torvalds } 81*1da177e4SLinus Torvalds 82*1da177e4SLinus Torvalds 83*1da177e4SLinus Torvalds static int suspend_enter(suspend_state_t state) 84*1da177e4SLinus Torvalds { 85*1da177e4SLinus Torvalds int error = 0; 86*1da177e4SLinus Torvalds unsigned long flags; 87*1da177e4SLinus Torvalds 88*1da177e4SLinus Torvalds local_irq_save(flags); 89*1da177e4SLinus Torvalds 90*1da177e4SLinus Torvalds if ((error = device_power_down(PMSG_SUSPEND))) { 91*1da177e4SLinus Torvalds printk(KERN_ERR "Some devices failed to power down\n"); 92*1da177e4SLinus Torvalds goto Done; 93*1da177e4SLinus Torvalds } 94*1da177e4SLinus Torvalds error = pm_ops->enter(state); 95*1da177e4SLinus Torvalds device_power_up(); 96*1da177e4SLinus Torvalds Done: 97*1da177e4SLinus Torvalds local_irq_restore(flags); 98*1da177e4SLinus Torvalds return error; 99*1da177e4SLinus Torvalds } 100*1da177e4SLinus Torvalds 101*1da177e4SLinus Torvalds 102*1da177e4SLinus Torvalds /** 103*1da177e4SLinus Torvalds * suspend_finish - Do final work before exiting suspend sequence. 104*1da177e4SLinus Torvalds * @state: State we're coming out of. 105*1da177e4SLinus Torvalds * 106*1da177e4SLinus Torvalds * Call platform code to clean up, restart processes, and free the 107*1da177e4SLinus Torvalds * console that we've allocated. This is not called for suspend-to-disk. 108*1da177e4SLinus Torvalds */ 109*1da177e4SLinus Torvalds 110*1da177e4SLinus Torvalds static void suspend_finish(suspend_state_t state) 111*1da177e4SLinus Torvalds { 112*1da177e4SLinus Torvalds device_resume(); 113*1da177e4SLinus Torvalds if (pm_ops && pm_ops->finish) 114*1da177e4SLinus Torvalds pm_ops->finish(state); 115*1da177e4SLinus Torvalds thaw_processes(); 116*1da177e4SLinus Torvalds pm_restore_console(); 117*1da177e4SLinus Torvalds } 118*1da177e4SLinus Torvalds 119*1da177e4SLinus Torvalds 120*1da177e4SLinus Torvalds 121*1da177e4SLinus Torvalds 122*1da177e4SLinus Torvalds static char * pm_states[] = { 123*1da177e4SLinus Torvalds [PM_SUSPEND_STANDBY] = "standby", 124*1da177e4SLinus Torvalds [PM_SUSPEND_MEM] = "mem", 125*1da177e4SLinus Torvalds [PM_SUSPEND_DISK] = "disk", 126*1da177e4SLinus Torvalds NULL, 127*1da177e4SLinus Torvalds }; 128*1da177e4SLinus Torvalds 129*1da177e4SLinus Torvalds 130*1da177e4SLinus Torvalds /** 131*1da177e4SLinus Torvalds * enter_state - Do common work of entering low-power state. 132*1da177e4SLinus Torvalds * @state: pm_state structure for state we're entering. 133*1da177e4SLinus Torvalds * 134*1da177e4SLinus Torvalds * Make sure we're the only ones trying to enter a sleep state. Fail 135*1da177e4SLinus Torvalds * if someone has beat us to it, since we don't want anything weird to 136*1da177e4SLinus Torvalds * happen when we wake up. 137*1da177e4SLinus Torvalds * Then, do the setup for suspend, enter the state, and cleaup (after 138*1da177e4SLinus Torvalds * we've woken up). 139*1da177e4SLinus Torvalds */ 140*1da177e4SLinus Torvalds 141*1da177e4SLinus Torvalds static int enter_state(suspend_state_t state) 142*1da177e4SLinus Torvalds { 143*1da177e4SLinus Torvalds int error; 144*1da177e4SLinus Torvalds 145*1da177e4SLinus Torvalds if (down_trylock(&pm_sem)) 146*1da177e4SLinus Torvalds return -EBUSY; 147*1da177e4SLinus Torvalds 148*1da177e4SLinus Torvalds if (state == PM_SUSPEND_DISK) { 149*1da177e4SLinus Torvalds error = pm_suspend_disk(); 150*1da177e4SLinus Torvalds goto Unlock; 151*1da177e4SLinus Torvalds } 152*1da177e4SLinus Torvalds 153*1da177e4SLinus Torvalds /* Suspend is hard to get right on SMP. */ 154*1da177e4SLinus Torvalds if (num_online_cpus() != 1) { 155*1da177e4SLinus Torvalds error = -EPERM; 156*1da177e4SLinus Torvalds goto Unlock; 157*1da177e4SLinus Torvalds } 158*1da177e4SLinus Torvalds 159*1da177e4SLinus Torvalds pr_debug("PM: Preparing system for suspend\n"); 160*1da177e4SLinus Torvalds if ((error = suspend_prepare(state))) 161*1da177e4SLinus Torvalds goto Unlock; 162*1da177e4SLinus Torvalds 163*1da177e4SLinus Torvalds pr_debug("PM: Entering state.\n"); 164*1da177e4SLinus Torvalds error = suspend_enter(state); 165*1da177e4SLinus Torvalds 166*1da177e4SLinus Torvalds pr_debug("PM: Finishing up.\n"); 167*1da177e4SLinus Torvalds suspend_finish(state); 168*1da177e4SLinus Torvalds Unlock: 169*1da177e4SLinus Torvalds up(&pm_sem); 170*1da177e4SLinus Torvalds return error; 171*1da177e4SLinus Torvalds } 172*1da177e4SLinus Torvalds 173*1da177e4SLinus Torvalds /* 174*1da177e4SLinus Torvalds * This is main interface to the outside world. It needs to be 175*1da177e4SLinus Torvalds * called from process context. 176*1da177e4SLinus Torvalds */ 177*1da177e4SLinus Torvalds int software_suspend(void) 178*1da177e4SLinus Torvalds { 179*1da177e4SLinus Torvalds return enter_state(PM_SUSPEND_DISK); 180*1da177e4SLinus Torvalds } 181*1da177e4SLinus Torvalds 182*1da177e4SLinus Torvalds 183*1da177e4SLinus Torvalds /** 184*1da177e4SLinus Torvalds * pm_suspend - Externally visible function for suspending system. 185*1da177e4SLinus Torvalds * @state: Enumarted value of state to enter. 186*1da177e4SLinus Torvalds * 187*1da177e4SLinus Torvalds * Determine whether or not value is within range, get state 188*1da177e4SLinus Torvalds * structure, and enter (above). 189*1da177e4SLinus Torvalds */ 190*1da177e4SLinus Torvalds 191*1da177e4SLinus Torvalds int pm_suspend(suspend_state_t state) 192*1da177e4SLinus Torvalds { 193*1da177e4SLinus Torvalds if (state > PM_SUSPEND_ON && state < PM_SUSPEND_MAX) 194*1da177e4SLinus Torvalds return enter_state(state); 195*1da177e4SLinus Torvalds return -EINVAL; 196*1da177e4SLinus Torvalds } 197*1da177e4SLinus Torvalds 198*1da177e4SLinus Torvalds 199*1da177e4SLinus Torvalds 200*1da177e4SLinus Torvalds decl_subsys(power,NULL,NULL); 201*1da177e4SLinus Torvalds 202*1da177e4SLinus Torvalds 203*1da177e4SLinus Torvalds /** 204*1da177e4SLinus Torvalds * state - control system power state. 205*1da177e4SLinus Torvalds * 206*1da177e4SLinus Torvalds * show() returns what states are supported, which is hard-coded to 207*1da177e4SLinus Torvalds * 'standby' (Power-On Suspend), 'mem' (Suspend-to-RAM), and 208*1da177e4SLinus Torvalds * 'disk' (Suspend-to-Disk). 209*1da177e4SLinus Torvalds * 210*1da177e4SLinus Torvalds * store() accepts one of those strings, translates it into the 211*1da177e4SLinus Torvalds * proper enumerated value, and initiates a suspend transition. 212*1da177e4SLinus Torvalds */ 213*1da177e4SLinus Torvalds 214*1da177e4SLinus Torvalds static ssize_t state_show(struct subsystem * subsys, char * buf) 215*1da177e4SLinus Torvalds { 216*1da177e4SLinus Torvalds int i; 217*1da177e4SLinus Torvalds char * s = buf; 218*1da177e4SLinus Torvalds 219*1da177e4SLinus Torvalds for (i = 0; i < PM_SUSPEND_MAX; i++) { 220*1da177e4SLinus Torvalds if (pm_states[i]) 221*1da177e4SLinus Torvalds s += sprintf(s,"%s ",pm_states[i]); 222*1da177e4SLinus Torvalds } 223*1da177e4SLinus Torvalds s += sprintf(s,"\n"); 224*1da177e4SLinus Torvalds return (s - buf); 225*1da177e4SLinus Torvalds } 226*1da177e4SLinus Torvalds 227*1da177e4SLinus Torvalds static ssize_t state_store(struct subsystem * subsys, const char * buf, size_t n) 228*1da177e4SLinus Torvalds { 229*1da177e4SLinus Torvalds suspend_state_t state = PM_SUSPEND_STANDBY; 230*1da177e4SLinus Torvalds char ** s; 231*1da177e4SLinus Torvalds char *p; 232*1da177e4SLinus Torvalds int error; 233*1da177e4SLinus Torvalds int len; 234*1da177e4SLinus Torvalds 235*1da177e4SLinus Torvalds p = memchr(buf, '\n', n); 236*1da177e4SLinus Torvalds len = p ? p - buf : n; 237*1da177e4SLinus Torvalds 238*1da177e4SLinus Torvalds for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) { 239*1da177e4SLinus Torvalds if (*s && !strncmp(buf, *s, len)) 240*1da177e4SLinus Torvalds break; 241*1da177e4SLinus Torvalds } 242*1da177e4SLinus Torvalds if (*s) 243*1da177e4SLinus Torvalds error = enter_state(state); 244*1da177e4SLinus Torvalds else 245*1da177e4SLinus Torvalds error = -EINVAL; 246*1da177e4SLinus Torvalds return error ? error : n; 247*1da177e4SLinus Torvalds } 248*1da177e4SLinus Torvalds 249*1da177e4SLinus Torvalds power_attr(state); 250*1da177e4SLinus Torvalds 251*1da177e4SLinus Torvalds static struct attribute * g[] = { 252*1da177e4SLinus Torvalds &state_attr.attr, 253*1da177e4SLinus Torvalds NULL, 254*1da177e4SLinus Torvalds }; 255*1da177e4SLinus Torvalds 256*1da177e4SLinus Torvalds static struct attribute_group attr_group = { 257*1da177e4SLinus Torvalds .attrs = g, 258*1da177e4SLinus Torvalds }; 259*1da177e4SLinus Torvalds 260*1da177e4SLinus Torvalds 261*1da177e4SLinus Torvalds static int __init pm_init(void) 262*1da177e4SLinus Torvalds { 263*1da177e4SLinus Torvalds int error = subsystem_register(&power_subsys); 264*1da177e4SLinus Torvalds if (!error) 265*1da177e4SLinus Torvalds error = sysfs_create_group(&power_subsys.kset.kobj,&attr_group); 266*1da177e4SLinus Torvalds return error; 267*1da177e4SLinus Torvalds } 268*1da177e4SLinus Torvalds 269*1da177e4SLinus Torvalds core_initcall(pm_init); 270