|
@@ -20,6 +20,7 @@
|
|
|
#include <asm/processor.h>
|
|
|
#include <asm/delay.h>
|
|
|
#include <asm/timer.h>
|
|
|
+#include <asm/mwait.h>
|
|
|
|
|
|
#ifdef CONFIG_SMP
|
|
|
# include <asm/smp.h>
|
|
@@ -83,6 +84,44 @@ static void delay_tsc(unsigned long __loops)
|
|
|
preempt_enable();
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * On some AMD platforms, MWAITX has a configurable 32-bit timer, that
|
|
|
+ * counts with TSC frequency. The input value is the loop of the
|
|
|
+ * counter, it will exit when the timer expires.
|
|
|
+ */
|
|
|
+static void delay_mwaitx(unsigned long __loops)
|
|
|
+{
|
|
|
+ u64 start, end, delay, loops = __loops;
|
|
|
+
|
|
|
+ start = rdtsc_ordered();
|
|
|
+
|
|
|
+ for (;;) {
|
|
|
+ delay = min_t(u64, MWAITX_MAX_LOOPS, loops);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Use cpu_tss as a cacheline-aligned, seldomly
|
|
|
+ * accessed per-cpu variable as the monitor target.
|
|
|
+ */
|
|
|
+ __monitorx(this_cpu_ptr(&cpu_tss), 0, 0);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * AMD, like Intel, supports the EAX hint and EAX=0xf
|
|
|
+ * means, do not enter any deep C-state and we use it
|
|
|
+ * here in delay() to minimize wakeup latency.
|
|
|
+ */
|
|
|
+ __mwaitx(MWAITX_DISABLE_CSTATES, delay, MWAITX_ECX_TIMER_ENABLE);
|
|
|
+
|
|
|
+ end = rdtsc_ordered();
|
|
|
+
|
|
|
+ if (loops <= end - start)
|
|
|
+ break;
|
|
|
+
|
|
|
+ loops -= end - start;
|
|
|
+
|
|
|
+ start = end;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Since we calibrate only once at boot, this
|
|
|
* function should be set once at boot and not changed
|
|
@@ -91,7 +130,13 @@ static void (*delay_fn)(unsigned long) = delay_loop;
|
|
|
|
|
|
void use_tsc_delay(void)
|
|
|
{
|
|
|
- delay_fn = delay_tsc;
|
|
|
+ if (delay_fn == delay_loop)
|
|
|
+ delay_fn = delay_tsc;
|
|
|
+}
|
|
|
+
|
|
|
+void use_mwaitx_delay(void)
|
|
|
+{
|
|
|
+ delay_fn = delay_mwaitx;
|
|
|
}
|
|
|
|
|
|
int read_current_timer(unsigned long *timer_val)
|