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)