mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
525751c33b
Previously, timer_clear() was a no-op, resulting in spurious IRQs from already canceled timeouts. This fixes the issue.
190 lines
3.9 KiB
C
190 lines
3.9 KiB
C
/*
|
|
* Copyright 2017 Ken Rabold
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @ingroup cpu_riscv_common
|
|
* @{
|
|
*
|
|
* @file timer.c
|
|
* @brief Low-level timer implementation based on the CLINT
|
|
*
|
|
* RISCV implementations using this peripheral must define the `CLINT_BASE_ADDR`
|
|
* in order to use the clint as timer.
|
|
*
|
|
* This implementation assumes the following registers at their offsets:
|
|
*
|
|
* - mtimecmp: 0x4000
|
|
* - mtime: 0xBFF8
|
|
*
|
|
* The MTIP flag in the mie csr is used to enable and disable the interrupt
|
|
*
|
|
* @author Ken Rabold
|
|
* @}
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include "cpu.h"
|
|
#include "cpu_conf.h"
|
|
#include "periph_cpu.h"
|
|
#include "periph_conf.h"
|
|
#include "periph/timer.h"
|
|
#include "vendor/riscv_csr.h"
|
|
|
|
#ifndef CLINT_BASE_ADDR
|
|
#error CLINT_BASE_ADDR must be defined to use the CLINT as timer
|
|
#endif
|
|
|
|
/**
|
|
* @brief Save reference to the timer callback
|
|
*/
|
|
static timer_cb_t isr_cb;
|
|
|
|
/**
|
|
* @brief Save argument for the callback
|
|
*/
|
|
static void *isr_arg;
|
|
|
|
int timer_init(tim_t dev, uint32_t freq, timer_cb_t cb, void *arg)
|
|
{
|
|
/* Using RISC-V built in timer (64bit value) */
|
|
if (dev != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (freq != RTC_FREQ) {
|
|
return -1;
|
|
}
|
|
|
|
/* Save timer callback and arg */
|
|
isr_cb = cb;
|
|
isr_arg = arg;
|
|
|
|
/* reset timer counter */
|
|
volatile uint64_t *mtime = (uint64_t *)(CLINT_BASE_ADDR + CLINT_MTIME);
|
|
|
|
*mtime = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int timer_set(tim_t dev, int channel, unsigned int timeout)
|
|
{
|
|
volatile uint64_t *mtime = (uint64_t *)(CLINT_BASE_ADDR + CLINT_MTIME);
|
|
volatile uint64_t *mtimecmp =
|
|
(uint64_t *)(CLINT_BASE_ADDR + CLINT_MTIMECMP);
|
|
|
|
/* Compute delta for timer */
|
|
uint64_t now = *mtime;
|
|
uint64_t then = now + (uint64_t)timeout;
|
|
|
|
if (dev != 0 || channel != 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* Avoid spurious timer intr */
|
|
clear_csr(mie, MIP_MTIP);
|
|
|
|
/* New intr time */
|
|
*mtimecmp = then;
|
|
|
|
/* Re-enalble timer intr */
|
|
set_csr(mie, MIP_MTIP);
|
|
return 0;
|
|
}
|
|
|
|
int timer_set_absolute(tim_t dev, int channel, unsigned int value)
|
|
{
|
|
|
|
volatile uint64_t *mtime = (uint64_t *)(CLINT_BASE_ADDR + CLINT_MTIME);
|
|
volatile uint64_t *mtimecmp =
|
|
(uint64_t *)(CLINT_BASE_ADDR + CLINT_MTIMECMP);
|
|
|
|
/* Compute absolute for timer */
|
|
uint64_t now = *mtime;
|
|
uint64_t then = (now & 0xFFFFFFFF00000000) + (uint64_t)value;
|
|
|
|
if (dev != 0 || channel != 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* Avoid spurious timer intr */
|
|
clear_csr(mie, MIP_MTIP);
|
|
|
|
/* New intr time (handle 32bit rollover) */
|
|
*mtimecmp = (then > now) ? then : then + 0x100000000;
|
|
|
|
/* Re-enable timer intr */
|
|
set_csr(mie, MIP_MTIP);
|
|
return 0;
|
|
}
|
|
|
|
int timer_clear(tim_t dev, int channel)
|
|
{
|
|
if (dev != 0 || channel != 0) {
|
|
return -1;
|
|
}
|
|
|
|
clear_csr(mie, MIP_MTIP);
|
|
return 0;
|
|
}
|
|
|
|
unsigned int timer_read(tim_t dev)
|
|
{
|
|
uint32_t lo = *(volatile uint32_t *)(CLINT_BASE_ADDR + CLINT_MTIME);
|
|
|
|
if (dev != 0) {
|
|
return 0;
|
|
}
|
|
|
|
/* Read current timer value */
|
|
return (unsigned int)lo;
|
|
}
|
|
|
|
void timer_start(tim_t dev)
|
|
{
|
|
if (dev != 0) {
|
|
return;
|
|
}
|
|
|
|
/* Timer is continuous running
|
|
* Enable the timer interrupt */
|
|
set_csr(mie, MIP_MTIP);
|
|
}
|
|
|
|
void timer_stop(tim_t dev)
|
|
{
|
|
if (dev != 0) {
|
|
return;
|
|
}
|
|
|
|
/* Disable the timer interrupt */
|
|
clear_csr(mie, MIP_MTIP);
|
|
}
|
|
|
|
void timer_isr(void)
|
|
{
|
|
volatile uint64_t *mtimecmp =
|
|
(uint64_t *)(CLINT_BASE_ADDR + CLINT_MTIMECMP);
|
|
|
|
/* Clear intr */
|
|
clear_csr(mie, MIP_MTIP);
|
|
|
|
/* Set mtimecmp to largest value to clear the interrupt */
|
|
*mtimecmp = 0xFFFFFFFFFFFFFFFF;
|
|
|
|
/* Call timer callback function */
|
|
if (isr_cb) {
|
|
isr_cb(isr_arg, 0);
|
|
}
|
|
|
|
/* Reset interrupt */
|
|
set_csr(mie, MIP_MTIP);
|
|
}
|