1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/sam0_common/periph/timer.c
Benjamin Valentin 1496149bba cpu/sam0: don't hard-code peripheral clocks
Instead of hard-coding the peripheral clocks to CLOCK_CORECLOCK
introduce helper functions to return the frequency of the individual
GCLKs and use those for baud-rate calculations.

This requires the GCLK to be part of the peripheral's config struct.
While this is already the case for most peripherals, this also adds
it for those where it wasn't used before.

As it defaults to 0 (CLOCK_CORECLOCK) no change is to be expected.
2020-02-04 21:06:21 +01:00

275 lines
5.8 KiB
C

/*
* Copyright (C) 2019 ML!PA Consulting GmbH
*
*
* 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_sam0_common
* @ingroup drivers_periph_timer
* @{
*
* @file timer.c
* @brief Low-level timer driver implementation
*
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
*
* @}
*/
#include <stdlib.h>
#include <stdio.h>
#include "board.h"
#include "cpu.h"
#include "periph/timer.h"
#include "periph_conf.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
/**
* @brief Timer state memory
*/
static timer_isr_ctx_t config[TIMER_NUMOF];
static inline TcCount32 *dev(tim_t tim)
{
return &timer_config[tim].dev->COUNT32;
}
static inline TcCount16 *dev16(tim_t tim)
{
return &timer_config[tim].dev->COUNT16;
}
static inline TcCount8 *dev8(tim_t tim)
{
return &timer_config[tim].dev->COUNT8;
}
static inline void wait_synchronization(tim_t tim)
{
#if defined(TC_SYNCBUSY_MASK)
/* SYNCBUSY is a register */
while ((dev(tim)->SYNCBUSY.reg) != 0) {}
#elif defined(TC_STATUS_SYNCBUSY)
/* SYNCBUSY is a bit */
while ((dev(tim)->STATUS.reg & TC_STATUS_SYNCBUSY) != 0) {}
#else
#error Unsupported device
#endif
}
/* enable timer interrupts */
static inline void _irq_enable(tim_t tim)
{
NVIC_EnableIRQ(timer_config[tim].irq);
}
/**
* @brief Setup the given timer
*/
int timer_init(tim_t tim, unsigned long freq, timer_cb_t cb, void *arg)
{
(void) freq;
const tc32_conf_t *cfg = &timer_config[tim];
/* make sure given device is valid */
if (tim >= TIMER_NUMOF) {
return -1;
}
/* make sure the timer is not running */
timer_stop(tim);
sam0_gclk_enable(cfg->gclk_src);
#ifdef MCLK
GCLK->PCHCTRL[cfg->gclk_id].reg = GCLK_PCHCTRL_GEN(cfg->gclk_src) | GCLK_PCHCTRL_CHEN;
*cfg->mclk |= cfg->mclk_mask;
#else
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(cfg->gclk_src) | cfg->gclk_ctrl;
PM->APBCMASK.reg |= cfg->pm_mask;
#endif
/* reset the timer */
dev(tim)->CTRLA.bit.SWRST = 1;
while (dev(tim)->CTRLA.bit.SWRST) {}
dev(tim)->CTRLA.reg = cfg->flags
#ifdef TC_CTRLA_WAVEGEN_NFRQ
| TC_CTRLA_WAVEGEN_NFRQ
#endif
| cfg->prescaler
| TC_CTRLA_PRESCSYNC_RESYNC;
#ifdef TC_WAVE_WAVEGEN_NFRQ
dev(tim)->WAVE.reg = TC_WAVE_WAVEGEN_NFRQ;
#endif
wait_synchronization(tim);
dev(tim)->INTENCLR.reg = TC_INTENCLR_MASK;
/* save callback */
config[tim].cb = cb;
config[tim].arg = arg;
timer_start(tim);
/* enable interrupts for given timer */
_irq_enable(tim);
return 0;
}
static void _set_cc(tim_t tim, int cc, unsigned int value)
{
const uint16_t flags = timer_config[tim].flags;
if (flags & TC_CTRLA_MODE_COUNT32) {
dev(tim)->CC[cc].reg = value;
return;
}
if (flags & TC_CTRLA_MODE_COUNT8) {
dev8(tim)->CC[cc].reg = value;
return;
}
/* 16 bit is the default */
dev16(tim)->CC[cc].reg = value;
}
int timer_set_absolute(tim_t tim, int channel, unsigned int value)
{
DEBUG("Setting timer %i channel %i to %i\n", tim, channel, value);
/* set timeout value */
switch (channel) {
case 0:
dev(tim)->INTFLAG.reg = TC_INTFLAG_MC0;
_set_cc(tim, 0, value);
dev(tim)->INTENSET.reg = TC_INTENSET_MC0;
break;
case 1:
dev(tim)->INTFLAG.reg = TC_INTFLAG_MC1;
_set_cc(tim, 1, value);
dev(tim)->INTENSET.reg = TC_INTENSET_MC1;
break;
default:
return -1;
}
return 0;
}
int timer_clear(tim_t tim, int channel)
{
switch (channel) {
case 0:
dev(tim)->INTFLAG.reg = TC_INTFLAG_MC0;
dev(tim)->INTENCLR.reg = TC_INTENCLR_MC0;
break;
case 1:
dev(tim)->INTFLAG.reg = TC_INTFLAG_MC1;
dev(tim)->INTENCLR.reg = TC_INTENCLR_MC1;
break;
default:
return -1;
}
return 0;
}
unsigned int timer_read(tim_t tim)
{
/* WORKAROUND to prevent being stuck there if timer not init */
if (!dev(tim)->CTRLA.bit.ENABLE) {
return 0;
}
/* request syncronisation */
#ifdef TC_CTRLBSET_CMD_READSYNC_Val
dev(tim)->CTRLBSET.reg = TC_CTRLBSET_CMD_READSYNC;
/* work around a possible hardware bug where it takes some
cycles for the timer peripheral to set the SYNCBUSY/READSYNC bit
after writing the READSYNC bit
The problem was observed on SAME54.
*/
while(dev(tim)->CTRLBSET.bit.CMD == TC_CTRLBSET_CMD_READSYNC_Val) {}
#else
dev(tim)->READREQ.reg = TC_READREQ_RREQ | TC_READREQ_ADDR(TC_COUNT32_COUNT_OFFSET);
#endif
wait_synchronization(tim);
return dev(tim)->COUNT.reg;
}
void timer_stop(tim_t tim)
{
dev(tim)->CTRLA.bit.ENABLE = 0;
}
void timer_start(tim_t tim)
{
dev(tim)->CTRLA.bit.ENABLE = 1;
}
static inline void timer_isr(tim_t tim)
{
TcCount32 *tc = dev(tim);
uint8_t status = tc->INTFLAG.reg;
/* Acknowledge all interrupts */
tc->INTFLAG.reg = status;
if ((status & TC_INTFLAG_MC0) && tc->INTENSET.bit.MC0) {
tc->INTENCLR.reg = TC_INTENCLR_MC0;
if (config[tim].cb) {
config[tim].cb(config[tim].arg, 0);
}
}
if ((status & TC_INTFLAG_MC1) && tc->INTENSET.bit.MC1) {
tc->INTENCLR.reg = TC_INTENCLR_MC1;
if (config[tim].cb) {
config[tim].cb(config[tim].arg, 1);
}
}
}
#ifdef TIMER_0_ISR
void TIMER_0_ISR(void)
{
timer_isr(0);
cortexm_isr_end();
}
#endif
#ifdef TIMER_1_ISR
void TIMER_1_ISR(void)
{
timer_isr(1);
cortexm_isr_end();
}
#endif
#ifdef TIMER_2_ISR
void TIMER_2_ISR(void)
{
timer_isr(2);
cortexm_isr_end();
}
#endif
#ifdef TIMER_3_ISR
void TIMER_3_ISR(void)
{
timer_isr(3);
cortexm_isr_end();
}
#endif