/* * Copyright (c) 2017 Jean-Paul Etienne * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include typedef struct { u32_t val_low; u32_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) { u64_t rtc; /* * Disable timer interrupt while rearming the timer * to avoid generation of interrupts while setting * the mtimecmp->val_low register. */ irq_disable(RISCV_MACHINE_TIMER_IRQ); /* * 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 |= ((u64_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 = (u32_t)(rtc & 0xffffffff); mtimecmp->val_high = (u32_t)((rtc >> 32) & 0xffffffff); /* Enable timer interrupt */ irq_enable(RISCV_MACHINE_TIMER_IRQ); } 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); /* 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 */ u32_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; }