]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
armv8: generic_timer: Use event stream for udelay
authorPeter Hoyes <Peter.Hoyes@arm.com>
Wed, 1 May 2024 08:16:33 +0000 (09:16 +0100)
committerTom Rini <trini@konsulko.com>
Mon, 13 May 2024 22:51:14 +0000 (16:51 -0600)
Polling cntpct_el0 in a tight loop for delays is inefficient.
This is particularly apparent on Arm FVPs, which do not simulate
real time, meaning that a 1s sleep can take a couple of orders
of magnitude longer to execute in wall time.

If running at EL2 or above (where CNTHCTL_EL2 is available), enable
the cntpct_el0 event stream temporarily and use wfe to implement
the delay more efficiently. The event period is chosen as a
trade-off between efficiency and the fact that Arm FVPs do not
typically simulate real time.

This is only implemented for Armv8 boards, where an architectural
timer exists, and only enabled by default for the ARCH_VEXPRESS64
board family.

Signed-off-by: Peter Hoyes <Peter.Hoyes@arm.com>
Reviewed-by: Andre Przywara <andre.przywara@arm.com>
arch/arm/cpu/armv8/Kconfig
arch/arm/cpu/armv8/generic_timer.c
arch/arm/include/asm/system.h

index 9f0fb369f773ab8725efdedcdf9b67faac9fd1a8..199335cd6040d30a355a7fd5448a449fa7f550fb 100644 (file)
@@ -191,6 +191,14 @@ config ARMV8_EA_EL3_FIRST
          Exception handling at all exception levels for External Abort and
          SError interrupt exception are taken in EL3.
 
+config ARMV8_UDELAY_EVENT_STREAM
+       bool "Use the event stream for udelay"
+       default y if ARCH_VEXPRESS64
+       help
+         Use the event stream provided by the AArch64 architectural timer for
+         delays. This is more efficient than the default polling
+         implementation.
+
 menuconfig ARMV8_CRYPTO
        bool "ARM64 Accelerated Cryptographic Algorithms"
 
index e4aa5a47455307ea15759f88bde82bd8b79821f5..1de7ec596fc7cbbc3e78a241f163bc0a4fcad6b6 100644 (file)
@@ -114,3 +114,30 @@ ulong timer_get_boot_us(void)
 
        return val / get_tbclk();
 }
+
+#if CONFIG_IS_ENABLED(ARMV8_UDELAY_EVENT_STREAM)
+void __udelay(unsigned long usec)
+{
+       u64 target = get_ticks() + usec_to_tick(usec);
+
+       /* At EL2 or above, use the event stream to avoid polling CNTPCT_EL0 so often */
+       if (current_el() >= 2) {
+               u32 cnthctl_val;
+               const u8 event_period = 0x7;
+
+               asm volatile("mrs %0, cnthctl_el2" : "=r" (cnthctl_val));
+               asm volatile("msr cnthctl_el2, %0" : : "r"
+                       (cnthctl_val | CNTHCTL_EL2_EVNT_EN | CNTHCTL_EL2_EVNT_I(event_period)));
+
+               while (get_ticks() + (1ULL << event_period) <= target)
+                       wfe();
+
+               /* Reset the event stream */
+               asm volatile("msr cnthctl_el2, %0" : : "r" (cnthctl_val));
+       }
+
+       /* Fall back to polling CNTPCT_EL0 */
+       while (get_ticks() <= target)
+               ;
+}
+#endif
index 51123c2968430a77e2732cc2f0cc77255077915d..7e30cac32a098b94e8410d1163a058b8aa507e3f 100644 (file)
 /*
  * CNTHCTL_EL2 bits definitions
  */
-#define CNTHCTL_EL2_EL1PCEN_EN (1 << 1)  /* Physical timer regs accessible   */
-#define CNTHCTL_EL2_EL1PCTEN_EN        (1 << 0)  /* Physical counter accessible      */
+#define CNTHCTL_EL2_EVNT_EN    BIT(2)       /* Enable the event stream       */
+#define CNTHCTL_EL2_EVNT_I(val)        ((val) << 4) /* Event stream trigger bits     */
+#define CNTHCTL_EL2_EL1PCEN_EN (1 << 1)     /* Physical timer regs accessible */
+#define CNTHCTL_EL2_EL1PCTEN_EN        (1 << 0)     /* Physical counter accessible   */
 
 /*
  * HCR_EL2 bits definitions