Merge branches 'pm-sleep' and 'pm-runtime'
[firefly-linux-kernel-4.4.55.git] / drivers / cpuidle / cpuidle.c
index 61c417b9e53f8795175b29d71dfd201f15be151c..e8e2775c3821e26dc179ea6e300025d0cf6e75e5 100644 (file)
@@ -65,7 +65,7 @@ int cpuidle_play_dead(void)
                return -ENODEV;
 
        /* Find lowest-power state that supports long-term idle */
-       for (i = drv->state_count - 1; i >= CPUIDLE_DRIVER_STATE_START; i--)
+       for (i = drv->state_count - 1; i >= 0; i--)
                if (drv->states[i].enter_dead)
                        return drv->states[i].enter_dead(dev, i);
 
@@ -73,16 +73,21 @@ int cpuidle_play_dead(void)
 }
 
 static int find_deepest_state(struct cpuidle_driver *drv,
-                             struct cpuidle_device *dev, bool freeze)
+                             struct cpuidle_device *dev,
+                             unsigned int max_latency,
+                             unsigned int forbidden_flags,
+                             bool freeze)
 {
        unsigned int latency_req = 0;
-       int i, ret = freeze ? -1 : CPUIDLE_DRIVER_STATE_START - 1;
+       int i, ret = -ENXIO;
 
-       for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) {
+       for (i = 0; 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
+                   || s->exit_latency > max_latency
+                   || (s->flags & forbidden_flags)
                    || (freeze && !s->enter_freeze))
                        continue;
 
@@ -92,6 +97,7 @@ static int find_deepest_state(struct cpuidle_driver *drv,
        return ret;
 }
 
+#ifdef CONFIG_SUSPEND
 /**
  * cpuidle_find_deepest_state - Find the deepest available idle state.
  * @drv: cpuidle driver for the given CPU.
@@ -100,7 +106,7 @@ static int find_deepest_state(struct cpuidle_driver *drv,
 int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
                               struct cpuidle_device *dev)
 {
-       return find_deepest_state(drv, dev, false);
+       return find_deepest_state(drv, dev, UINT_MAX, 0, false);
 }
 
 static void enter_freeze_proper(struct cpuidle_driver *drv,
@@ -139,18 +145,19 @@ int cpuidle_enter_freeze(struct cpuidle_driver *drv, struct cpuidle_device *dev)
         * that interrupts won't be enabled when it exits and allows the tick to
         * be frozen safely.
         */
-       index = find_deepest_state(drv, dev, true);
+       index = find_deepest_state(drv, dev, UINT_MAX, 0, true);
        if (index >= 0)
                enter_freeze_proper(drv, dev, index);
 
        return index;
 }
+#endif /* CONFIG_SUSPEND */
 
 /**
  * cpuidle_enter_state - enter the state and update stats
  * @dev: cpuidle device for this cpu
  * @drv: cpuidle driver for this cpu
- * @next_state: index into drv->states of the state to enter
+ * @index: index into the states table in @drv of the state to enter
  */
 int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
                        int index)
@@ -167,8 +174,18 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
         * local timer will be shut down.  If a local timer is used from another
         * CPU as a broadcast timer, this call may fail if it is not available.
         */
-       if (broadcast && tick_broadcast_enter())
-               return -EBUSY;
+       if (broadcast && tick_broadcast_enter()) {
+               index = find_deepest_state(drv, dev, target_state->exit_latency,
+                                          CPUIDLE_FLAG_TIMER_STOP, false);
+               if (index < 0) {
+                       default_idle_call();
+                       return -EBUSY;
+               }
+               target_state = &drv->states[index];
+       }
+
+       /* Take note of the planned idle state. */
+       sched_idle_set_state(target_state);
 
        trace_cpu_idle_rcuidle(index, dev->cpu);
        time_start = ktime_get();
@@ -178,6 +195,9 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
        time_end = ktime_get();
        trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu);
 
+       /* The cpu is no longer idle or about to enter idle. */
+       sched_idle_set_state(NULL);
+
        if (broadcast) {
                if (WARN_ON_ONCE(!irqs_disabled()))
                        local_irq_disable();
@@ -249,7 +269,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)
+       if (cpuidle_curr_governor->reflect && index >= 0)
                cpuidle_curr_governor->reflect(dev, index);
 }