Merge tag 'platform-drivers-x86-v3.20-1' of git://git.infradead.org/users/dvhart...
[firefly-linux-kernel-4.4.55.git] / drivers / cpuidle / cpuidle.c
index 125150dc6e81edf5a120adffce852080eeccf76f..4d534582514e014b5fdb3fc5e0b9db7e52c7b306 100644 (file)
@@ -19,6 +19,8 @@
 #include <linux/ktime.h>
 #include <linux/hrtimer.h>
 #include <linux/module.h>
+#include <linux/suspend.h>
+#include <linux/tick.h>
 #include <trace/events/power.h>
 
 #include "cpuidle.h"
@@ -32,7 +34,6 @@ LIST_HEAD(cpuidle_detected_devices);
 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)
 {
@@ -66,36 +67,23 @@ int cpuidle_play_dead(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.
+ * @freeze: Whether or not the state should be suitable for suspend-to-idle.
  */
 static int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
-                                     struct cpuidle_device *dev)
+                                     struct cpuidle_device *dev, bool freeze)
 {
        unsigned int latency_req = 0;
-       int i, ret = CPUIDLE_DRIVER_STATE_START - 1;
+       int i, ret = freeze ? -1 : CPUIDLE_DRIVER_STATE_START - 1;
 
        for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) {
                struct cpuidle_state *s = &drv->states[i];
                struct cpuidle_state_usage *su = &dev->states_usage[i];
 
-               if (s->disabled || su->disable || s->exit_latency <= latency_req)
+               if (s->disabled || su->disable || s->exit_latency <= latency_req
+                   || (freeze && !s->enter_freeze))
                        continue;
 
                latency_req = s->exit_latency;
@@ -104,6 +92,63 @@ static int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
        return ret;
 }
 
+static void enter_freeze_proper(struct cpuidle_driver *drv,
+                               struct cpuidle_device *dev, int index)
+{
+       tick_freeze();
+       /*
+        * The state used here cannot be a "coupled" one, because the "coupled"
+        * cpuidle mechanism enables interrupts and doing that with timekeeping
+        * suspended is generally unsafe.
+        */
+       drv->states[index].enter_freeze(dev, drv, index);
+       WARN_ON(!irqs_disabled());
+       /*
+        * timekeeping_resume() that will be called by tick_unfreeze() for the
+        * last CPU executing it calls functions containing RCU read-side
+        * critical sections, so tell RCU about that.
+        */
+       RCU_NONIDLE(tick_unfreeze());
+}
+
+/**
+ * cpuidle_enter_freeze - Enter an idle state suitable for suspend-to-idle.
+ *
+ * If there are states with the ->enter_freeze callback, find the deepest of
+ * them and enter it with frozen tick.  Otherwise, find the deepest state
+ * available and enter it normally.
+ */
+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;
+
+       /*
+        * Find the deepest state with ->enter_freeze present, which guarantees
+        * that interrupts won't be enabled when it exits and allows the tick to
+        * be frozen safely.
+        */
+       index = cpuidle_find_deepest_state(drv, dev, true);
+       if (index >= 0) {
+               enter_freeze_proper(drv, dev, index);
+               return;
+       }
+
+       /*
+        * It is not safe to freeze the tick, find the deepest state available
+        * at all and try to enter it normally.
+        */
+       index = cpuidle_find_deepest_state(drv, dev, false);
+       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
@@ -166,9 +211,6 @@ int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
        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);
 }
 
@@ -200,7 +242,7 @@ int cpuidle_enter(struct cpuidle_driver *drv, struct cpuidle_device *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);
 }