From ab0b3336c450ef6ed06c94fa544a84c6cfcd133e Mon Sep 17 00:00:00 2001 From: hujun5 Date: Fri, 17 Mar 2023 15:49:43 +0800 Subject: [PATCH] arch/arm64:Suppot tickless mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1 Similar to Linux and zephyr, all implementations are in arm64_arch_timer.c 2 Arm64 tickless is turned off by default. If it needs to be turned on, you need to configure the switch CONFIG_SCHED_TICKLESS ON 3 The implementation strategy for tick/tickless is to use the timer inside the CPU and implement the timer driver based on the ARCH_TIMER framework. 4 We implemented tick_* Callback functions to adapt to the driven interface to avoid time format conversion overhead 5 In arm64_tick_cancel func,The remaining time that is not used, so this value can be ignored without reading the corresponding register to obtain the remaining cycles 6 Currently, tick/tickless can takes effect in SMP and non SMP mode, ostest can pass. Signed-off-by: hujun5 --- arch/Kconfig | 2 + arch/arm64/src/common/arm64_arch_timer.c | 392 +++++++++++++++++------ arch/arm64/src/common/arm64_arch_timer.h | 6 +- arch/arm64/src/common/arm64_cpustart.c | 2 + 4 files changed, 296 insertions(+), 106 deletions(-) diff --git a/arch/Kconfig b/arch/Kconfig index 46311b93e1..122c7fa394 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -24,6 +24,7 @@ config ARCH_ARM config ARCH_ARM64 bool "ARM64" + select ALARM_ARCH select ARCH_HAVE_BACKTRACE select ARCH_HAVE_INTERRUPTSTACK select ARCH_HAVE_VFORK @@ -33,6 +34,7 @@ config ARCH_ARM64 select ARCH_HAVE_SYSCALL_HOOKS select ARCH_HAVE_RDWR_MEM_CPU_RUN select ARCH_HAVE_THREAD_LOCAL + select ONESHOT ---help--- The ARM64 architectures diff --git a/arch/arm64/src/common/arm64_arch_timer.c b/arch/arm64/src/common/arm64_arch_timer.c index c2e860aa41..06489a3070 100644 --- a/arch/arm64/src/common/arm64_arch_timer.c +++ b/arch/arm64/src/common/arm64_arch_timer.c @@ -25,11 +25,13 @@ #include #include #include +#include #include #include #include #include +#include #include "arm64_arch.h" #include "arm64_internal.h" @@ -37,17 +39,25 @@ #include "arm64_arch_timer.h" /**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ -#define MIN_DELAY (1000) - -/**************************************************************************** - * Private Data + * Private Types ****************************************************************************/ -static uint64_t last_cycle; -static uint64_t cycle_per_tick; -static uint32_t arch_timer_rate; +struct arm64_oneshot_lowerhalf_s +{ + /* This is the part of the lower half driver that is visible to the upper- + * half client of the driver. This must be the first thing in this + * structure so that pointers to struct oneshot_lowerhalf_s are cast + * compatible to struct arm64_oneshot_lowerhalf_s and vice versa. + */ + + struct oneshot_lowerhalf_s lh; /* Common lower-half driver fields */ + + /* Private lower half data follows */ + + void *arg; /* Argument that is passed to the handler */ + uint64_t cycle_per_tick; /* cycle per tick */ + oneshot_callback_t callback; /* Internal handler that receives callback */ +}; /**************************************************************************** * Private Functions @@ -58,6 +68,11 @@ static inline void arm64_arch_timer_set_compare(uint64_t value) write_sysreg(value, cntv_cval_el0); } +static inline uint64_t arm64_arch_timer_get_compare(void) +{ + return read_sysreg(cntv_cval_el0); +} + static inline void arm64_arch_timer_enable(bool enable) { uint64_t value; @@ -99,76 +114,284 @@ static inline uint64_t arm64_arch_timer_count(void) return read_sysreg(cntvct_el0); } -static inline uint32_t arm64_arch_timer_get_cntfrq(void) +static inline uint64_t arm64_arch_timer_get_cntfrq(void) { return read_sysreg(cntfrq_el0); } -#ifdef CONFIG_SCHED_TICKLESS -static int arm64_arch_timer_compare_isr(int irq, void *regs, void *arg) -{ - irqstate_t flags; - uint64_t curr_cycle; - uint32_t delta_ticks; - - UNUSED(regs); - UNUSED(arg); - - flags = spin_lock_irqsave(&g_arch_timer_lock); - - curr_cycle = arm64_arch_timer_count(); - delta_ticks = (uint32_t)((curr_cycle - last_cycle) / cycle_per_tick); - - last_cycle += delta_ticks * cycle_per_tick; - - arm_arch_timer_set_irq_mask(true); - - spin_unlock_irqrestore(&g_arch_timer_lock, flags); - - nxsched_process_timer(); - return OK; -} - -#else +/**************************************************************************** + * Name: arm64_arch_timer_compare_isr + * + * Description: + * Common timer interrupt callback. When any oneshot timer interrupt + * expires, this function will be called. It will forward the call to + * the next level up. + * + * Input Parameters: + * oneshot - The state associated with the expired timer + * + * Returned Value: + * Always returns OK + * + ****************************************************************************/ static int arm64_arch_timer_compare_isr(int irq, void *regs, void *arg) { - uint64_t curr_cycle; - uint64_t delta_ticks; - uint64_t next_cycle; + struct arm64_oneshot_lowerhalf_s *priv = + (struct arm64_oneshot_lowerhalf_s *)arg; - UNUSED(irq); - UNUSED(regs); - UNUSED(arg); + arm64_arch_timer_set_irq_mask(true); - curr_cycle = arm64_arch_timer_count(); - delta_ticks = (curr_cycle - last_cycle) / cycle_per_tick; - - last_cycle += delta_ticks * cycle_per_tick; - - next_cycle = last_cycle + cycle_per_tick; - - if ((uint64_t)(next_cycle - curr_cycle) < MIN_DELAY) + if (priv->callback) { - next_cycle += cycle_per_tick; + /* Then perform the callback */ + + priv->callback(&priv->lh, priv->arg); } - arm64_arch_timer_set_compare(next_cycle); - arm64_arch_timer_set_irq_mask(false); - - nxsched_process_timer(); return OK; } -#endif +/**************************************************************************** + * Name: arm64_tick_max_delay + * + * Description: + * Determine the maximum delay of the one-shot timer (in microseconds) + * + * Input Parameters: + * lower An instance of the lower-half oneshot state structure. This + * structure must have been previously initialized via a call to + * oneshot_initialize(); + * ticks The location in which to return the maximum delay. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +static int arm64_tick_max_delay(struct oneshot_lowerhalf_s *lower, + clock_t *ticks) +{ + DEBUGASSERT(ticks != NULL); + + *ticks = UINT64_MAX; + + return OK; +} + +/**************************************************************************** + * Name: arm64_tick_cancel + * + * Description: + * Cancel the oneshot timer and return the time remaining on the timer. + * + * NOTE: This function may execute at a high rate with no timer running (as + * when pre-emption is enabled and disabled). + * + * Input Parameters: + * lower Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * oneshot_initialize(); + * ticks The location in which to return the time remaining on the + * oneshot timer. + * + * Returned Value: + * Zero (OK) is returned on success. A call to up_timer_cancel() when + * the timer is not active should also return success; a negated errno + * value is returned on any failure. + * + ****************************************************************************/ + +static int arm64_tick_cancel(struct oneshot_lowerhalf_s *lower, + clock_t *ticks) +{ + struct arm64_oneshot_lowerhalf_s *priv = + (struct arm64_oneshot_lowerhalf_s *)lower; + + DEBUGASSERT(priv != NULL && ticks != NULL); + + /* Disable int */ + + arm64_arch_timer_set_irq_mask(true); + + return OK; +} + +/**************************************************************************** + * Name: arm64_tick_start + * + * Description: + * Start the oneshot timer + * + * Input Parameters: + * lower An instance of the lower-half oneshot state structure. This + * structure must have been previously initialized via a call to + * oneshot_initialize(); + * handler The function to call when when the oneshot timer expires. + * arg An opaque argument that will accompany the callback. + * ticks Provides the duration of the one shot timer. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +static int arm64_tick_start(struct oneshot_lowerhalf_s *lower, + oneshot_callback_t callback, void *arg, + clock_t ticks) +{ + struct arm64_oneshot_lowerhalf_s *priv = + (struct arm64_oneshot_lowerhalf_s *)lower; + + DEBUGASSERT(priv != NULL && callback != NULL); + + /* Save the new handler and its argument */ + + priv->callback = callback; + priv->arg = arg; + + /* Set the timeout */ + + arm64_arch_timer_set_compare(arm64_arch_timer_count() + + priv->cycle_per_tick * ticks); + arm64_arch_timer_set_irq_mask(false); + + return OK; +} + +/**************************************************************************** + * Name: arm64_tick_current + * + * Description: + * Get the current time. + * + * Input Parameters: + * lower Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * oneshot_initialize(); + * ticks The location in which to return the current time. + * + * Returned Value: + * Zero (OK) is returned on success, a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +static int arm64_tick_current(struct oneshot_lowerhalf_s *lower, + clock_t *ticks) +{ + struct arm64_oneshot_lowerhalf_s *priv = + (struct arm64_oneshot_lowerhalf_s *)lower; + + DEBUGASSERT(ticks != NULL); + + *ticks = arm64_arch_timer_count() / priv->cycle_per_tick; + + return OK; +} + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct oneshot_operations_s g_oneshot_ops = +{ + .tick_start = arm64_tick_start, + .tick_current = arm64_tick_current, + .tick_max_delay = arm64_tick_max_delay, + .tick_cancel = arm64_tick_cancel, +}; + +/**************************************************************************** + * Name: oneshot_initialize + * + * Description: + * Initialize the oneshot timer and return a oneshot lower half driver + * instance. + * + * Returned Value: + * On success, a non-NULL instance of the oneshot lower-half driver is + * returned. NULL is return on any failure. + * + ****************************************************************************/ + +static struct oneshot_lowerhalf_s *arm64_oneshot_initialize(void) +{ + struct arm64_oneshot_lowerhalf_s *priv; + + tmrinfo("oneshot_initialize\n"); + + /* Allocate an instance of the lower half driver */ + + priv = (struct arm64_oneshot_lowerhalf_s *) + kmm_zalloc(sizeof(struct arm64_oneshot_lowerhalf_s)); + + if (priv == NULL) + { + tmrerr("ERROR: Failed to initialized state structure\n"); + + return NULL; + } + + /* Initialize the lower-half driver structure */ + + priv->lh.ops = &g_oneshot_ops; + priv->cycle_per_tick = arm64_arch_timer_get_cntfrq() / TICK_PER_SEC; + tmrinfo("cycle_per_tick %" PRIu64 "\n", priv->cycle_per_tick); + + /* Attach handler */ + + irq_attach(ARM_ARCH_TIMER_IRQ, + arm64_arch_timer_compare_isr, priv); + + /* Enable int */ + + up_enable_irq(ARM_ARCH_TIMER_IRQ); + + /* Start timer */ + + arm64_arch_timer_enable(true); + + tmrinfo("oneshot_initialize ok %p \n", &priv->lh); + + return &priv->lh; +} /**************************************************************************** * Public Functions ****************************************************************************/ -#ifdef CONFIG_SMP -/* Notes: +/**************************************************************************** + * Function: up_timer_initialize * + * Description: + * This function is called during start-up to initialize the system timer + * interrupt. + * + ****************************************************************************/ + +void up_timer_initialize(void) +{ + uint64_t freq; + + freq = arm64_arch_timer_get_cntfrq(); + tmrinfo("%s: cp15 timer(s) running at %" PRIu64 ".%" PRIu64 "MHz\n", + __func__, freq / 1000000, (freq / 10000) % 100); + + up_alarm_set_lowerhalf(arm64_oneshot_initialize()); +} + +#ifdef CONFIG_SMP +/**************************************************************************** + * Function: arm64_arch_timer_secondary_init + * + * Description: + * This function is called during start-up to initialize the system timer + * interrupt for smp. + * + * Notes: * The origin design for ARMv8-A timer is assigned private timer to * every PE(CPU core), the ARM_ARCH_TIMER_IRQ is a PPI so it's * should be enable at every core. @@ -179,53 +402,20 @@ static int arm64_arch_timer_compare_isr(int irq, void *regs, void *arg) * * IMX6 use GPT which is a SPI rather than generic timer to handle * timer interrupt - */ - -void arm64_smp_timer_init(void) -{ - uint64_t curr_cycle; - - /* set the initial status of timer0 of each secondary core */ - - curr_cycle = arm64_arch_timer_count(); - - arm64_arch_timer_set_compare(curr_cycle + cycle_per_tick); - arm64_arch_timer_enable(true); - up_enable_irq(ARM_ARCH_TIMER_IRQ); - arm64_arch_timer_set_irq_mask(false); -} - -#endif - -uint64_t arm64_counter_read(void) -{ - return arm64_arch_timer_count(); -} - -/**************************************************************************** - * Name: up_timer_initialize - * - * Description: - * ****************************************************************************/ -void up_timer_initialize(void) +void arm64_arch_timer_secondary_init() { - uint64_t curr_cycle; +#ifdef CONFIG_SCHED_TICKLESS + tmrinfo("arm64_arch_timer_secondary_init\n"); - arch_timer_rate = arm64_arch_timer_get_cntfrq(); - cycle_per_tick = ((uint64_t)arch_timer_rate / (uint64_t)TICK_PER_SEC); - - sinfo("%s: cp15 timer(s) running at %lu.%02luMHz, cycle %ld\n", __func__, - (unsigned long)arch_timer_rate / 1000000, - (unsigned long)(arch_timer_rate / 10000) % 100, cycle_per_tick); - - irq_attach(ARM_ARCH_TIMER_IRQ, arm64_arch_timer_compare_isr, 0); - - curr_cycle = arm64_arch_timer_count(); - arm64_arch_timer_set_compare(curr_cycle + cycle_per_tick); - arm64_arch_timer_enable(true); + /* Enable int */ up_enable_irq(ARM_ARCH_TIMER_IRQ); - arm64_arch_timer_set_irq_mask(false); + + /* Start timer */ + + arm64_arch_timer_enable(true); +#endif } +#endif \ No newline at end of file diff --git a/arch/arm64/src/common/arm64_arch_timer.h b/arch/arm64/src/common/arm64_arch_timer.h index 5fc8a7db99..4cdb2305a3 100644 --- a/arch/arm64/src/common/arm64_arch_timer.h +++ b/arch/arm64/src/common/arm64_arch_timer.h @@ -43,12 +43,8 @@ /**************************************************************************** * Public Function Prototypes ****************************************************************************/ - -extern uint64_t arm64_counter_read(void); -extern void arm64_start_timer(void); - #ifdef CONFIG_SMP -void arm64_smp_timer_init(void); +void arm64_arch_timer_secondary_init(void); #endif #endif /* __ARCH_ARM64_SRC_COMMON_ARM64_ARCH_TIMER_H */ diff --git a/arch/arm64/src/common/arm64_cpustart.c b/arch/arm64/src/common/arm64_cpustart.c index b1f023d338..0986f95786 100644 --- a/arch/arm64/src/common/arm64_cpustart.c +++ b/arch/arm64/src/common/arm64_cpustart.c @@ -268,6 +268,8 @@ void arm64_boot_secondary_c_routine(void) arm64_gic_secondary_init(); + arm64_arch_timer_secondary_init(); + up_perf_init(NULL); up_enable_irq(SGI_CPU_PAUSE);