zephyr/drivers/timer/riscv_machine_timer.c
Andrew Boie e08d07c97d kernel: add flexibility to k_cycle_get_32() definition
Some arches may want to define this as an inline function, or
define in core arch code instead of timer driver code.
Unfortunately, this means we need to remove from the footprint
tests, but this is not typically a large function.

Issue: ZEP-1546
Change-Id: Ic0d7a33507da855995838f4703d872cd613a2ca2
Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
2017-02-16 19:27:59 +00:00

99 lines
2.4 KiB
C

/*
* Copyright (c) 2017 Jean-Paul Etienne <fractalclone@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <kernel.h>
#include <arch/cpu.h>
#include <device.h>
#include <system_timer.h>
#include <board.h>
typedef struct {
uint32_t val_low;
uint32_t val_high;
} riscv_machine_timer_t;
static volatile riscv_machine_timer_t *mtime =
(riscv_machine_timer_t *)RISCV_MTIME_BASE;
static volatile riscv_machine_timer_t *mtimecmp =
(riscv_machine_timer_t *)RISCV_MTIMECMP_BASE;
/*
* The RISCV machine-mode timer is a one shot timer that needs to be rearm upon
* every interrupt. Timer clock is a 64-bits ART.
* To arm timer, we need to read the RTC value and update the
* timer compare register by the RTC value + time interval we want timer
* to interrupt.
*/
static ALWAYS_INLINE void riscv_machine_rearm_timer(void)
{
uint64_t rtc;
/*
* Following machine-mode timer implementation in QEMU, the actual
* RTC read is performed when reading low timer value register.
* Reading high timer value just reads the most significant 32-bits
* of a cache value, obtained from a previous read to the low
* timer value register. Hence, always read timer->val_low first.
* This also works for other implementations.
*/
rtc = mtime->val_low;
rtc |= ((uint64_t)mtime->val_high << 32);
/*
* Rearm timer to generate an interrupt after
* sys_clock_hw_cycles_per_tick
*/
rtc += sys_clock_hw_cycles_per_tick;
mtimecmp->val_low = (uint32_t)(rtc & 0xffffffff);
mtimecmp->val_high = (uint32_t)((rtc >> 32) & 0xffffffff);
}
static void riscv_machine_timer_irq_handler(void *unused)
{
ARG_UNUSED(unused);
_sys_clock_tick_announce();
/* Rearm timer */
riscv_machine_rearm_timer();
}
#ifdef CONFIG_TICKLESS_IDLE
#error "Tickless idle not yet implemented for riscv-machine timer"
#endif
int _sys_clock_driver_init(struct device *device)
{
ARG_UNUSED(device);
IRQ_CONNECT(RISCV_MACHINE_TIMER_IRQ, 0,
riscv_machine_timer_irq_handler, NULL, 0);
irq_enable(RISCV_MACHINE_TIMER_IRQ);
/* Initialize timer, just call riscv_machine_rearm_timer */
riscv_machine_rearm_timer();
return 0;
}
/**
*
* @brief Read the platform's timer hardware
*
* This routine returns the current time in terms of timer hardware clock
* cycles.
*
* @return up counter of elapsed clock cycles
*/
uint32_t _timer_cycle_get_32(void)
{
/* We just want a cycle count so just post what's in the low 32
* bits of the mtime real-time counter
*/
return mtime->val_low;
}