#include <linux/ktime.h>
 #include <linux/hrtimer.h>
 #include <linux/module.h>
+#include <linux/suspend.h>
 #include <trace/events/power.h>
 
 #include "cpuidle.h"
 static int enabled_devices;
 static int off __read_mostly;
 static int initialized __read_mostly;
-static bool use_deepest_state __read_mostly;
 
 int cpuidle_disabled(void)
 {
 }
 
 /**
- * cpuidle_use_deepest_state - Enable/disable the "deepest idle" mode.
- * @enable: Whether enable or disable the feature.
- *
- * If the "deepest idle" mode is enabled, cpuidle will ignore the governor and
- * always use the state with the greatest exit latency (out of the states that
- * are not disabled).
- *
- * This function can only be called after cpuidle_pause() to avoid races.
- */
-void cpuidle_use_deepest_state(bool enable)
-{
-       use_deepest_state = enable;
-}
-
-/**
- * cpuidle_find_deepest_state - Find the state of the greatest exit latency.
- * @drv: cpuidle driver for a given CPU.
- * @dev: cpuidle device for a given CPU.
+ * cpuidle_find_deepest_state - Find deepest state meeting specific conditions.
+ * @drv: cpuidle driver for the given CPU.
+ * @dev: cpuidle device for the given CPU.
  */
 static int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
                                      struct cpuidle_device *dev)
        return ret;
 }
 
+/**
+ * cpuidle_enter_freeze - Enter an idle state suitable for suspend-to-idle.
+ *
+ * Find the deepest state available and enter it.
+ */
+void cpuidle_enter_freeze(void)
+{
+       struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
+       struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
+       int index;
+
+       index = cpuidle_find_deepest_state(drv, dev);
+       if (index >= 0)
+               cpuidle_enter(drv, dev, index);
+       else
+               arch_cpu_idle();
+
+       /* Interrupts are enabled again here. */
+       local_irq_disable();
+}
+
 /**
  * cpuidle_enter_state - enter the state and update stats
  * @dev: cpuidle device for this cpu
        if (!drv || !dev || !dev->enabled)
                return -EBUSY;
 
-       if (unlikely(use_deepest_state))
-               return cpuidle_find_deepest_state(drv, dev);
-
        return cpuidle_curr_governor->select(drv, dev);
 }
 
  */
 void cpuidle_reflect(struct cpuidle_device *dev, int index)
 {
-       if (cpuidle_curr_governor->reflect && !unlikely(use_deepest_state))
+       if (cpuidle_curr_governor->reflect)
                cpuidle_curr_governor->reflect(dev, index);
 }
 
 
 extern int cpuidle_enable_device(struct cpuidle_device *dev);
 extern void cpuidle_disable_device(struct cpuidle_device *dev);
 extern int cpuidle_play_dead(void);
-extern void cpuidle_use_deepest_state(bool enable);
+extern void cpuidle_enter_freeze(void);
 
 extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev);
 #else
 {return -ENODEV; }
 static inline void cpuidle_disable_device(struct cpuidle_device *dev) { }
 static inline int cpuidle_play_dead(void) {return -ENODEV; }
-static inline void cpuidle_use_deepest_state(bool enable) {}
+static inline void cpuidle_enter_freeze(void) { }
 static inline struct cpuidle_driver *cpuidle_get_cpu_driver(
        struct cpuidle_device *dev) {return NULL; }
 #endif
 
  */
 extern void suspend_set_ops(const struct platform_suspend_ops *ops);
 extern int suspend_valid_only_mem(suspend_state_t state);
+
+/* Suspend-to-idle state machnine. */
+enum freeze_state {
+       FREEZE_STATE_NONE,      /* Not suspended/suspending. */
+       FREEZE_STATE_ENTER,     /* Enter suspend-to-idle. */
+       FREEZE_STATE_WAKE,      /* Wake up from suspend-to-idle. */
+};
+
+extern enum freeze_state __read_mostly suspend_freeze_state;
+
+static inline bool idle_should_freeze(void)
+{
+       return unlikely(suspend_freeze_state == FREEZE_STATE_ENTER);
+}
+
 extern void freeze_set_ops(const struct platform_freeze_ops *ops);
 extern void freeze_wake(void);
 
 
 static inline void suspend_set_ops(const struct platform_suspend_ops *ops) {}
 static inline int pm_suspend(suspend_state_t state) { return -ENOSYS; }
+static inline bool idle_should_freeze(void) { return false; }
 static inline void freeze_set_ops(const struct platform_freeze_ops *ops) {}
 static inline void freeze_wake(void) {}
 #endif /* !CONFIG_SUSPEND */
 
 static const struct platform_suspend_ops *suspend_ops;
 static const struct platform_freeze_ops *freeze_ops;
 static DECLARE_WAIT_QUEUE_HEAD(suspend_freeze_wait_head);
-static bool suspend_freeze_wake;
+
+enum freeze_state __read_mostly suspend_freeze_state;
+static DEFINE_SPINLOCK(suspend_freeze_lock);
 
 void freeze_set_ops(const struct platform_freeze_ops *ops)
 {
 
 static void freeze_begin(void)
 {
-       suspend_freeze_wake = false;
+       suspend_freeze_state = FREEZE_STATE_NONE;
 }
 
 static void freeze_enter(void)
 {
-       cpuidle_use_deepest_state(true);
+       spin_lock_irq(&suspend_freeze_lock);
+       if (pm_wakeup_pending())
+               goto out;
+
+       suspend_freeze_state = FREEZE_STATE_ENTER;
+       spin_unlock_irq(&suspend_freeze_lock);
+
+       get_online_cpus();
        cpuidle_resume();
-       wait_event(suspend_freeze_wait_head, suspend_freeze_wake);
+
+       /* Push all the CPUs into the idle loop. */
+       wake_up_all_idle_cpus();
+       pr_debug("PM: suspend-to-idle\n");
+       /* Make the current CPU wait so it can enter the idle loop too. */
+       wait_event(suspend_freeze_wait_head,
+                  suspend_freeze_state == FREEZE_STATE_WAKE);
+       pr_debug("PM: resume from suspend-to-idle\n");
+
        cpuidle_pause();
-       cpuidle_use_deepest_state(false);
+       put_online_cpus();
+
+       spin_lock_irq(&suspend_freeze_lock);
+
+ out:
+       suspend_freeze_state = FREEZE_STATE_NONE;
+       spin_unlock_irq(&suspend_freeze_lock);
 }
 
 void freeze_wake(void)
 {
-       suspend_freeze_wake = true;
-       wake_up(&suspend_freeze_wait_head);
+       unsigned long flags;
+
+       spin_lock_irqsave(&suspend_freeze_lock, flags);
+       if (suspend_freeze_state > FREEZE_STATE_NONE) {
+               suspend_freeze_state = FREEZE_STATE_WAKE;
+               wake_up(&suspend_freeze_wait_head);
+       }
+       spin_unlock_irqrestore(&suspend_freeze_lock, flags);
 }
 EXPORT_SYMBOL_GPL(freeze_wake);
 
 
 #include <linux/tick.h>
 #include <linux/mm.h>
 #include <linux/stackprotector.h>
+#include <linux/suspend.h>
 
 #include <asm/tlb.h>
 
         */
        rcu_idle_enter();
 
+       /*
+        * Suspend-to-idle ("freeze") is a system state in which all user space
+        * has been frozen, all I/O devices have been suspended and the only
+        * activity happens here and in iterrupts (if any).  In that case bypass
+        * the cpuidle governor and go stratight for the deepest idle state
+        * available.  Possibly also suspend the local tick and the entire
+        * timekeeping to prevent timer interrupts from kicking us out of idle
+        * until a proper wakeup interrupt happens.
+        */
+       if (idle_should_freeze()) {
+               cpuidle_enter_freeze();
+               local_irq_enable();
+               goto exit_idle;
+       }
+
        /*
         * Ask the cpuidle framework to choose a convenient idle state.
         * Fall back to the default arch idle method on errors.