Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux
[linux-flexiantxendom0-3.2.10.git] / drivers / cpuidle / cpuidle.c
index 6588f43..87411ce 100644 (file)
@@ -53,6 +53,52 @@ static void cpuidle_kick_cpus(void) {}
 
 static int __cpuidle_register_device(struct cpuidle_device *dev);
 
+static inline int cpuidle_enter(struct cpuidle_device *dev,
+                               struct cpuidle_driver *drv, int index)
+{
+       struct cpuidle_state *target_state = &drv->states[index];
+       return target_state->enter(dev, drv, index);
+}
+
+static inline int cpuidle_enter_tk(struct cpuidle_device *dev,
+                              struct cpuidle_driver *drv, int index)
+{
+       return cpuidle_wrap_enter(dev, drv, index, cpuidle_enter);
+}
+
+typedef int (*cpuidle_enter_t)(struct cpuidle_device *dev,
+                              struct cpuidle_driver *drv, int index);
+
+static cpuidle_enter_t cpuidle_enter_ops;
+
+/**
+ * cpuidle_play_dead - cpu off-lining
+ *
+ * Only returns in case of an error
+ */
+int cpuidle_play_dead(void)
+{
+       struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
+       struct cpuidle_driver *drv = cpuidle_get_driver();
+       int i, dead_state = -1;
+       int power_usage = -1;
+
+       /* Find lowest-power state that supports long-term idle */
+       for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) {
+               struct cpuidle_state *s = &drv->states[i];
+
+               if (s->power_usage < power_usage && s->enter_dead) {
+                       power_usage = s->power_usage;
+                       dead_state = i;
+               }
+       }
+
+       if (dead_state != -1)
+               return drv->states[dead_state].enter_dead(dev, dead_state);
+
+       return -ENODEV;
+}
+
 /**
  * cpuidle_idle_call - the main idle loop
  *
@@ -63,7 +109,6 @@ int cpuidle_idle_call(void)
 {
        struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
        struct cpuidle_driver *drv = cpuidle_get_driver();
-       struct cpuidle_state *target_state;
        int next_state, entered_state;
 
        if (off)
@@ -92,12 +137,10 @@ int cpuidle_idle_call(void)
                return 0;
        }
 
-       target_state = &drv->states[next_state];
-
        trace_power_start_rcuidle(POWER_CSTATE, next_state, dev->cpu);
        trace_cpu_idle_rcuidle(next_state, dev->cpu);
 
-       entered_state = target_state->enter(dev, drv, next_state);
+       entered_state = cpuidle_enter_ops(dev, drv, next_state);
 
        trace_power_end_rcuidle(dev->cpu);
        trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu);
@@ -110,6 +153,8 @@ int cpuidle_idle_call(void)
                dev->states_usage[entered_state].time +=
                                (unsigned long long)dev->last_residency;
                dev->states_usage[entered_state].usage++;
+       } else {
+               dev->last_residency = 0;
        }
 
        /* give the governor an opportunity to reflect on the outcome */
@@ -164,6 +209,37 @@ void cpuidle_resume_and_unlock(void)
 
 EXPORT_SYMBOL_GPL(cpuidle_resume_and_unlock);
 
+/**
+ * cpuidle_wrap_enter - performs timekeeping and irqen around enter function
+ * @dev: pointer to a valid cpuidle_device object
+ * @drv: pointer to a valid cpuidle_driver object
+ * @index: index of the target cpuidle state.
+ */
+int cpuidle_wrap_enter(struct cpuidle_device *dev,
+                               struct cpuidle_driver *drv, int index,
+                               int (*enter)(struct cpuidle_device *dev,
+                                       struct cpuidle_driver *drv, int index))
+{
+       ktime_t time_start, time_end;
+       s64 diff;
+
+       time_start = ktime_get();
+
+       index = enter(dev, drv, index);
+
+       time_end = ktime_get();
+
+       local_irq_enable();
+
+       diff = ktime_to_us(ktime_sub(time_end, time_start));
+       if (diff > INT_MAX)
+               diff = INT_MAX;
+
+       dev->last_residency = (int) diff;
+
+       return index;
+}
+
 #ifdef CONFIG_ARCH_HAS_CPU_RELAX
 static int poll_idle(struct cpuidle_device *dev,
                struct cpuidle_driver *drv, int index)
@@ -197,6 +273,7 @@ static void poll_idle_init(struct cpuidle_driver *drv)
        state->power_usage = -1;
        state->flags = 0;
        state->enter = poll_idle;
+       state->disable = 0;
 }
 #else
 static void poll_idle_init(struct cpuidle_driver *drv) {}
@@ -212,13 +289,14 @@ static void poll_idle_init(struct cpuidle_driver *drv) {}
 int cpuidle_enable_device(struct cpuidle_device *dev)
 {
        int ret, i;
+       struct cpuidle_driver *drv = cpuidle_get_driver();
 
        if (dev->enabled)
                return 0;
-       if (!cpuidle_get_driver() || !cpuidle_curr_governor)
+       if (!drv || !cpuidle_curr_governor)
                return -EIO;
        if (!dev->state_count)
-               return -EINVAL;
+               dev->state_count = drv->state_count;
 
        if (dev->registered == 0) {
                ret = __cpuidle_register_device(dev);
@@ -226,13 +304,16 @@ int cpuidle_enable_device(struct cpuidle_device *dev)
                        return ret;
        }
 
-       poll_idle_init(cpuidle_get_driver());
+       cpuidle_enter_ops = drv->en_core_tk_irqen ?
+               cpuidle_enter_tk : cpuidle_enter;
+
+       poll_idle_init(drv);
 
        if ((ret = cpuidle_add_state_sysfs(dev)))
                return ret;
 
        if (cpuidle_curr_governor->enable &&
-           (ret = cpuidle_curr_governor->enable(cpuidle_get_driver(), dev)))
+           (ret = cpuidle_curr_governor->enable(drv, dev)))
                goto fail_sysfs;
 
        for (i = 0; i < dev->state_count; i++) {