diff options
Diffstat (limited to 'kernel/power/main.c')
-rw-r--r-- | kernel/power/main.c | 171 |
1 files changed, 145 insertions, 26 deletions
diff --git a/kernel/power/main.c b/kernel/power/main.c index efc08360e62..6a6d5eb3524 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -24,13 +24,112 @@ #include "power.h" -BLOCKING_NOTIFIER_HEAD(pm_chain_head); - DEFINE_MUTEX(pm_mutex); unsigned int pm_flags; EXPORT_SYMBOL(pm_flags); +#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; +} + +#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", +}; + +static ssize_t pm_test_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + 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); +} + +static ssize_t pm_test_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n) +{ + 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 */ + +#endif /* CONFIG_PM_SLEEP */ + #ifdef CONFIG_SUSPEND /* This is just an arbitrary number */ @@ -76,13 +175,13 @@ static int suspend_prepare(void) if (!suspend_ops || !suspend_ops->enter) return -EPERM; + pm_prepare_console(); + error = pm_notifier_call_chain(PM_SUSPEND_PREPARE); if (error) goto Finish; - pm_prepare_console(); - - if (freeze_processes()) { + if (suspend_freeze_processes()) { error = -EAGAIN; goto Thaw; } @@ -100,10 +199,10 @@ static int suspend_prepare(void) return 0; Thaw: - thaw_processes(); - pm_restore_console(); + suspend_thaw_processes(); Finish: pm_notifier_call_chain(PM_POST_SUSPEND); + pm_restore_console(); return error; } @@ -133,10 +232,13 @@ static int suspend_enter(suspend_state_t state) BUG_ON(!irqs_disabled()); if ((error = device_power_down(PMSG_SUSPEND))) { - printk(KERN_ERR "Some devices failed to power down\n"); + printk(KERN_ERR "PM: Some devices failed to power down\n"); goto Done; } - error = suspend_ops->enter(state); + + if (!suspend_test(TEST_CORE)) + error = suspend_ops->enter(state); + device_power_up(); Done: arch_suspend_enable_irqs(); @@ -145,8 +247,8 @@ static int suspend_enter(suspend_state_t state) } /** - * suspend_devices_and_enter - suspend devices and enter the desired system sleep - * state. + * suspend_devices_and_enter - suspend devices and enter the desired system + * sleep state. * @state: state to enter */ int suspend_devices_and_enter(suspend_state_t state) @@ -156,33 +258,45 @@ int suspend_devices_and_enter(suspend_state_t state) if (!suspend_ops) return -ENOSYS; - if (suspend_ops->set_target) { - error = suspend_ops->set_target(state); + if (suspend_ops->begin) { + error = suspend_ops->begin(state); if (error) - return error; + goto Close; } suspend_console(); error = device_suspend(PMSG_SUSPEND); if (error) { - printk(KERN_ERR "Some devices failed to suspend\n"); + printk(KERN_ERR "PM: Some devices failed to suspend\n"); goto Resume_console; } + + if (suspend_test(TEST_DEVICES)) + goto Resume_devices; + if (suspend_ops->prepare) { error = suspend_ops->prepare(); if (error) goto Resume_devices; } + + if (suspend_test(TEST_PLATFORM)) + goto Finish; + error = disable_nonboot_cpus(); - if (!error) + if (!error && !suspend_test(TEST_CPUS)) suspend_enter(state); enable_nonboot_cpus(); + Finish: if (suspend_ops->finish) suspend_ops->finish(); Resume_devices: device_resume(); Resume_console: resume_console(); + Close: + if (suspend_ops->end) + suspend_ops->end(); return error; } @@ -194,9 +308,9 @@ int suspend_devices_and_enter(suspend_state_t state) */ static void suspend_finish(void) { - thaw_processes(); - pm_restore_console(); + suspend_thaw_processes(); pm_notifier_call_chain(PM_POST_SUSPEND); + pm_restore_console(); } @@ -238,17 +352,22 @@ static int enter_state(suspend_state_t state) if (!mutex_trylock(&pm_mutex)) return -EBUSY; - printk("Syncing filesystems ... "); + printk(KERN_INFO "PM: Syncing filesystems ... "); sys_sync(); printk("done.\n"); pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); - if ((error = suspend_prepare())) + error = suspend_prepare(); + if (error) goto Unlock; + if (suspend_test(TEST_FREEZER)) + goto Finish; + pr_debug("PM: Entering %s sleep\n", pm_states[state]); error = suspend_devices_and_enter(state); + Finish: pr_debug("PM: Finishing wakeup.\n"); suspend_finish(); Unlock: @@ -369,18 +488,18 @@ pm_trace_store(struct kobject *kobj, struct kobj_attribute *attr, } power_attr(pm_trace); +#endif /* CONFIG_PM_TRACE */ static struct attribute * g[] = { &state_attr.attr, +#ifdef CONFIG_PM_TRACE &pm_trace_attr.attr, +#endif +#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_DEBUG) + &pm_test_attr.attr, +#endif NULL, }; -#else -static struct attribute * g[] = { - &state_attr.attr, - NULL, -}; -#endif /* CONFIG_PM_TRACE */ static struct attribute_group attr_group = { .attrs = g, |