Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[pandora-kernel.git] / drivers / idle / intel_idle.c
index c131d58..1fa091e 100644 (file)
@@ -59,6 +59,8 @@
 #include <linux/hrtimer.h>     /* ktime_get_real() */
 #include <trace/events/power.h>
 #include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/cpu.h>
 #include <asm/mwait.h>
 
 #define INTEL_IDLE_VERSION "0.4"
@@ -73,6 +75,7 @@ static int max_cstate = MWAIT_MAX_NUM_CSTATES - 1;
 
 static unsigned int mwait_substates;
 
+#define LAPIC_TIMER_ALWAYS_RELIABLE 0xFFFFFFFF
 /* Reliable LAPIC Timer States, bit 1 for C1 etc.  */
 static unsigned int lapic_timer_reliable_states = (1 << 1);     /* Default to only C1 */
 
@@ -81,6 +84,14 @@ static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state);
 
 static struct cpuidle_state *cpuidle_state_table;
 
+/*
+ * Set this flag for states where the HW flushes the TLB for us
+ * and so we don't need cross-calls to keep it consistent.
+ * If this flag is set, SW flushes the TLB, so even if the
+ * HW doesn't do the flushing, this flag is safe to use.
+ */
+#define CPUIDLE_FLAG_TLB_FLUSHED       0x10000
+
 /*
  * States are indexed by the cstate number,
  * which is also the index into the MWAIT hint array.
@@ -122,7 +133,7 @@ static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = {
                .driver_data = (void *) 0x00,
                .flags = CPUIDLE_FLAG_TIME_VALID,
                .exit_latency = 1,
-               .target_residency = 4,
+               .target_residency = 1,
                .enter = &intel_idle },
        { /* MWAIT C2 */
                .name = "SNB-C3",
@@ -130,7 +141,7 @@ static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = {
                .driver_data = (void *) 0x10,
                .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
                .exit_latency = 80,
-               .target_residency = 160,
+               .target_residency = 211,
                .enter = &intel_idle },
        { /* MWAIT C3 */
                .name = "SNB-C6",
@@ -138,7 +149,7 @@ static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = {
                .driver_data = (void *) 0x20,
                .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
                .exit_latency = 104,
-               .target_residency = 208,
+               .target_residency = 345,
                .enter = &intel_idle },
        { /* MWAIT C4 */
                .name = "SNB-C7",
@@ -146,7 +157,7 @@ static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = {
                .driver_data = (void *) 0x30,
                .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
                .exit_latency = 109,
-               .target_residency = 300,
+               .target_residency = 345,
                .enter = &intel_idle },
 };
 
@@ -220,9 +231,6 @@ static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state)
        kt_before = ktime_get_real();
 
        stop_critical_timings();
-#ifndef MODULE
-       trace_power_start(POWER_CSTATE, (eax >> 4) + 1, cpu);
-#endif
        if (!need_resched()) {
 
                __monitor((void *)&current_thread_info()->flags, 0, 0);
@@ -244,6 +252,35 @@ static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state)
        return usec_delta;
 }
 
+static void __setup_broadcast_timer(void *arg)
+{
+       unsigned long reason = (unsigned long)arg;
+       int cpu = smp_processor_id();
+
+       reason = reason ?
+               CLOCK_EVT_NOTIFY_BROADCAST_ON : CLOCK_EVT_NOTIFY_BROADCAST_OFF;
+
+       clockevents_notify(reason, &cpu);
+}
+
+static int setup_broadcast_cpuhp_notify(struct notifier_block *n,
+               unsigned long action, void *hcpu)
+{
+       int hotcpu = (unsigned long)hcpu;
+
+       switch (action & 0xf) {
+       case CPU_ONLINE:
+               smp_call_function_single(hotcpu, __setup_broadcast_timer,
+                       (void *)true, 1);
+               break;
+       }
+       return NOTIFY_OK;
+}
+
+static struct notifier_block setup_broadcast_notifier = {
+       .notifier_call = setup_broadcast_cpuhp_notify,
+};
+
 /*
  * intel_idle_probe()
  */
@@ -306,7 +343,11 @@ static int intel_idle_probe(void)
        }
 
        if (boot_cpu_has(X86_FEATURE_ARAT))     /* Always Reliable APIC Timer */
-               lapic_timer_reliable_states = 0xFFFFFFFF;
+               lapic_timer_reliable_states = LAPIC_TIMER_ALWAYS_RELIABLE;
+       else {
+               smp_call_function(__setup_broadcast_timer, (void *)true, 1);
+               register_cpu_notifier(&setup_broadcast_notifier);
+       }
 
        pr_debug(PREFIX "v" INTEL_IDLE_VERSION
                " model 0x%X\n", boot_cpu_data.x86_model);
@@ -404,6 +445,10 @@ static int __init intel_idle_init(void)
 {
        int retval;
 
+       /* Do not load intel_idle at all for now if idle= is passed */
+       if (boot_option_idle_override != IDLE_NO_OVERRIDE)
+               return -ENODEV;
+
        retval = intel_idle_probe();
        if (retval)
                return retval;
@@ -429,6 +474,11 @@ static void __exit intel_idle_exit(void)
        intel_idle_cpuidle_devices_uninit();
        cpuidle_unregister_driver(&intel_idle_driver);
 
+       if (lapic_timer_reliable_states != LAPIC_TIMER_ALWAYS_RELIABLE) {
+               smp_call_function(__setup_broadcast_timer, (void *)false, 1);
+               unregister_cpu_notifier(&setup_broadcast_notifier);
+       }
+
        return;
 }