|
@@ -24,6 +24,7 @@
|
|
|
#include <asm/syscalls.h>
|
|
|
#include <asm/idle.h>
|
|
|
#include <asm/uaccess.h>
|
|
|
+#include <asm/mwait.h>
|
|
|
#include <asm/i387.h>
|
|
|
#include <asm/fpu-internal.h>
|
|
|
#include <asm/debugreg.h>
|
|
@@ -398,6 +399,49 @@ static void amd_e400_idle(void)
|
|
|
default_idle();
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Intel Core2 and older machines prefer MWAIT over HALT for C1.
|
|
|
+ * We can't rely on cpuidle installing MWAIT, because it will not load
|
|
|
+ * on systems that support only C1 -- so the boot default must be MWAIT.
|
|
|
+ *
|
|
|
+ * Some AMD machines are the opposite, they depend on using HALT.
|
|
|
+ *
|
|
|
+ * So for default C1, which is used during boot until cpuidle loads,
|
|
|
+ * use MWAIT-C1 on Intel HW that has it, else use HALT.
|
|
|
+ */
|
|
|
+static int prefer_mwait_c1_over_halt(const struct cpuinfo_x86 *c)
|
|
|
+{
|
|
|
+ if (c->x86_vendor != X86_VENDOR_INTEL)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (!cpu_has(c, X86_FEATURE_MWAIT))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * MONITOR/MWAIT with no hints, used for default default C1 state.
|
|
|
+ * This invokes MWAIT with interrutps enabled and no flags,
|
|
|
+ * which is backwards compatible with the original MWAIT implementation.
|
|
|
+ */
|
|
|
+
|
|
|
+static void mwait_idle(void)
|
|
|
+{
|
|
|
+ if (!need_resched()) {
|
|
|
+ if (this_cpu_has(X86_BUG_CLFLUSH_MONITOR))
|
|
|
+ clflush((void *)¤t_thread_info()->flags);
|
|
|
+
|
|
|
+ __monitor((void *)¤t_thread_info()->flags, 0, 0);
|
|
|
+ smp_mb();
|
|
|
+ if (!need_resched())
|
|
|
+ __sti_mwait(0, 0);
|
|
|
+ else
|
|
|
+ local_irq_enable();
|
|
|
+ } else
|
|
|
+ local_irq_enable();
|
|
|
+}
|
|
|
+
|
|
|
void select_idle_routine(const struct cpuinfo_x86 *c)
|
|
|
{
|
|
|
#ifdef CONFIG_SMP
|
|
@@ -411,6 +455,9 @@ void select_idle_routine(const struct cpuinfo_x86 *c)
|
|
|
/* E400: APIC timer interrupt does not wake up CPU from C1e */
|
|
|
pr_info("using AMD E400 aware idle routine\n");
|
|
|
x86_idle = amd_e400_idle;
|
|
|
+ } else if (prefer_mwait_c1_over_halt(c)) {
|
|
|
+ pr_info("using mwait in idle threads\n");
|
|
|
+ x86_idle = mwait_idle;
|
|
|
} else
|
|
|
x86_idle = default_idle;
|
|
|
}
|