Merge branch 'linux-linaro-lsk' into linux-linaro-lsk-android
[firefly-linux-kernel-4.4.55.git] / kernel / power / suspend.c
index 454568e6c8d280a59a9f2e3e0ed50c6af6f27f90..82450c20875c3736f2579f9b86f302adeb542190 100644 (file)
 #include <linux/ftrace.h>
 #include <linux/rtc.h>
 #include <trace/events/power.h>
+#include <linux/wakeup_reason.h>
 
 #include "power.h"
 
-const char *const pm_states[PM_SUSPEND_MAX] = {
-       [PM_SUSPEND_FREEZE]     = "freeze",
-       [PM_SUSPEND_STANDBY]    = "standby",
-       [PM_SUSPEND_MEM]        = "mem",
+struct pm_sleep_state pm_states[PM_SUSPEND_MAX] = {
+       [PM_SUSPEND_FREEZE] = { .label = "freeze", .state = PM_SUSPEND_FREEZE },
+       [PM_SUSPEND_STANDBY] = { .label = "standby", },
+       [PM_SUSPEND_MEM] = { .label = "mem", },
 };
 
 static const struct platform_suspend_ops *suspend_ops;
@@ -63,42 +64,34 @@ void freeze_wake(void)
 }
 EXPORT_SYMBOL_GPL(freeze_wake);
 
+static bool valid_state(suspend_state_t state)
+{
+       /*
+        * PM_SUSPEND_STANDBY and PM_SUSPEND_MEM states need low level
+        * support and need to be valid to the low level
+        * implementation, no valid callback implies that none are valid.
+        */
+       return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
+}
+
 /**
  * suspend_set_ops - Set the global suspend method table.
  * @ops: Suspend operations to use.
  */
 void suspend_set_ops(const struct platform_suspend_ops *ops)
 {
+       suspend_state_t i;
+
        lock_system_sleep();
+
        suspend_ops = ops;
+       for (i = PM_SUSPEND_STANDBY; i <= PM_SUSPEND_MEM; i++)
+               pm_states[i].state = valid_state(i) ? i : 0;
+
        unlock_system_sleep();
 }
 EXPORT_SYMBOL_GPL(suspend_set_ops);
 
-bool valid_state(suspend_state_t state)
-{
-       if (state == PM_SUSPEND_FREEZE) {
-#ifdef CONFIG_PM_DEBUG
-               if (pm_test_level != TEST_NONE &&
-                   pm_test_level != TEST_FREEZER &&
-                   pm_test_level != TEST_DEVICES &&
-                   pm_test_level != TEST_PLATFORM) {
-                       printk(KERN_WARNING "Unsupported pm_test mode for "
-                                       "freeze state, please choose "
-                                       "none/freezer/devices/platform.\n");
-                       return false;
-               }
-#endif
-                       return true;
-       }
-       /*
-        * 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);
-}
-
 /**
  * suspend_valid_only_mem - Generic memory-only valid callback.
  *
@@ -147,7 +140,7 @@ static int suspend_prepare(suspend_state_t state)
        error = suspend_freeze_processes();
        if (!error)
                return 0;
-
+       log_suspend_abort_reason("One or more tasks refusing to freeze");
        suspend_stats.failed_freeze++;
        dpm_save_failed_step(SUSPEND_FREEZE);
  Finish:
@@ -177,7 +170,8 @@ void __attribute__ ((weak)) arch_suspend_enable_irqs(void)
  */
 static int suspend_enter(suspend_state_t state, bool *wakeup)
 {
-       int error;
+       char suspend_abort[MAX_SUSPEND_ABORT_LEN];
+       int error, last_dev;
 
        if (need_suspend_ops(state) && suspend_ops->prepare) {
                error = suspend_ops->prepare();
@@ -187,7 +181,11 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
 
        error = dpm_suspend_end(PMSG_SUSPEND);
        if (error) {
+               last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
+               last_dev %= REC_FAILED_NUM;
                printk(KERN_ERR "PM: Some devices failed to power down\n");
+               log_suspend_abort_reason("%s device failed to power down",
+                       suspend_stats.failed_devs[last_dev]);
                goto Platform_finish;
        }
 
@@ -212,8 +210,10 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
        }
 
        error = disable_nonboot_cpus();
-       if (error || suspend_test(TEST_CPUS))
+       if (error || suspend_test(TEST_CPUS)) {
+               log_suspend_abort_reason("Disabling non-boot cpus failed");
                goto Enable_cpus;
+       }
 
        arch_suspend_disable_irqs();
        BUG_ON(!irqs_disabled());
@@ -224,6 +224,10 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
                if (!(suspend_test(TEST_CORE) || *wakeup)) {
                        error = suspend_ops->enter(state);
                        events_check_enabled = false;
+               } else {
+                       pm_get_active_wakeup_sources(suspend_abort,
+                               MAX_SUSPEND_ABORT_LEN);
+                       log_suspend_abort_reason(suspend_abort);
                }
                syscore_resume();
        }
@@ -271,6 +275,7 @@ int suspend_devices_and_enter(suspend_state_t state)
        error = dpm_suspend_start(PMSG_SUSPEND);
        if (error) {
                printk(KERN_ERR "PM: Some devices failed to suspend\n");
+               log_suspend_abort_reason("Some devices failed to suspend");
                goto Recover_platform;
        }
        suspend_test_finish("suspend devices");
@@ -325,9 +330,17 @@ static int enter_state(suspend_state_t state)
 {
        int error;
 
-       if (!valid_state(state))
-               return -ENODEV;
-
+       if (state == PM_SUSPEND_FREEZE) {
+#ifdef CONFIG_PM_DEBUG
+               if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
+                       pr_warning("PM: Unsupported test mode for freeze state,"
+                                  "please choose none/freezer/devices/platform.\n");
+                       return -EAGAIN;
+               }
+#endif
+       } else if (!valid_state(state)) {
+               return -EINVAL;
+       }
        if (!mutex_trylock(&pm_mutex))
                return -EBUSY;
 
@@ -338,7 +351,7 @@ static int enter_state(suspend_state_t state)
        sys_sync();
        printk("done.\n");
 
-       pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
+       pr_debug("PM: Preparing system for %s sleep\n", pm_states[state].label);
        error = suspend_prepare(state);
        if (error)
                goto Unlock;
@@ -346,7 +359,7 @@ static int enter_state(suspend_state_t state)
        if (suspend_test(TEST_FREEZER))
                goto Finish;
 
-       pr_debug("PM: Entering %s sleep\n", pm_states[state]);
+       pr_debug("PM: Entering %s sleep\n", pm_states[state].label);
        pm_restrict_gfp_mask();
        error = suspend_devices_and_enter(state);
        pm_restore_gfp_mask();