arm64: use WFE for long delays
authorJulien Thierry <julien.thierry@arm.com>
Fri, 13 Oct 2017 13:32:56 +0000 (14:32 +0100)
committerWill Deacon <will.deacon@arm.com>
Fri, 13 Oct 2017 17:56:15 +0000 (18:56 +0100)
The current delay implementation uses the yield instruction, which is a
hint that it is beneficial to schedule another thread. As this is a hint,
it may be implemented as a NOP, causing all delays to be busy loops. This
is the case for many existing CPUs.

Taking advantage of the generic timer sending periodic events to all
cores, we can use WFE during delays to reduce power consumption. This is
beneficial only for delays longer than the period of the timer event
stream.

If timer event stream is not enabled, delays will behave as yield/busy
loops.

Signed-off-by: Julien Thierry <julien.thierry@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
arch/arm64/lib/delay.c
include/clocksource/arm_arch_timer.h

index dad4ec9bbfd1e8c7298ca43d3c0d2e25d177c725..e48ac402e7be131781fc2e5befd769118bffacec 100644 (file)
 #include <linux/module.h>
 #include <linux/timex.h>
 
+#include <clocksource/arm_arch_timer.h>
+
+#define USECS_TO_CYCLES(time_usecs)                    \
+       xloops_to_cycles((time_usecs) * 0x10C7UL)
+
+static inline unsigned long xloops_to_cycles(unsigned long xloops)
+{
+       return (xloops * loops_per_jiffy * HZ) >> 32;
+}
+
 void __delay(unsigned long cycles)
 {
        cycles_t start = get_cycles();
 
+       if (arch_timer_evtstrm_available()) {
+               const cycles_t timer_evt_period =
+                       USECS_TO_CYCLES(ARCH_TIMER_EVT_STREAM_PERIOD_US);
+
+               while ((get_cycles() - start + timer_evt_period) < cycles)
+                       wfe();
+       }
+
        while ((get_cycles() - start) < cycles)
                cpu_relax();
 }
@@ -35,10 +53,7 @@ EXPORT_SYMBOL(__delay);
 
 inline void __const_udelay(unsigned long xloops)
 {
-       unsigned long loops;
-
-       loops = xloops * loops_per_jiffy * HZ;
-       __delay(loops >> 32);
+       __delay(xloops_to_cycles(xloops));
 }
 EXPORT_SYMBOL(__const_udelay);
 
index 4e28283e2ec6a75bf715ebbd913f12ea1f01690b..349e5957c9498db170d85c434d9fc037d5d2acba 100644 (file)
@@ -67,7 +67,9 @@ enum arch_timer_spi_nr {
 #define ARCH_TIMER_USR_VT_ACCESS_EN    (1 << 8) /* virtual timer registers */
 #define ARCH_TIMER_USR_PT_ACCESS_EN    (1 << 9) /* physical timer registers */
 
-#define ARCH_TIMER_EVT_STREAM_FREQ     10000   /* 100us */
+#define ARCH_TIMER_EVT_STREAM_PERIOD_US        100
+#define ARCH_TIMER_EVT_STREAM_FREQ                             \
+       (USEC_PER_SEC / ARCH_TIMER_EVT_STREAM_PERIOD_US)
 
 struct arch_timer_kvm_info {
        struct timecounter timecounter;