From 6f3630021943d7d21ce8022e681472ed99392e8a Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Sun, 27 Oct 2019 16:12:58 +0000 Subject: [PATCH] drivers: timer: Add per-core ARM architected timer ARM cores may have a per-core architected timer, which provides per-cpu timers, attached to a GIC to deliver its per-processor interrupts via PPIs. This is the most common case supported by QEMU in the virt platform. This patch introduces support for this timer abstracting the way the timer registers are actually accessed. This is needed because different architectures (for example ARMv7-R vs ARMv8-A) use different registers and even the same architecture (ARMv8-A) can actually use different timers (ELx physical timers vs ELx virtual timers). So we introduce the common driver here but the actual SoC / architecture / board must provide the three helpers (arm_arch_timer_set_compare(), arm_arch_timer_toggle(), arm_arch_timer_count()) using an header file imported through the arch/cpu.h header file. Signed-off-by: Carlo Caione --- CODEOWNERS | 1 + drivers/timer/CMakeLists.txt | 1 + drivers/timer/Kconfig | 9 ++ drivers/timer/arm_arch_timer.c | 109 ++++++++++++++++++++++++ dts/bindings/timer/arm,armv8-timer.yaml | 13 +++ include/drivers/timer/arm_arch_timer.h | 21 +++++ tests/kernel/context/src/main.c | 2 + 7 files changed, 156 insertions(+) create mode 100644 drivers/timer/arm_arch_timer.c create mode 100644 dts/bindings/timer/arm,armv8-timer.yaml create mode 100644 include/drivers/timer/arm_arch_timer.h diff --git a/CODEOWNERS b/CODEOWNERS index 91b8d96e9e4..0119fd04cca 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -186,6 +186,7 @@ /drivers/spi/spi_ll_stm32.* @superna9999 /drivers/spi/spi_rv32m1_lpspi* @karstenkoenig /drivers/timer/apic_timer.c @andrewboie +/drivers/timer/arm_arch_timer.c @carlocaione /drivers/timer/cortex_m_systick.c @ioannisg /drivers/timer/altera_avalon_timer_hal.c @wentongwu /drivers/timer/riscv_machine_timer.c @nategraff-sifive @kgugala @pgielda diff --git a/drivers/timer/CMakeLists.txt b/drivers/timer/CMakeLists.txt index 9edfe667952..a139196ed35 100644 --- a/drivers/timer/CMakeLists.txt +++ b/drivers/timer/CMakeLists.txt @@ -3,6 +3,7 @@ zephyr_sources( sys_clock_init.c) zephyr_sources_ifdef(CONFIG_HPET_TIMER hpet.c) zephyr_sources_ifdef(CONFIG_ARCV2_TIMER arcv2_timer0.c) +zephyr_sources_ifdef(CONFIG_ARM_ARCH_TIMER arm_arch_timer.c) zephyr_sources_if_kconfig( loapic_timer.c) zephyr_sources_if_kconfig( apic_timer.c) zephyr_sources_ifdef(CONFIG_ALTERA_AVALON_TIMER altera_avalon_timer_hal.c) diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index 2fc0baff7ac..da7f374a97b 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -111,6 +111,15 @@ config ARCV2_TIMER_IRQ_PRIORITY This option specifies the IRQ priority used by the ARC timer. Lower values have higher priority. +config ARM_ARCH_TIMER + bool "ARM architected timer" + depends on GIC + select TICKLESS_CAPABLE + help + This module implements a kernel device driver for the ARM architected + timer which provides per-cpu timers attached to a GIC to deliver its + per-processor interrupts via PPIs. + 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 new file mode 100644 index 00000000000..ed9a7f056ab --- /dev/null +++ b/drivers/timer/arm_arch_timer.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2019 Carlo Caione + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#define CYC_PER_TICK ((u32_t)((u64_t)sys_clock_hw_cycles_per_sec() \ + / (u64_t)CONFIG_SYS_CLOCK_TICKS_PER_SEC)) + +#define MAX_TICKS ((0xffffffffu - CYC_PER_TICK) / CYC_PER_TICK) +#define MIN_DELAY (1000) + +static struct k_spinlock lock; +static volatile u64_t last_cycle; + +static void arm_arch_timer_compare_isr(void *arg) +{ + ARG_UNUSED(arg); + + k_spinlock_key_t key = k_spin_lock(&lock); + + u64_t curr_cycle = arm_arch_timer_count(); + u32_t delta_ticks = (u32_t)((curr_cycle - last_cycle) / CYC_PER_TICK); + + last_cycle += delta_ticks * CYC_PER_TICK; + + if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL) || + IS_ENABLED(CONFIG_QEMU_TICKLESS_WORKAROUND)) { + u64_t next_cycle = last_cycle + CYC_PER_TICK; + + if ((s64_t)(next_cycle - curr_cycle) < MIN_DELAY) { + next_cycle += CYC_PER_TICK; + } + arm_arch_timer_set_compare(next_cycle); + } + + k_spin_unlock(&lock, key); + + z_clock_announce(IS_ENABLED(CONFIG_TICKLESS_KERNEL) ? delta_ticks : 1); +} + +int z_clock_driver_init(struct device *device) +{ + ARG_UNUSED(device); + + IRQ_CONNECT(ARM_ARCH_TIMER_IRQ, 0, arm_arch_timer_compare_isr, 0, + ARM_TIMER_FLAGS); + arm_arch_timer_set_compare(arm_arch_timer_count() + CYC_PER_TICK); + arm_arch_timer_enable(true); + irq_enable(ARM_ARCH_TIMER_IRQ); + + return 0; +} + +void z_clock_set_timeout(s32_t ticks, bool idle) +{ + ARG_UNUSED(idle); + +#if defined(CONFIG_TICKLESS_KERNEL) && !defined(CONFIG_QEMU_TICKLESS_WORKAROUND) + + if (idle) { + return; + } + + ticks = (ticks == K_FOREVER) ? MAX_TICKS : ticks; + ticks = MAX(MIN(ticks - 1, (s32_t)MAX_TICKS), 0); + + k_spinlock_key_t key = k_spin_lock(&lock); + u64_t curr_cycle = arm_arch_timer_count(); + u32_t req_cycle = ticks * CYC_PER_TICK; + + /* Round up to next tick boundary */ + req_cycle += (u32_t)(curr_cycle - last_cycle) + (CYC_PER_TICK - 1); + req_cycle = (req_cycle / CYC_PER_TICK) * CYC_PER_TICK; + + if ((s32_t)(req_cycle + last_cycle - curr_cycle) < MIN_DELAY) { + req_cycle += CYC_PER_TICK; + } + + arm_arch_timer_set_compare(req_cycle + last_cycle); + k_spin_unlock(&lock, key); + +#endif +} + +u32_t z_clock_elapsed(void) +{ + if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { + return 0; + } + + k_spinlock_key_t key = k_spin_lock(&lock); + u32_t ret = ((u32_t)arm_arch_timer_count() - (u32_t)last_cycle) + / CYC_PER_TICK; + + k_spin_unlock(&lock, key); + return ret; +} + +u32_t z_timer_cycle_get_32(void) +{ + return (u32_t)arm_arch_timer_count(); +} diff --git a/dts/bindings/timer/arm,armv8-timer.yaml b/dts/bindings/timer/arm,armv8-timer.yaml new file mode 100644 index 00000000000..7b6e4c58e35 --- /dev/null +++ b/dts/bindings/timer/arm,armv8-timer.yaml @@ -0,0 +1,13 @@ +description: > + per-core ARM architected timer + +compatible: "arm,arm-timer" + +include: base.yaml + +properties: + interrupts: + required: true + + label: + required: true diff --git a/include/drivers/timer/arm_arch_timer.h b/include/drivers/timer/arm_arch_timer.h new file mode 100644 index 00000000000..c130477f222 --- /dev/null +++ b/include/drivers/timer/arm_arch_timer.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2019 Carlo Caione + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#ifndef ZEPHYR_INCLUDE_DRIVERS_TIMER_ARM_ARCH_TIMER_H_ +#define ZEPHYR_INCLUDE_DRIVERS_TIMER_ARM_ARCH_TIMER_H_ + +#include +#include + +#define ARM_TIMER_SECURE_IRQ DT_ARM_ARM_TIMER_TIMER_IRQ_0 +#define ARM_TIMER_NON_SECURE_IRQ DT_ARM_ARM_TIMER_TIMER_IRQ_1 +#define ARM_TIMER_VIRTUAL_IRQ DT_ARM_ARM_TIMER_TIMER_IRQ_2 +#define ARM_TIMER_HYP_IRQ DT_ARM_ARM_TIMER_TIMER_IRQ_3 + +#define ARM_TIMER_FLAGS IRQ_TYPE_EDGE + +#endif /* ZEPHYR_INCLUDE_DRIVERS_TIMER_ARM_ARCH_TIMER_H_ */ diff --git a/tests/kernel/context/src/main.c b/tests/kernel/context/src/main.c index 31f865122cf..eba12a3a004 100644 --- a/tests/kernel/context/src/main.c +++ b/tests/kernel/context/src/main.c @@ -49,6 +49,8 @@ */ #if defined(CONFIG_HPET_TIMER) #define TICK_IRQ DT_INST_0_INTEL_HPET_IRQ_0 +#elif defined(CONFIG_ARM_ARCH_TIMER) +#define TICK_IRQ ARM_ARCH_TIMER_IRQ #elif defined(CONFIG_APIC_TIMER) #define TICK_IRQ CONFIG_APIC_TIMER_IRQ #elif defined(CONFIG_LOAPIC_TIMER)