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 <ccaione@baylibre.com>
This commit is contained in:
parent
9ec17d6556
commit
6f36300219
7 changed files with 156 additions and 0 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
109
drivers/timer/arm_arch_timer.c
Normal file
109
drivers/timer/arm_arch_timer.c
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Carlo Caione <ccaione@baylibre.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <drivers/timer/arm_arch_timer.h>
|
||||
#include <drivers/timer/system_timer.h>
|
||||
#include <sys_clock.h>
|
||||
#include <spinlock.h>
|
||||
#include <arch/cpu.h>
|
||||
|
||||
#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();
|
||||
}
|
13
dts/bindings/timer/arm,armv8-timer.yaml
Normal file
13
dts/bindings/timer/arm,armv8-timer.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
description: >
|
||||
per-core ARM architected timer
|
||||
|
||||
compatible: "arm,arm-timer"
|
||||
|
||||
include: base.yaml
|
||||
|
||||
properties:
|
||||
interrupts:
|
||||
required: true
|
||||
|
||||
label:
|
||||
required: true
|
21
include/drivers/timer/arm_arch_timer.h
Normal file
21
include/drivers/timer/arm_arch_timer.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Carlo Caione <ccaione@baylibre.com>
|
||||
*
|
||||
* 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 <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <zephyr/types.h>
|
||||
|
||||
#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_ */
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue