diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index 9ab6ae84f09..864bea68323 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -120,6 +120,17 @@ DT_COMPAT_ARM_V7M_SYSTICK := arm,armv7m-systick DT_COMPAT_ARM_V8M_SYSTICK := arm,armv8m-systick DT_COMPAT_ARM_V8_1M_SYSTICK := arm,armv8.1m-systick +config ARM_ARCH_TIMER_ERRATUM_740657 + bool "ARM architected timer is affected by ARM erratum 740657" + depends on ARM_ARCH_TIMER + help + This option indicates that the ARM architected timer as implemented + in the target hardware is affected by the ARM erratum 740657 (comp. + ARM Cortex-A9 processors Software Developers Errata Notice, ARM + document ID032315) which leads to an additional, spurious interrupt + indication upon every actual timer interrupt. This option activates + the workaround for the erratum within the timer driver. + config CORTEX_M_SYSTICK bool "Cortex-M SYSTICK timer" depends on CPU_CORTEX_M_HAS_SYSTICK diff --git a/drivers/timer/arm_arch_timer.c b/drivers/timer/arm_arch_timer.c index 7bb7338ac35..b2ceae45025 100644 --- a/drivers/timer/arm_arch_timer.c +++ b/drivers/timer/arm_arch_timer.c @@ -24,6 +24,24 @@ static void arm_arch_timer_compare_isr(const void *arg) k_spinlock_key_t key = k_spin_lock(&lock); +#ifdef CONFIG_ARM_ARCH_TIMER_ERRATUM_740657 + /* + * Workaround required for Cortex-A9 MPCore erratum 740657 + * comp. ARM Cortex-A9 processors Software Developers Errata Notice, + * ARM document ID032315. + */ + + if (!arm_arch_timer_get_int_status()) { + /* + * If the event flag is not set, this is a spurious interrupt. + * DO NOT modify the compare register's value, DO NOT announce + * elapsed ticks! + */ + k_spin_unlock(&lock, key); + return; + } +#endif /* CONFIG_ARM_ARCH_TIMER_ERRATUM_740657 */ + uint64_t curr_cycle = arm_arch_timer_count(); uint32_t delta_ticks = (uint32_t)((curr_cycle - last_cycle) / CYC_PER_TICK); @@ -39,8 +57,29 @@ static void arm_arch_timer_compare_isr(const void *arg) arm_arch_timer_set_irq_mask(false); } else { arm_arch_timer_set_irq_mask(true); +#ifdef CONFIG_ARM_ARCH_TIMER_ERRATUM_740657 + /* + * In tickless mode, the compare register is normally not + * updated from within the ISR. Yet, to work around the timer's + * erratum, a new value *must* be written while the interrupt + * is being processed before the interrupt is acknowledged + * by the handling interrupt controller. + */ + arm_arch_timer_set_compare(~0ULL); } + /* + * Clear the event flag so that in case the erratum strikes (the timer's + * vector will still be indicated as pending by the GIC's pending register + * after this ISR has been executed) the error will be detected by the + * check performed upon entry of the ISR -> the event flag is not set, + * therefore, no actual hardware interrupt has occurred. + */ + arm_arch_timer_clear_int_status(); +#else + } +#endif /* CONFIG_ARM_ARCH_TIMER_ERRATUM_740657 */ + k_spin_unlock(&lock, key); sys_clock_announce(delta_ticks); diff --git a/include/arch/arm/aarch32/cortex_a_r/timer.h b/include/arch/arm/aarch32/cortex_a_r/timer.h index b0450033596..66c249ec013 100644 --- a/include/arch/arm/aarch32/cortex_a_r/timer.h +++ b/include/arch/arm/aarch32/cortex_a_r/timer.h @@ -35,6 +35,8 @@ extern "C" { #define TIMER_COMP_ENABLE BIT(1) #define TIMER_ENABLE BIT(0) +#define TIMER_ISR_EVENT_FLAG BIT(0) + DEVICE_MMIO_TOPLEVEL_STATIC(timer_regs, ARM_TIMER_NODE); #define TIMER_REG_GET(offs) (DEVICE_MMIO_TOPLEVEL_GET(timer_regs) + offs) @@ -63,6 +65,30 @@ static ALWAYS_INLINE void arm_arch_timer_set_compare(uint64_t val) sys_write32(ctrl, TIMER_REG_GET(TIMER_CTRL)); } +#if defined(CONFIG_ARM_ARCH_TIMER_ERRATUM_740657) + +/* + * R/W access to the event flag register is required for the timer errata + * 740657 workaround -> comp. ISR implementation in arm_arch_timer.c. + * This functionality is not present in the aarch64 implementation of the + * ARM global timer access functions. + * + * comp. ARM Cortex-A9 processors Software Developers Errata Notice, + * ARM document ID032315. + */ + +static ALWAYS_INLINE uint8_t arm_arch_timer_get_int_status(void) +{ + return (uint8_t)(sys_read32(TIMER_REG_GET(TIMER_ISR)) & TIMER_ISR_EVENT_FLAG); +} + +static ALWAYS_INLINE void arm_arch_timer_clear_int_status(void) +{ + sys_write32(TIMER_ISR_EVENT_FLAG, TIMER_REG_GET(TIMER_ISR)); +} + +#endif /* CONFIG_ARM_ARCH_TIMER_ERRATUM_740657 */ + static ALWAYS_INLINE void arm_arch_timer_enable(bool enable) { uint32_t ctrl;