intel_idle: disable Atom/Lincroft HW C-state auto-demotion
[pandora-kernel.git] / drivers / idle / intel_idle.c
index 41665d2..d3d6ee1 100644 (file)
 #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>
+#include <asm/msr.h>
 
 #define INTEL_IDLE_VERSION "0.4"
 #define PREFIX "intel_idle: "
@@ -73,6 +76,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 +85,12 @@ static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state);
 
 static struct cpuidle_state *cpuidle_state_table;
 
+/*
+ * Hardware C-state auto-demotion may not always be optimal.
+ * Indicate which enable bits to clear here.
+ */
+static unsigned long long auto_demotion_disable_flags;
+
 /*
  * States are indexed by the cstate number,
  * which is also the index into the MWAIT hint array.
@@ -244,6 +254,44 @@ 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,
+};
+
+static void auto_demotion_disable(void *dummy)
+{
+       unsigned long long msr_bits;
+
+       rdmsrl(MSR_NHM_SNB_PKG_CST_CFG_CTL, msr_bits);
+       msr_bits &= ~auto_demotion_disable_flags;
+       wrmsrl(MSR_NHM_SNB_PKG_CST_CFG_CTL, msr_bits);
+}
+
 /*
  * intel_idle_probe()
  */
@@ -273,8 +321,6 @@ static int intel_idle_probe(void)
 
        pr_debug(PREFIX "MWAIT substates: 0x%x\n", mwait_substates);
 
-       if (boot_cpu_has(X86_FEATURE_ARAT))     /* Always Reliable APIC Timer */
-               lapic_timer_reliable_states = 0xFFFFFFFF;
 
        if (boot_cpu_data.x86 != 6)     /* family 6 */
                return -ENODEV;
@@ -286,27 +332,26 @@ static int intel_idle_probe(void)
        case 0x1F:      /* Core i7 and i5 Processor - Nehalem */
        case 0x2E:      /* Nehalem-EX Xeon */
        case 0x2F:      /* Westmere-EX Xeon */
-               lapic_timer_reliable_states = (1 << 1);  /* C1 */
-
        case 0x25:      /* Westmere */
        case 0x2C:      /* Westmere */
                cpuidle_state_table = nehalem_cstates;
+               auto_demotion_disable_flags =
+                       (NHM_C1_AUTO_DEMOTE | NHM_C3_AUTO_DEMOTE);
                break;
 
        case 0x1C:      /* 28 - Atom Processor */
+               cpuidle_state_table = atom_cstates;
+               break;
+
        case 0x26:      /* 38 - Lincroft Atom Processor */
-               lapic_timer_reliable_states = (1 << 1); /* C1 */
                cpuidle_state_table = atom_cstates;
+               auto_demotion_disable_flags = ATM_LNC_C6_AUTO_DEMOTE;
                break;
 
        case 0x2A:      /* SNB */
        case 0x2D:      /* SNB Xeon */
                cpuidle_state_table = snb_cstates;
                break;
-#ifdef FUTURE_USE
-       case 0x17:      /* 23 - Core 2 Duo */
-               lapic_timer_reliable_states = (1 << 2) | (1 << 1); /* C2, C1 */
-#endif
 
        default:
                pr_debug(PREFIX "does not run on family %d model %d\n",
@@ -314,6 +359,13 @@ static int intel_idle_probe(void)
                return -ENODEV;
        }
 
+       if (boot_cpu_has(X86_FEATURE_ARAT))     /* Always Reliable APIC Timer */
+               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);
 
@@ -401,6 +453,8 @@ static int intel_idle_cpuidle_devices_init(void)
                        return -EIO;
                }
        }
+       if (auto_demotion_disable_flags)
+               smp_call_function(auto_demotion_disable, NULL, 1);
 
        return 0;
 }
@@ -435,6 +489,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;
 }