1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/mips_pic32_common/periph/timer.c
2021-08-13 19:50:38 +02:00

219 lines
5.0 KiB
C

/*
* Copyright 2016, Imagination Technologies Limited and/or its
* affiliated group companies.
* 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_mips32r2_common
* @ingroup drivers_periph_timer
* @{
*
* @file
* @brief Implementation of the low-level timer driver
*
* @}
*/
#include <assert.h>
#include <mips/cpu.h>
#include <mips/m32c0.h>
#include <mips/regdef.h>
#include <mips/asm.h>
#include <string.h>
#include <periph/timer.h>
#include "cpu_conf.h"
#include <stdio.h>
#include "sched.h"
#include "thread.h"
#include "board.h"
#include "irq.h"
#include "timex.h"
#include "div.h"
#include <sys/time.h>
#include "eic.h"
/*
* setting TIMER_ACCURACY_SHIFT lower will improve accuracy
* at the cost of more regular interrupts (hence less power efficient).
* */
#define TIMER_ACCURACY_SHIFT (10)
#define TIMER_ACCURACY (1 << TIMER_ACCURACY_SHIFT)
#define CHANNELS (3)
/* TICKS_PER_US must be set in the board file */
#ifndef TICKS_PER_US
#error "Please set TICK_PER_US in your board file"
#endif
/*
* The base MIPS count / compare timer is fixed frequency at core clock / 2
* and is pretty basic This timer is currently only supported in Vectored
* Interrupt Mode (VI), EIC mode is partially supported in non-vectored mode
* only.
*
* RIOT's xtimer expects the timer to operate at 1MHZ or any 2^n multiple or
* factor of this, thus we maintain a software timer which counts at 1MHz.
* This is not particularly power efficient and may add latency too.
*
* If other SoC specific timers are available which are more flexible then
* it is advised to use them, this timer is a fallback version
* that should work on all MIPS implementations.
*
*/
static timer_isr_ctx_t timer_isr_ctx;
volatile unsigned int counter;
volatile unsigned int compares[CHANNELS];
static void timer_isr(void)
{
IFS0CLR =_IFS0_CTIF_MASK;
uint32_t status = irq_disable();
counter += TIMER_ACCURACY;
irq_restore(status);
if (counter == compares[0]) {
/*
* The Xtimer code expects the ISR to take some time
* but our counter is a fake software one, so bump it a
* bit to give the impression some time elapsed in the ISR.
* Without this the callback ( _shoot(timer) on xtimer_core.c )
* never fires.
*/
counter += TIMER_ACCURACY;
timer_isr_ctx.cb(timer_isr_ctx.arg, 0);
mips32r2_isr_end();
}
if (counter == compares[1]) {
timer_isr_ctx.cb(timer_isr_ctx.arg, 1);
mips32r2_isr_end();
}
if (counter == compares[2]) {
timer_isr_ctx.cb(timer_isr_ctx.arg, 2);
mips32r2_isr_end();
}
mips_setcompare(mips_getcount() + TICKS_PER_US * TIMER_ACCURACY);
}
/*
* The mips toolchain C library does not implement gettimeofday()
*
* implement it here using the timer.
*
*/
int gettimeofday(struct timeval *__restrict __p, void *__restrict __tz)
{
(void)__tz;
uint64_t now = counter * US_PER_MS;
__p->tv_sec = div_u64_by_1000000(now);
__p->tv_usec = now - (__p->tv_sec * US_PER_SEC);
return 0;
}
int timer_init(tim_t dev, uint32_t freq, timer_cb_t cb, void *arg)
{
assert(dev == 0);
(void)dev;
(void)freq; /* Cannot adjust Frequency */
timer_isr_ctx.cb = cb;
timer_isr_ctx.arg = arg;
/* Clear down soft counters */
memset((void *)compares, 0, sizeof(compares));
counter = (1 << TIMER_ACCURACY_SHIFT);
/* Set compare up */
mips_setcompare(mips_getcount() + TICKS_PER_US * TIMER_ACCURACY);
/* Start the timer if stopped */
mips32_bc_c0(C0_CAUSE, CR_DC);
/* Enable Timer Interrupts */
set_external_isr_cb(_CORE_TIMER_VECTOR, timer_isr);
eic_configure_priority(_CORE_TIMER_VECTOR, 1, 0);
eic_enable(_CORE_TIMER_VECTOR);
return 0;
}
int timer_set(tim_t dev, int channel, unsigned int timeout)
{
assert(dev == 0);
assert(channel < CHANNELS);
(void)dev;
timeout >>= TIMER_ACCURACY_SHIFT;
timeout <<= TIMER_ACCURACY_SHIFT;
uint32_t status = irq_disable();
compares[channel] = counter + timeout;
irq_restore(status);
return 0;
}
int timer_set_absolute(tim_t dev, int channel, unsigned int value)
{
assert(dev == 0);
assert(channel < CHANNELS);
(void)dev;
value >>= TIMER_ACCURACY_SHIFT;
value <<= TIMER_ACCURACY_SHIFT;
uint32_t status = irq_disable();
compares[channel] = value;
irq_restore(status);
return 0;
}
int timer_clear(tim_t dev, int channel)
{
assert(dev == 0);
assert(channel < CHANNELS);
(void)dev;
uint32_t status = irq_disable();
compares[channel] = 0;
irq_restore(status);
return 0;
}
unsigned int timer_read(tim_t dev)
{
assert(dev == 0);
(void)dev;
return counter;
}
void timer_start(tim_t dev)
{
(void)dev;
mips32_bc_c0(C0_CAUSE, CR_DC);
}
void timer_stop(tim_t dev)
{
(void)dev;
mips32_bs_c0(C0_CAUSE, CR_DC);
}