linux/kernel/power/suspend.c
<<
>>
Prefs
   1/*
   2 * kernel/power/suspend.c - Suspend to RAM and standby functionality.
   3 *
   4 * Copyright (c) 2003 Patrick Mochel
   5 * Copyright (c) 2003 Open Source Development Lab
   6 * Copyright (c) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
   7 *
   8 * This file is released under the GPLv2.
   9 */
  10
  11#include <linux/string.h>
  12#include <linux/delay.h>
  13#include <linux/errno.h>
  14#include <linux/init.h>
  15#include <linux/console.h>
  16#include <linux/cpu.h>
  17#include <linux/syscalls.h>
  18
  19#include "power.h"
  20
  21const char *const pm_states[PM_SUSPEND_MAX] = {
  22        [PM_SUSPEND_STANDBY]    = "standby",
  23        [PM_SUSPEND_MEM]        = "mem",
  24};
  25
  26static struct platform_suspend_ops *suspend_ops;
  27
  28/**
  29 *      suspend_set_ops - Set the global suspend method table.
  30 *      @ops:   Pointer to ops structure.
  31 */
  32void suspend_set_ops(struct platform_suspend_ops *ops)
  33{
  34        mutex_lock(&pm_mutex);
  35        suspend_ops = ops;
  36        mutex_unlock(&pm_mutex);
  37}
  38
  39bool valid_state(suspend_state_t state)
  40{
  41        /*
  42         * All states need lowlevel support and need to be valid to the lowlevel
  43         * implementation, no valid callback implies that none are valid.
  44         */
  45        return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
  46}
  47
  48/**
  49 * suspend_valid_only_mem - generic memory-only valid callback
  50 *
  51 * Platform drivers that implement mem suspend only and only need
  52 * to check for that in their .valid callback can use this instead
  53 * of rolling their own .valid callback.
  54 */
  55int suspend_valid_only_mem(suspend_state_t state)
  56{
  57        return state == PM_SUSPEND_MEM;
  58}
  59
  60static int suspend_test(int level)
  61{
  62#ifdef CONFIG_PM_DEBUG
  63        if (pm_test_level == level) {
  64                printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n");
  65                mdelay(5000);
  66                return 1;
  67        }
  68#endif /* !CONFIG_PM_DEBUG */
  69        return 0;
  70}
  71
  72/**
  73 *      suspend_prepare - Do prep work before entering low-power state.
  74 *
  75 *      This is common code that is called for each state that we're entering.
  76 *      Run suspend notifiers, allocate a console and stop all processes.
  77 */
  78static int suspend_prepare(void)
  79{
  80        int error;
  81
  82        if (!suspend_ops || !suspend_ops->enter)
  83                return -EPERM;
  84
  85        pm_prepare_console();
  86
  87        error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
  88        if (error)
  89                goto Finish;
  90
  91        error = usermodehelper_disable();
  92        if (error)
  93                goto Finish;
  94
  95        error = suspend_freeze_processes();
  96        if (!error)
  97                return 0;
  98
  99        suspend_thaw_processes();
 100        usermodehelper_enable();
 101 Finish:
 102        pm_notifier_call_chain(PM_POST_SUSPEND);
 103        pm_restore_console();
 104        return error;
 105}
 106
 107/* default implementation */
 108void __attribute__ ((weak)) arch_suspend_disable_irqs(void)
 109{
 110        local_irq_disable();
 111}
 112
 113/* default implementation */
 114void __attribute__ ((weak)) arch_suspend_enable_irqs(void)
 115{
 116        local_irq_enable();
 117}
 118
 119/**
 120 *      suspend_enter - enter the desired system sleep state.
 121 *      @state:         state to enter
 122 *
 123 *      This function should be called after devices have been suspended.
 124 */
 125static int suspend_enter(suspend_state_t state)
 126{
 127        int error;
 128
 129        if (suspend_ops->prepare) {
 130                error = suspend_ops->prepare();
 131                if (error)
 132                        return error;
 133        }
 134
 135        error = dpm_suspend_noirq(PMSG_SUSPEND);
 136        if (error) {
 137                printk(KERN_ERR "PM: Some devices failed to power down\n");
 138                goto Platfrom_finish;
 139        }
 140
 141        if (suspend_ops->prepare_late) {
 142                error = suspend_ops->prepare_late();
 143                if (error)
 144                        goto Power_up_devices;
 145        }
 146
 147        if (suspend_test(TEST_PLATFORM))
 148                goto Platform_wake;
 149
 150        error = disable_nonboot_cpus();
 151        if (error || suspend_test(TEST_CPUS))
 152                goto Enable_cpus;
 153
 154        arch_suspend_disable_irqs();
 155        BUG_ON(!irqs_disabled());
 156
 157        error = sysdev_suspend(PMSG_SUSPEND);
 158        if (!error) {
 159                if (!suspend_test(TEST_CORE))
 160                        error = suspend_ops->enter(state);
 161                sysdev_resume();
 162        }
 163
 164        arch_suspend_enable_irqs();
 165        BUG_ON(irqs_disabled());
 166
 167 Enable_cpus:
 168        enable_nonboot_cpus();
 169
 170 Platform_wake:
 171        if (suspend_ops->wake)
 172                suspend_ops->wake();
 173
 174 Power_up_devices:
 175        dpm_resume_noirq(PMSG_RESUME);
 176
 177 Platfrom_finish:
 178        if (suspend_ops->finish)
 179                suspend_ops->finish();
 180
 181        return error;
 182}
 183
 184/**
 185 *      suspend_devices_and_enter - suspend devices and enter the desired system
 186 *                                  sleep state.
 187 *      @state:           state to enter
 188 */
 189int suspend_devices_and_enter(suspend_state_t state)
 190{
 191        int error;
 192
 193        if (!suspend_ops)
 194                return -ENOSYS;
 195
 196        if (suspend_ops->begin) {
 197                error = suspend_ops->begin(state);
 198                if (error)
 199                        goto Close;
 200        }
 201        suspend_console();
 202        suspend_test_start();
 203        error = dpm_suspend_start(PMSG_SUSPEND);
 204        if (error) {
 205                printk(KERN_ERR "PM: Some devices failed to suspend\n");
 206                goto Recover_platform;
 207        }
 208        suspend_test_finish("suspend devices");
 209        if (suspend_test(TEST_DEVICES))
 210                goto Recover_platform;
 211
 212        suspend_enter(state);
 213
 214 Resume_devices:
 215        suspend_test_start();
 216        dpm_resume_end(PMSG_RESUME);
 217        suspend_test_finish("resume devices");
 218        resume_console();
 219 Close:
 220        if (suspend_ops->end)
 221                suspend_ops->end();
 222        return error;
 223
 224 Recover_platform:
 225        if (suspend_ops->recover)
 226                suspend_ops->recover();
 227        goto Resume_devices;
 228}
 229
 230/**
 231 *      suspend_finish - Do final work before exiting suspend sequence.
 232 *
 233 *      Call platform code to clean up, restart processes, and free the
 234 *      console that we've allocated. This is not called for suspend-to-disk.
 235 */
 236static void suspend_finish(void)
 237{
 238        suspend_thaw_processes();
 239        usermodehelper_enable();
 240        pm_notifier_call_chain(PM_POST_SUSPEND);
 241        pm_restore_console();
 242}
 243
 244/**
 245 *      enter_state - Do common work of entering low-power state.
 246 *      @state:         pm_state structure for state we're entering.
 247 *
 248 *      Make sure we're the only ones trying to enter a sleep state. Fail
 249 *      if someone has beat us to it, since we don't want anything weird to
 250 *      happen when we wake up.
 251 *      Then, do the setup for suspend, enter the state, and cleaup (after
 252 *      we've woken up).
 253 */
 254int enter_state(suspend_state_t state)
 255{
 256        int error;
 257
 258        if (!valid_state(state))
 259                return -ENODEV;
 260
 261        if (!mutex_trylock(&pm_mutex))
 262                return -EBUSY;
 263
 264        printk(KERN_INFO "PM: Syncing filesystems ... ");
 265        sys_sync();
 266        printk("done.\n");
 267
 268        pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
 269        error = suspend_prepare();
 270        if (error)
 271                goto Unlock;
 272
 273        if (suspend_test(TEST_FREEZER))
 274                goto Finish;
 275
 276        pr_debug("PM: Entering %s sleep\n", pm_states[state]);
 277        error = suspend_devices_and_enter(state);
 278
 279 Finish:
 280        pr_debug("PM: Finishing wakeup.\n");
 281        suspend_finish();
 282 Unlock:
 283        mutex_unlock(&pm_mutex);
 284        return error;
 285}
 286
 287/**
 288 *      pm_suspend - Externally visible function for suspending system.
 289 *      @state:         Enumerated value of state to enter.
 290 *
 291 *      Determine whether or not value is within range, get state
 292 *      structure, and enter (above).
 293 */
 294int pm_suspend(suspend_state_t state)
 295{
 296        if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)
 297                return enter_state(state);
 298        return -EINVAL;
 299}
 300EXPORT_SYMBOL(pm_suspend);
 301