Merge tag 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik...
[platform/adaptation/renesas_rcar/renesas_kernel.git] / kernel / power / suspend.c
index c8b7446..d4feda0 100644 (file)
 #include "power.h"
 
 const char *const pm_states[PM_SUSPEND_MAX] = {
+       [PM_SUSPEND_FREEZE]     = "freeze",
        [PM_SUSPEND_STANDBY]    = "standby",
        [PM_SUSPEND_MEM]        = "mem",
 };
 
 static const struct platform_suspend_ops *suspend_ops;
 
+static bool need_suspend_ops(suspend_state_t state)
+{
+       return !!(state > PM_SUSPEND_FREEZE);
+}
+
+static DECLARE_WAIT_QUEUE_HEAD(suspend_freeze_wait_head);
+static bool suspend_freeze_wake;
+
+static void freeze_begin(void)
+{
+       suspend_freeze_wake = false;
+}
+
+static void freeze_enter(void)
+{
+       wait_event(suspend_freeze_wait_head, suspend_freeze_wake);
+}
+
+void freeze_wake(void)
+{
+       suspend_freeze_wake = true;
+       wake_up(&suspend_freeze_wait_head);
+}
+EXPORT_SYMBOL_GPL(freeze_wake);
+
 /**
  * suspend_set_ops - Set the global suspend method table.
  * @ops: Suspend operations to use.
@@ -50,8 +76,11 @@ EXPORT_SYMBOL_GPL(suspend_set_ops);
 
 bool valid_state(suspend_state_t state)
 {
+       if (state == PM_SUSPEND_FREEZE)
+               return true;
        /*
-        * All states need lowlevel support and need to be valid to the lowlevel
+        * PM_SUSPEND_STANDBY and PM_SUSPEND_MEMORY states need lowlevel
+        * support and need to be valid to the lowlevel
         * implementation, no valid callback implies that none are valid.
         */
        return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
@@ -89,11 +118,11 @@ static int suspend_test(int level)
  * hibernation).  Run suspend notifiers, allocate the "suspend" console and
  * freeze processes.
  */
-static int suspend_prepare(void)
+static int suspend_prepare(suspend_state_t state)
 {
        int error;
 
-       if (!suspend_ops || !suspend_ops->enter)
+       if (need_suspend_ops(state) && (!suspend_ops || !suspend_ops->enter))
                return -EPERM;
 
        pm_prepare_console();
@@ -137,7 +166,7 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
 {
        int error;
 
-       if (suspend_ops->prepare) {
+       if (need_suspend_ops(state) && suspend_ops->prepare) {
                error = suspend_ops->prepare();
                if (error)
                        goto Platform_finish;
@@ -149,12 +178,23 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
                goto Platform_finish;
        }
 
-       if (suspend_ops->prepare_late) {
+       if (need_suspend_ops(state) && suspend_ops->prepare_late) {
                error = suspend_ops->prepare_late();
                if (error)
                        goto Platform_wake;
        }
 
+       /*
+        * PM_SUSPEND_FREEZE equals
+        * frozen processes + suspended devices + idle processors.
+        * Thus we should invoke freeze_enter() soon after
+        * all the devices are suspended.
+        */
+       if (state == PM_SUSPEND_FREEZE) {
+               freeze_enter();
+               goto Platform_wake;
+       }
+
        if (suspend_test(TEST_PLATFORM))
                goto Platform_wake;
 
@@ -182,13 +222,13 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
        enable_nonboot_cpus();
 
  Platform_wake:
-       if (suspend_ops->wake)
+       if (need_suspend_ops(state) && suspend_ops->wake)
                suspend_ops->wake();
 
        dpm_resume_start(PMSG_RESUME);
 
  Platform_finish:
-       if (suspend_ops->finish)
+       if (need_suspend_ops(state) && suspend_ops->finish)
                suspend_ops->finish();
 
        return error;
@@ -203,11 +243,11 @@ int suspend_devices_and_enter(suspend_state_t state)
        int error;
        bool wakeup = false;
 
-       if (!suspend_ops)
+       if (need_suspend_ops(state) && !suspend_ops)
                return -ENOSYS;
 
        trace_machine_suspend(state);
-       if (suspend_ops->begin) {
+       if (need_suspend_ops(state) && suspend_ops->begin) {
                error = suspend_ops->begin(state);
                if (error)
                        goto Close;
@@ -226,7 +266,7 @@ int suspend_devices_and_enter(suspend_state_t state)
 
        do {
                error = suspend_enter(state, &wakeup);
-       } while (!error && !wakeup
+       } while (!error && !wakeup && need_suspend_ops(state)
                && suspend_ops->suspend_again && suspend_ops->suspend_again());
 
  Resume_devices:
@@ -236,13 +276,13 @@ int suspend_devices_and_enter(suspend_state_t state)
        ftrace_start();
        resume_console();
  Close:
-       if (suspend_ops->end)
+       if (need_suspend_ops(state) && suspend_ops->end)
                suspend_ops->end();
        trace_machine_suspend(PWR_EVENT_EXIT);
        return error;
 
  Recover_platform:
-       if (suspend_ops->recover)
+       if (need_suspend_ops(state) && suspend_ops->recover)
                suspend_ops->recover();
        goto Resume_devices;
 }
@@ -278,12 +318,15 @@ static int enter_state(suspend_state_t state)
        if (!mutex_trylock(&pm_mutex))
                return -EBUSY;
 
+       if (state == PM_SUSPEND_FREEZE)
+               freeze_begin();
+
        printk(KERN_INFO "PM: Syncing filesystems ... ");
        sys_sync();
        printk("done.\n");
 
        pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
-       error = suspend_prepare();
+       error = suspend_prepare(state);
        if (error)
                goto Unlock;