[PATCH] armv8: Initialize CNTFRQ if at highest exception level

From: Peter Hoyes Peter.Hoyes@arm.com
CNTFRQ_EL0 is only writable from the highest supported exception level on the platform. For Armv8-A, this is typically EL3, but technically EL2 and EL3 are optional so it may need to be initialized at EL2 or EL1. For Armv8-R, the highest exception level is always EL2.
This patch moves the initialization outside of the switch_el block and uses a new macro branch_if_not_highest_el which dynamically detects whether it is at the highest supported exception level.
Linux's docs state that CNTFRQ_EL0 should be initialized by the bootloader. If not set, the the U-Boot prompt countdown hangs.
Signed-off-by: Peter Hoyes Peter.Hoyes@arm.com --- arch/arm/cpu/armv8/start.S | 13 ++++++++----- arch/arm/include/asm/macro.h | 18 ++++++++++++++++++ arch/arm/include/asm/system.h | 6 ++++++ 3 files changed, 32 insertions(+), 5 deletions(-)
diff --git a/arch/arm/cpu/armv8/start.S b/arch/arm/cpu/armv8/start.S index 662449156b..8d7cfd7a87 100644 --- a/arch/arm/cpu/armv8/start.S +++ b/arch/arm/cpu/armv8/start.S @@ -127,10 +127,6 @@ pie_fixup_done: orr x0, x0, #0xf /* SCR_EL3.NS|IRQ|FIQ|EA */ msr scr_el3, x0 msr cptr_el3, xzr /* Enable FP/SIMD */ -#ifdef COUNTER_FREQUENCY - ldr x0, =COUNTER_FREQUENCY - msr cntfrq_el0, x0 /* Initialize CNTFRQ */ -#endif b 0f 2: set_vbar vbar_el2, x0 mov x0, #0x33ff @@ -140,7 +136,14 @@ pie_fixup_done: mov x0, #3 << 20 msr cpacr_el1, x0 /* Enable FP/SIMD */ 0: - isb + +#ifdef COUNTER_FREQUENCY + branch_if_not_highest_el x0, 4f + ldr x0, =COUNTER_FREQUENCY + msr cntfrq_el0, x0 /* Initialize CNTFRQ */ +#endif + +4: isb
/* * Enable SMPEN bit for coherency. diff --git a/arch/arm/include/asm/macro.h b/arch/arm/include/asm/macro.h index bb33b4bc89..485310d660 100644 --- a/arch/arm/include/asm/macro.h +++ b/arch/arm/include/asm/macro.h @@ -77,6 +77,24 @@ lr .req x30 b.eq \el1_label .endm
+/* + * Branch if we are not in the highest exception level + */ +.macro branch_if_not_highest_el, xreg, label + switch_el \xreg, 3f, 2f, 1f + +2: mrs \xreg, ID_AA64PFR0_EL1 + and \xreg, \xreg, #(ID_AA64PFR0_EL1_EL3) + cbnz \xreg, \label + b 3f + +1: mrs \xreg, ID_AA64PFR0_EL1 + and \xreg, \xreg, #(ID_AA64PFR0_EL1_EL3 | ID_AA64PFR0_EL1_EL2) + cbnz \xreg, \label + +3: +.endm + /* * Branch if current processor is a Cortex-A57 core. */ diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index 11fceec4d2..8b3a54e64c 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -79,6 +79,12 @@ #define HCR_EL2_RW_AARCH32 (0 << 31) /* Lower levels are AArch32 */ #define HCR_EL2_HCD_DIS (1 << 29) /* Hypervisor Call disabled */
+/* + * ID_AA64PFR0_EL1 bits definitions + */ +#define ID_AA64PFR0_EL1_EL3 (0xF << 12) /* EL3 implemented */ +#define ID_AA64PFR0_EL1_EL2 (0xF << 8) /* EL2 implemented */ + /* * CPACR_EL1 bits definitions */

On Mon, Jul 12, 2021 at 03:04:21PM +0100, Peter Hoyes wrote:
From: Peter Hoyes Peter.Hoyes@arm.com
CNTFRQ_EL0 is only writable from the highest supported exception level on the platform. For Armv8-A, this is typically EL3, but technically EL2 and EL3 are optional so it may need to be initialized at EL2 or EL1. For Armv8-R, the highest exception level is always EL2.
This patch moves the initialization outside of the switch_el block and uses a new macro branch_if_not_highest_el which dynamically detects whether it is at the highest supported exception level.
Linux's docs state that CNTFRQ_EL0 should be initialized by the bootloader. If not set, the the U-Boot prompt countdown hangs.
Signed-off-by: Peter Hoyes Peter.Hoyes@arm.com
Applied to u-boot/master, thanks!
participants (2)
-
Peter Hoyes
-
Tom Rini