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