From 568448f68cb3dc8693e77294965b3b26eeefeafd Mon Sep 17 00:00:00 2001 From: Jue Date: Tue, 18 Oct 2022 10:55:01 +0200 Subject: [PATCH] cpu/efm32/timer: add series 2 periph driver --- cpu/efm32/include/periph_cpu.h | 18 +- cpu/efm32/periph/Makefile | 9 + .../periph/{timer.c => timer_series01.c} | 0 cpu/efm32/periph/timer_series2.c | 343 ++++++++++++++++++ 4 files changed, 367 insertions(+), 3 deletions(-) rename cpu/efm32/periph/{timer.c => timer_series01.c} (100%) create mode 100644 cpu/efm32/periph/timer_series2.c diff --git a/cpu/efm32/include/periph_cpu.h b/cpu/efm32/include/periph_cpu.h index 4792bc82e0..c7db1d5faf 100644 --- a/cpu/efm32/include/periph_cpu.h +++ b/cpu/efm32/include/periph_cpu.h @@ -385,21 +385,33 @@ typedef struct { /** * @brief Define timer configuration values * - * @note The two timers must be adjacent to each other (e.g. TIMER0 and - * TIMER1, or TIMER2 and TIMER3, etc.). + * @note For the configuration of series 0 and 1, prescale and actual timer + * must be adjacent to each other (e.g. TIMER0 and TIMER1, or TIMER2 + * and TIMER3, etc.). * @{ */ +#if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1) || defined(DOXYGEN) typedef struct { void *dev; /**< TIMER_TypeDef or LETIMER_TypeDef device used */ CMU_Clock_TypeDef cmu; /**< the device CMU channel */ } timer_dev_t; +#endif typedef struct { +#if defined(_SILICON_LABS_32B_SERIES_0) || defined(_SILICON_LABS_32B_SERIES_1) || defined(DOXYGEN) timer_dev_t prescaler; /**< the lower neighboring timer (not initialized for LETIMER) */ timer_dev_t timer; /**< the higher numbered timer */ IRQn_Type irq; /**< number of the higher timer IRQ channel */ - uint8_t channel_numof; /**< number of channels per timer */ + uint8_t channel_numof; /**< number of channels per timer */ +#else + void *dev; /**< TIMER_TypeDef or LETIMER_TypeDef device used */ + CMU_Clock_TypeDef cmu; /**< the device CMU channel */ + IRQn_Type irq; /**< number of the higher timer IRQ channel */ +#endif } timer_conf_t; + +#define LETIMER_MAX_VALUE _LETIMER_TOP_MASK /**< max timer value of LETIMER peripheral */ +#define TIMER_MAX_VALUE _TIMER_TOP_MASK /**< max timer value of TIMER peripheral */ /** @} */ /** diff --git a/cpu/efm32/periph/Makefile b/cpu/efm32/periph/Makefile index d9dc96a0b5..65086fa9c0 100644 --- a/cpu/efm32/periph/Makefile +++ b/cpu/efm32/periph/Makefile @@ -18,6 +18,15 @@ ifneq (,$(filter periph_rtt,$(USEMODULE))) endif endif +# Select the correct implementation for `periph_timer` +ifneq (,$(filter periph_timer,$(USEMODULE))) + ifeq (2,$(EFM32_SERIES)) + SRC += timer_series2.c + else ifneq (,$(filter $(EFM32_SERIES),0 1)) + SRC += timer_series01.c + endif +endif + # Select the correct implementation for `periph_uart` ifneq (,$(filter periph_uart,$(USEMODULE))) ifeq (2,$(EFM32_SERIES)) diff --git a/cpu/efm32/periph/timer.c b/cpu/efm32/periph/timer_series01.c similarity index 100% rename from cpu/efm32/periph/timer.c rename to cpu/efm32/periph/timer_series01.c diff --git a/cpu/efm32/periph/timer_series2.c b/cpu/efm32/periph/timer_series2.c new file mode 100644 index 0000000000..59b3274294 --- /dev/null +++ b/cpu/efm32/periph/timer_series2.c @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2022 SSV Software Systems 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_efm32 + * @ingroup drivers_periph_timer + * @{ + * + * @file + * @brief Low-level timer driver implementation + * + * @author Juergen Fitschen + * @} + */ + +#include "cpu.h" +#include "log.h" +#include "assert.h" +#include "periph/timer.h" +#include "periph_conf.h" +#include "pm_layered.h" + +#include "em_cmu.h" +#include "em_timer.h" +#include "em_timer_utils.h" +#include "em_letimer.h" + +/** + * @brief These power modes will be blocked while the timer is running + */ +#ifndef TIMER_PM_BLOCKER +#define TIMER_PM_BLOCKER EFM32_PM_MODE_EM2 +#endif +#ifndef LETIMER_PM_BLOCKER +#define LETIMER_PM_BLOCKER EFM32_PM_MODE_EM3 +#endif + +/** + * @brief Timer state memory + */ +static timer_isr_ctx_t isr_ctx[TIMER_NUMOF]; + +/** + * @brief Check whether dev is using a LETIMER device + */ +static inline bool _is_letimer(tim_t dev) +{ +#if defined(LETIMER_COUNT) && (LETIMER_COUNT > 0) + return ((uint32_t) timer_config[dev].dev) == LETIMER0_BASE; +#else + (void) dev; + return false; +#endif +} + +#define LETIMER_CH_VALID(x) (x < 2) + +static inline void _letimer_init(tim_t dev, uint32_t freq) +{ + (void) freq; + assert(freq == CMU_ClockFreqGet(timer_config[dev].cmu)); + + LETIMER_TypeDef *tim = timer_config[dev].dev; + + /* disable and clear interrupts */ + LETIMER_IntDisable(tim, LETIMER_IEN_COMP0 | LETIMER_IEN_COMP1); + LETIMER_IntClear(tim, LETIMER_IF_COMP0 | LETIMER_IF_COMP1); + + /* initialize timer without starting it yet */ + LETIMER_Init_TypeDef init = LETIMER_INIT_DEFAULT; + init.enable = false; + init.topValue = LETIMER_MAX_VALUE; + LETIMER_Init(tim, &init); +} + +static inline void _timer_init(tim_t dev, uint32_t freq) +{ + /* bring peripheral into known state */ + TIMER_Reset(timer_config[dev].dev); + + /* get input clock of the timer */ + uint32_t clk_freq = CMU_ClockFreqGet(timer_config[dev].cmu); + + /* initialize timer */ + TIMER_Init_TypeDef init = TIMER_INIT_DEFAULT; + init.enable = false; + init.prescale = (clk_freq / freq) - 1; + /* check if any rounding errors occurred */ + assert(clk_freq / (init.prescale + 1) == freq); + TIMER_Init(timer_config[dev].dev, &init); + + /* set top variable */ + TIMER_TopSet(timer_config[dev].dev, TIMER_MAX_VALUE); + + /* initialize CC channels */ + TIMER_InitCC_TypeDef init_cc = TIMER_INITCC_DEFAULT; + init_cc.mode = timerCCModeCompare; + for (size_t i = 0; TIMER_CH_VALID(i); i++) { + TIMER_InitCC(timer_config[dev].dev, i, &init_cc); + } +} + +int timer_init(tim_t dev, uint32_t freq, timer_cb_t callback, void *arg) +{ + assert(dev < TIMER_NUMOF); + + /* enable clocks */ + CMU_ClockEnable(timer_config[dev].cmu, true); + + /* init underlying hardware */ + _is_letimer(dev) ? _letimer_init(dev, freq) : _timer_init(dev, freq); + + /* save callback */ + isr_ctx[dev].cb = callback; + isr_ctx[dev].arg = arg; + + /* setup Cortex-M IRQ line */ + NVIC_ClearPendingIRQ(timer_config[dev].irq); + NVIC_EnableIRQ(timer_config[dev].irq); + + timer_start(dev); + + return 0; +} + +static inline int _letimer_set_absolute(tim_t dev, int channel, unsigned int value) +{ + if (!LETIMER_CH_VALID(channel)) { + return -1; + } + + LETIMER_TypeDef *tim = timer_config[dev].dev; + + /* LETIMER is countdown only, so we invert the value */ + value = LETIMER_MAX_VALUE - value; + LETIMER_CompareSet(tim, channel, value); + + uint32_t irq_bit = (LETIMER_IF_COMP0 << channel); + LETIMER_IntClear(tim, irq_bit); + LETIMER_IntEnable(tim, irq_bit); + + return 0; +} + +static inline int _timer_set_absolute(tim_t dev, int channel, unsigned int value) +{ + if (!TIMER_CH_VALID(channel)) { + return -1; + } + + TIMER_TypeDef *tim = timer_config[dev].dev; + uint32_t irq_bit = (TIMER_IF_CC0 << channel); + + /* make sure to clear previously set irqs */ + TIMER_IntClear(tim, irq_bit); + + /* set compare value */ + TIMER_CompareSet(tim, channel, value); + + /* turn on IRQs */ + TIMER_IntEnable(tim, irq_bit); + + return 0; +} + +int timer_set_absolute(tim_t dev, int channel, unsigned int value) +{ + return _is_letimer(dev) ? _letimer_set_absolute(dev, channel, value) : _timer_set_absolute(dev, channel, value); +} + +static inline int _letimer_clear(tim_t dev, int channel) +{ + if (!LETIMER_CH_VALID(channel)) { + return -1; + } + + LETIMER_TypeDef *tim = timer_config[dev].dev; + uint32_t irq_bit = (LETIMER_IF_COMP0 << channel); + LETIMER_IntDisable(tim, irq_bit); + LETIMER_IntClear(tim, irq_bit); + + return 0; +} + +static inline int _timer_clear(tim_t dev, int channel) +{ + if (!TIMER_CH_VALID(channel)) { + return -1; + } + + /* turn off output compare IRQ */ + TIMER_IntDisable(timer_config[dev].dev, (TIMER_IF_CC0 << channel)); + + return 0; +} + +int timer_clear(tim_t dev, int channel) +{ + return _is_letimer(dev) ? _letimer_clear(dev, channel) : _timer_clear(dev, channel); +} + +static inline unsigned int _letimer_read(tim_t dev) +{ + LETIMER_TypeDef *tim = timer_config[dev].dev; + + /* LETIMER is countdown only, so we invert the value */ + return (unsigned int) LETIMER_MAX_VALUE - LETIMER_CounterGet(tim); +} + +static inline unsigned int _timer_read(tim_t dev) +{ + TIMER_TypeDef *tim = timer_config[dev].dev; + + return (unsigned int) TIMER_CounterGet(tim); +} + +unsigned int timer_read(tim_t dev) +{ + return _is_letimer(dev) ? _letimer_read(dev) : _timer_read(dev); +} + +static inline void _letimer_stop(tim_t dev) +{ + LETIMER_TypeDef *tim = timer_config[dev].dev; + + if (tim->STATUS & LETIMER_STATUS_RUNNING) { + pm_unblock(LETIMER_PM_BLOCKER); + } + + LETIMER_Enable(timer_config[dev].dev, false); +} + +static inline void _timer_stop(tim_t dev) +{ + TIMER_TypeDef *tim = timer_config[dev].dev; + + if (tim->STATUS & TIMER_STATUS_RUNNING) { + pm_unblock(TIMER_PM_BLOCKER); + } + + TIMER_Enable(timer_config[dev].dev, false); +} + +void timer_stop(tim_t dev) +{ + _is_letimer(dev) ? _letimer_stop(dev) : _timer_stop(dev); +} + +static inline void _letimer_start(tim_t dev) +{ + LETIMER_TypeDef *tim = timer_config[dev].dev; + + if (tim->STATUS & LETIMER_STATUS_RUNNING) { + pm_block(LETIMER_PM_BLOCKER); + } + + LETIMER_Enable(timer_config[dev].dev, true); +} + +static inline void _timer_start(tim_t dev) +{ + TIMER_TypeDef *tim = timer_config[dev].dev; + + if (tim->STATUS & TIMER_STATUS_RUNNING) { + pm_block(TIMER_PM_BLOCKER); + } + + TIMER_Enable(timer_config[dev].dev, true); +} + +void timer_start(tim_t dev) +{ + _is_letimer(dev) ? _letimer_start(dev) : _timer_start(dev); +} + +static inline void _letimer_isr(tim_t dev) +{ + LETIMER_TypeDef *tim = timer_config[dev].dev; + + for (int i = 0; TIMER_CH_VALID(i); i++) { + if (tim->IF & (LETIMER_IF_COMP0 << i)) + { + LETIMER_IntDisable(tim, LETIMER_IEN_COMP0 << i); + LETIMER_IntClear(tim, LETIMER_IF_COMP0 << i); + isr_ctx[dev].cb(isr_ctx[dev].arg, i); + } + } +} + +static inline void _timer_isr(tim_t dev) +{ + TIMER_TypeDef *tim = timer_config[dev].dev; + + for (size_t i = 0; TIMER_CH_VALID(i); i++) { + uint32_t irq_bit = (TIMER_IF_CC0 << i); + + if (TIMER_IntGetEnabled(tim) & irq_bit) { + TIMER_IntDisable(tim, irq_bit); + TIMER_IntClear(tim, irq_bit); + isr_ctx[dev].cb(isr_ctx[dev].arg, i); + } + } +} + +static void _isr(tim_t dev) +{ + _is_letimer(dev) ? _letimer_isr(dev) : _timer_isr(dev); + + cortexm_isr_end(); +} + +#ifdef TIMER_0_ISR +void TIMER_0_ISR(void) +{ + _isr(0); +} +#endif /* TIMER_0_ISR */ + +#ifdef TIMER_1_ISR +void TIMER_1_ISR(void) +{ + _isr(1); +} +#endif /* TIMER_1_ISR */ + +#ifdef TIMER_2_ISR +void TIMER_2_ISR(void) +{ + _isr(2); +} +#endif /* TIMER_2_ISR */ + +#ifdef TIMER_3_ISR +void TIMER_3_ISR(void) +{ + _isr(3); +} +#endif /* TIMER_3_ISR */