main.c 11.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10
/*
 * kernel/power/main.c - PM subsystem core functionality.
 *
 * Copyright (c) 2003 Patrick Mochel
 * Copyright (c) 2003 Open Source Development Lab
 * 
 * This file is released under the GPLv2
 *
 */

11
#include <linux/module.h>
L
Linus Torvalds 已提交
12 13 14 15 16 17
#include <linux/suspend.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/init.h>
18
#include <linux/console.h>
19
#include <linux/cpu.h>
20
#include <linux/resume-trace.h>
21
#include <linux/freezer.h>
C
Christoph Lameter 已提交
22
#include <linux/vmstat.h>
23
#include <linux/syscalls.h>
L
Linus Torvalds 已提交
24 25 26

#include "power.h"

27
DEFINE_MUTEX(pm_mutex);
L
Linus Torvalds 已提交
28

29 30 31
unsigned int pm_flags;
EXPORT_SYMBOL(pm_flags);

32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
#ifdef CONFIG_PM_SLEEP

/* Routines for PM-transition notifications */

static BLOCKING_NOTIFIER_HEAD(pm_chain_head);

int register_pm_notifier(struct notifier_block *nb)
{
	return blocking_notifier_chain_register(&pm_chain_head, nb);
}
EXPORT_SYMBOL_GPL(register_pm_notifier);

int unregister_pm_notifier(struct notifier_block *nb)
{
	return blocking_notifier_chain_unregister(&pm_chain_head, nb);
}
EXPORT_SYMBOL_GPL(unregister_pm_notifier);

int pm_notifier_call_chain(unsigned long val)
{
	return (blocking_notifier_call_chain(&pm_chain_head, val, NULL)
			== NOTIFY_BAD) ? -EINVAL : 0;
}

56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
#ifdef CONFIG_PM_DEBUG
int pm_test_level = TEST_NONE;

static int suspend_test(int level)
{
	if (pm_test_level == level) {
		printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n");
		mdelay(5000);
		return 1;
	}
	return 0;
}

static const char * const pm_tests[__TEST_AFTER_LAST] = {
	[TEST_NONE] = "none",
	[TEST_CORE] = "core",
	[TEST_CPUS] = "processors",
	[TEST_PLATFORM] = "platform",
	[TEST_DEVICES] = "devices",
	[TEST_FREEZER] = "freezer",
};

78 79
static ssize_t pm_test_show(struct kobject *kobj, struct kobj_attribute *attr,
				char *buf)
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
{
	char *s = buf;
	int level;

	for (level = TEST_FIRST; level <= TEST_MAX; level++)
		if (pm_tests[level]) {
			if (level == pm_test_level)
				s += sprintf(s, "[%s] ", pm_tests[level]);
			else
				s += sprintf(s, "%s ", pm_tests[level]);
		}

	if (s != buf)
		/* convert the last space to a newline */
		*(s-1) = '\n';

	return (s - buf);
}

99 100
static ssize_t pm_test_store(struct kobject *kobj, struct kobj_attribute *attr,
				const char *buf, size_t n)
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
{
	const char * const *s;
	int level;
	char *p;
	int len;
	int error = -EINVAL;

	p = memchr(buf, '\n', n);
	len = p ? p - buf : n;

	mutex_lock(&pm_mutex);

	level = TEST_FIRST;
	for (s = &pm_tests[level]; level <= TEST_MAX; s++, level++)
		if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) {
			pm_test_level = level;
			error = 0;
			break;
		}

	mutex_unlock(&pm_mutex);

	return error ? error : n;
}

power_attr(pm_test);
#else /* !CONFIG_PM_DEBUG */
static inline int suspend_test(int level) { return 0; }
#endif /* !CONFIG_PM_DEBUG */

131
#endif /* CONFIG_PM_SLEEP */
132

133 134 135 136 137
#ifdef CONFIG_SUSPEND

/* This is just an arbitrary number */
#define FREE_PAGE_NUMBER (100)

R
Rafael J. Wysocki 已提交
138
static struct platform_suspend_ops *suspend_ops;
L
Linus Torvalds 已提交
139 140

/**
141
 *	suspend_set_ops - Set the global suspend method table.
L
Linus Torvalds 已提交
142 143 144
 *	@ops:	Pointer to ops structure.
 */

145
void suspend_set_ops(struct platform_suspend_ops *ops)
L
Linus Torvalds 已提交
146
{
147
	mutex_lock(&pm_mutex);
148
	suspend_ops = ops;
149
	mutex_unlock(&pm_mutex);
L
Linus Torvalds 已提交
150 151
}

152
/**
153
 * suspend_valid_only_mem - generic memory-only valid callback
154
 *
155
 * Platform drivers that implement mem suspend only and only need
156 157 158
 * to check for that in their .valid callback can use this instead
 * of rolling their own .valid callback.
 */
159
int suspend_valid_only_mem(suspend_state_t state)
160 161 162 163
{
	return state == PM_SUSPEND_MEM;
}

L
Linus Torvalds 已提交
164 165 166
/**
 *	suspend_prepare - Do prep work before entering low-power state.
 *
167 168
 *	This is common code that is called for each state that we're entering.
 *	Run suspend notifiers, allocate a console and stop all processes.
L
Linus Torvalds 已提交
169
 */
170
static int suspend_prepare(void)
L
Linus Torvalds 已提交
171
{
172
	int error;
D
David Shaohua Li 已提交
173
	unsigned int free_pages;
L
Linus Torvalds 已提交
174

175
	if (!suspend_ops || !suspend_ops->enter)
L
Linus Torvalds 已提交
176 177
		return -EPERM;

178 179
	pm_prepare_console();

180 181 182 183
	error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
	if (error)
		goto Finish;

184
	if (suspend_freeze_processes()) {
L
Linus Torvalds 已提交
185 186 187 188
		error = -EAGAIN;
		goto Thaw;
	}

189 190
	free_pages = global_page_state(NR_FREE_PAGES);
	if (free_pages < FREE_PAGE_NUMBER) {
D
David Shaohua Li 已提交
191 192 193 194 195 196 197
		pr_debug("PM: free some memory\n");
		shrink_all_memory(FREE_PAGE_NUMBER - free_pages);
		if (nr_free_pages() < FREE_PAGE_NUMBER) {
			error = -ENOMEM;
			printk(KERN_ERR "PM: No enough memory\n");
		}
	}
198 199 200
	if (!error)
		return 0;

L
Linus Torvalds 已提交
201
 Thaw:
202
	suspend_thaw_processes();
203 204
 Finish:
	pm_notifier_call_chain(PM_POST_SUSPEND);
205
	pm_restore_console();
L
Linus Torvalds 已提交
206 207 208
	return error;
}

209 210 211 212 213 214 215 216 217 218 219
/* default implementation */
void __attribute__ ((weak)) arch_suspend_disable_irqs(void)
{
	local_irq_disable();
}

/* default implementation */
void __attribute__ ((weak)) arch_suspend_enable_irqs(void)
{
	local_irq_enable();
}
L
Linus Torvalds 已提交
220

221 222 223 224 225 226
/**
 *	suspend_enter - enter the desired system sleep state.
 *	@state:		state to enter
 *
 *	This function should be called after devices have been suspended.
 */
227
static int suspend_enter(suspend_state_t state)
L
Linus Torvalds 已提交
228 229 230
{
	int error = 0;

231
	device_pm_lock();
232 233
	arch_suspend_disable_irqs();
	BUG_ON(!irqs_disabled());
L
Linus Torvalds 已提交
234 235

	if ((error = device_power_down(PMSG_SUSPEND))) {
236
		printk(KERN_ERR "PM: Some devices failed to power down\n");
L
Linus Torvalds 已提交
237 238
		goto Done;
	}
239 240 241 242

	if (!suspend_test(TEST_CORE))
		error = suspend_ops->enter(state);

243
	device_power_up(PMSG_RESUME);
L
Linus Torvalds 已提交
244
 Done:
245 246
	arch_suspend_enable_irqs();
	BUG_ON(irqs_disabled());
247
	device_pm_unlock();
L
Linus Torvalds 已提交
248 249 250
	return error;
}

251
/**
252 253
 *	suspend_devices_and_enter - suspend devices and enter the desired system
 *				    sleep state.
254 255 256 257 258 259
 *	@state:		  state to enter
 */
int suspend_devices_and_enter(suspend_state_t state)
{
	int error;

260
	if (!suspend_ops)
261 262
		return -ENOSYS;

263 264
	if (suspend_ops->begin) {
		error = suspend_ops->begin(state);
265
		if (error)
266
			goto Close;
267 268 269 270
	}
	suspend_console();
	error = device_suspend(PMSG_SUSPEND);
	if (error) {
271
		printk(KERN_ERR "PM: Some devices failed to suspend\n");
272
		goto Recover_platform;
273
	}
274 275

	if (suspend_test(TEST_DEVICES))
276
		goto Recover_platform;
277

278
	if (suspend_ops->prepare) {
279
		error = suspend_ops->prepare();
280 281 282
		if (error)
			goto Resume_devices;
	}
283 284 285 286

	if (suspend_test(TEST_PLATFORM))
		goto Finish;

287
	error = disable_nonboot_cpus();
288
	if (!error && !suspend_test(TEST_CPUS))
289 290 291
		suspend_enter(state);

	enable_nonboot_cpus();
292
 Finish:
293 294
	if (suspend_ops->finish)
		suspend_ops->finish();
295
 Resume_devices:
296
	device_resume(PMSG_RESUME);
297
	resume_console();
298 299 300
 Close:
	if (suspend_ops->end)
		suspend_ops->end();
301
	return error;
302 303 304 305 306

 Recover_platform:
	if (suspend_ops->recover)
		suspend_ops->recover();
	goto Resume_devices;
307
}
L
Linus Torvalds 已提交
308 309 310 311 312 313 314

/**
 *	suspend_finish - Do final work before exiting suspend sequence.
 *
 *	Call platform code to clean up, restart processes, and free the 
 *	console that we've allocated. This is not called for suspend-to-disk.
 */
315
static void suspend_finish(void)
L
Linus Torvalds 已提交
316
{
317
	suspend_thaw_processes();
318
	pm_notifier_call_chain(PM_POST_SUSPEND);
319
	pm_restore_console();
L
Linus Torvalds 已提交
320 321 322 323 324
}




325
static const char * const pm_states[PM_SUSPEND_MAX] = {
L
Linus Torvalds 已提交
326 327 328 329
	[PM_SUSPEND_STANDBY]	= "standby",
	[PM_SUSPEND_MEM]	= "mem",
};

330 331
static inline int valid_state(suspend_state_t state)
{
332 333
	/* All states need lowlevel support and need to be valid
	 * to the lowlevel implementation, no valid callback
334
	 * implies that none are valid. */
335
	if (!suspend_ops || !suspend_ops->valid || !suspend_ops->valid(state))
336 337 338 339
		return 0;
	return 1;
}

L
Linus Torvalds 已提交
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354

/**
 *	enter_state - Do common work of entering low-power state.
 *	@state:		pm_state structure for state we're entering.
 *
 *	Make sure we're the only ones trying to enter a sleep state. Fail
 *	if someone has beat us to it, since we don't want anything weird to
 *	happen when we wake up.
 *	Then, do the setup for suspend, enter the state, and cleaup (after
 *	we've woken up).
 */
static int enter_state(suspend_state_t state)
{
	int error;

355
	if (!valid_state(state))
356
		return -ENODEV;
357

358
	if (!mutex_trylock(&pm_mutex))
L
Linus Torvalds 已提交
359 360
		return -EBUSY;

361
	printk(KERN_INFO "PM: Syncing filesystems ... ");
362 363 364
	sys_sync();
	printk("done.\n");

365
	pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
366 367
	error = suspend_prepare();
	if (error)
L
Linus Torvalds 已提交
368 369
		goto Unlock;

370 371 372
	if (suspend_test(TEST_FREEZER))
		goto Finish;

373
	pr_debug("PM: Entering %s sleep\n", pm_states[state]);
374
	error = suspend_devices_and_enter(state);
L
Linus Torvalds 已提交
375

376
 Finish:
377
	pr_debug("PM: Finishing wakeup.\n");
378
	suspend_finish();
L
Linus Torvalds 已提交
379
 Unlock:
380
	mutex_unlock(&pm_mutex);
L
Linus Torvalds 已提交
381 382 383 384 385 386
	return error;
}


/**
 *	pm_suspend - Externally visible function for suspending system.
387
 *	@state:		Enumerated value of state to enter.
L
Linus Torvalds 已提交
388 389 390 391 392 393 394
 *
 *	Determine whether or not value is within range, get state 
 *	structure, and enter (above).
 */

int pm_suspend(suspend_state_t state)
{
A
Alexey Starikovskiy 已提交
395
	if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)
L
Linus Torvalds 已提交
396 397 398 399
		return enter_state(state);
	return -EINVAL;
}

400
EXPORT_SYMBOL(pm_suspend);
L
Linus Torvalds 已提交
401

402 403
#endif /* CONFIG_SUSPEND */

404
struct kobject *power_kobj;
L
Linus Torvalds 已提交
405 406 407 408 409 410 411 412 413 414 415 416

/**
 *	state - control system power state.
 *
 *	show() returns what states are supported, which is hard-coded to
 *	'standby' (Power-On Suspend), 'mem' (Suspend-to-RAM), and
 *	'disk' (Suspend-to-Disk).
 *
 *	store() accepts one of those strings, translates it into the 
 *	proper enumerated value, and initiates a suspend transition.
 */

417 418
static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
			  char *buf)
L
Linus Torvalds 已提交
419
{
420 421
	char *s = buf;
#ifdef CONFIG_SUSPEND
L
Linus Torvalds 已提交
422 423 424
	int i;

	for (i = 0; i < PM_SUSPEND_MAX; i++) {
425 426
		if (pm_states[i] && valid_state(i))
			s += sprintf(s,"%s ", pm_states[i]);
L
Linus Torvalds 已提交
427
	}
428
#endif
429
#ifdef CONFIG_HIBERNATION
430 431 432 433 434 435
	s += sprintf(s, "%s\n", "disk");
#else
	if (s != buf)
		/* convert the last space to a newline */
		*(s-1) = '\n';
#endif
L
Linus Torvalds 已提交
436 437 438
	return (s - buf);
}

439 440
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
			   const char *buf, size_t n)
L
Linus Torvalds 已提交
441
{
442
#ifdef CONFIG_SUSPEND
L
Linus Torvalds 已提交
443
	suspend_state_t state = PM_SUSPEND_STANDBY;
444
	const char * const *s;
445
#endif
L
Linus Torvalds 已提交
446 447
	char *p;
	int len;
448
	int error = -EINVAL;
L
Linus Torvalds 已提交
449 450 451 452

	p = memchr(buf, '\n', n);
	len = p ? p - buf : n;

453
	/* First, check if we are requested to hibernate */
R
Rafael J. Wysocki 已提交
454
	if (len == 4 && !strncmp(buf, "disk", len)) {
455
		error = hibernate();
456
  goto Exit;
457 458
	}

459
#ifdef CONFIG_SUSPEND
L
Linus Torvalds 已提交
460
	for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
R
Rafael J. Wysocki 已提交
461
		if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
L
Linus Torvalds 已提交
462 463
			break;
	}
464
	if (state < PM_SUSPEND_MAX && *s)
L
Linus Torvalds 已提交
465
		error = enter_state(state);
466 467 468
#endif

 Exit:
L
Linus Torvalds 已提交
469 470 471 472 473
	return error ? error : n;
}

power_attr(state);

474 475 476
#ifdef CONFIG_PM_TRACE
int pm_trace_enabled;

477 478
static ssize_t pm_trace_show(struct kobject *kobj, struct kobj_attribute *attr,
			     char *buf)
479 480 481 482 483
{
	return sprintf(buf, "%d\n", pm_trace_enabled);
}

static ssize_t
484 485
pm_trace_store(struct kobject *kobj, struct kobj_attribute *attr,
	       const char *buf, size_t n)
486 487 488 489 490 491 492 493 494 495 496
{
	int val;

	if (sscanf(buf, "%d", &val) == 1) {
		pm_trace_enabled = !!val;
		return n;
	}
	return -EINVAL;
}

power_attr(pm_trace);
497
#endif /* CONFIG_PM_TRACE */
498 499 500

static struct attribute * g[] = {
	&state_attr.attr,
501
#ifdef CONFIG_PM_TRACE
502
	&pm_trace_attr.attr,
503
#endif
504
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_DEBUG)
505 506
	&pm_test_attr.attr,
#endif
507 508
	NULL,
};
L
Linus Torvalds 已提交
509 510 511 512 513 514 515 516

static struct attribute_group attr_group = {
	.attrs = g,
};


static int __init pm_init(void)
{
517 518
	power_kobj = kobject_create_and_add("power", NULL);
	if (!power_kobj)
519
		return -ENOMEM;
520
	return sysfs_create_group(power_kobj, &attr_group);
L
Linus Torvalds 已提交
521 522 523
}

core_initcall(pm_init);