Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cpu/rpx0xx: add periph timer #16627

Merged
merged 2 commits into from
Aug 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions boards/rpi-pico/Makefile.features
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
CPU := rpx0xx

# Put defined MCU peripherals here (in alphabetical order)
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart
31 changes: 31 additions & 0 deletions boards/rpi-pico/include/periph_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include <stdint.h>

#include "kernel_defines.h"
#include "cpu.h"
#include "periph_cpu.h"

Expand Down Expand Up @@ -47,6 +48,36 @@ static const uart_conf_t uart_config[] = {

#define UART_NUMOF ARRAY_SIZE(uart_config)

static const timer_channel_conf_t timer0_channel_config[] = {
{
.irqn = TIMER_IRQ_0_IRQn
},
{
.irqn = TIMER_IRQ_1_IRQn
},
{
.irqn = TIMER_IRQ_2_IRQn
},
{
.irqn = TIMER_IRQ_3_IRQn
}
};

static const timer_conf_t timer_config[] = {
{
.dev = TIMER,
.ch = timer0_channel_config,
.ch_numof = ARRAY_SIZE(timer0_channel_config)
}
};

#define TIMER_0_ISRA isr_timer0
#define TIMER_0_ISRB isr_timer1
#define TIMER_0_ISRC isr_timer2
#define TIMER_0_ISRD isr_timer3

#define TIMER_NUMOF ARRAY_SIZE(timer_config)

#ifdef __cplusplus
}
#endif
Expand Down
2 changes: 2 additions & 0 deletions cpu/rpx0xx/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ config CPU_FAM_RPX0XX
select HAS_CPU_RPX0XX
select HAS_PERIPH_GPIO
select HAS_PERIPH_GPIO_IRQ
select HAS_PERIPH_TIMER
select HAS_PERIPH_TIMER_PERIODIC
select HAS_PERIPH_UART_MODECFG
select HAS_PERIPH_UART_RECONFIGURE

Expand Down
1 change: 1 addition & 0 deletions cpu/rpx0xx/Makefile.features
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ include $(RIOTCPU)/cortexm_common/Makefile.features

FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_gpio_irq
FEATURES_PROVIDED += periph_timer_periodic
FEATURES_PROVIDED += periph_uart_reconfigure
FEATURES_PROVIDED += periph_uart_modecfg
23 changes: 23 additions & 0 deletions cpu/rpx0xx/include/periph_cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,29 @@ typedef struct {
IRQn_Type irqn; /**< IRQ number of the UART interface */
} uart_conf_t;

/**
* @brief Prevent shared timer functions from being used
*/
#define PERIPH_TIMER_PROVIDES_SET

/**
* @brief Configuration type of a timer channel
*/
typedef struct {
IRQn_Type irqn; /**< timer channel interrupt number */
} timer_channel_conf_t;

/**
* @brief Configuration type of a timer device @ref timer_conf_t::dev,
* having @ref timer_conf_t::ch_numof number of channels,
* each one modeled as @ref timer_channel_conf_t
*/
typedef struct {
TIMER_Type *dev; /**< pointer to timer base address */
const timer_channel_conf_t *ch; /**< pointer to timer channel configuration */
uint8_t ch_numof; /**< number of timer channels */
} timer_conf_t;

/**
* @brief Get the PAD control register for the given GPIO pin as word
*
Expand Down
251 changes: 251 additions & 0 deletions cpu/rpx0xx/periph/timer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
/*
* Copyright (C) 2021 Otto-von-Guericke Universität Magdeburg
*
* 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_rpx0xx
* @ingroup drivers_periph_timer
* @{
*
* @file
* @brief Timer implementation for the RPX0XX
* @details The RPX0XX has a 64 bit µs timer but timer interrupts match
* on the lower 32 bits.
*
* @author Fabian Hüßler <fabian.huessler@ovgu.de>
*
* @}
*/

#include <errno.h>
#include <assert.h>
#include <stdint.h>
#include <stdbool.h>

#include "vendor/RP2040.h"
#include "io_reg.h"
#include "timex.h"
#include "periph_conf.h"
#include "periph/timer.h"

#define DEV(d) (timer_config[d].dev)
#define ALARM(d, a) ((&(DEV(d)->ALARM0)) + (a))

static timer_cb_t _timer_ctx_cb[TIMER_NUMOF];
static void *_timer_ctx_arg[TIMER_NUMOF];
static unsigned _timer_flag_periodic[TIMER_NUMOF];
static unsigned _timer_flag_reset[TIMER_NUMOF];

static inline uint64_t _timer_read_us(tim_t dev)
{
/* This is not safe when the second core also accesses the timer */
unsigned state = irq_disable();
uint32_t lo = DEV(dev)->TIMELR; /* always read timelr to latch the value of timehr */
uint32_t hi = DEV(dev)->TIMEHR; /* read timehr to unlatch */
irq_restore(state);
return ((uint64_t)hi << 32U) | lo;
}

static inline void _timer_reset(tim_t dev)
{
unsigned state = irq_disable();
DEV(dev)->TIMELW = 0; /* always write timelw before timehw */
DEV(dev)->TIMEHW = 0; /* writes do not get copied to time until timehw is written */
maribu marked this conversation as resolved.
Show resolved Hide resolved
irq_restore(state);
}

static inline void _timer_enable_periodic(tim_t dev, int channel, uint8_t flags)
{
_timer_flag_periodic[dev] |= (1U << channel);
if (flags & TIM_FLAG_RESET_ON_MATCH) {
_timer_flag_reset[dev] |= (1U << channel);
}
else {
_timer_flag_reset[dev] &= ~(1U << channel);
}
}

static inline void _timer_disable_periodic(tim_t dev, int channel)
{
_timer_flag_periodic[dev] &= ~(1U << channel);
}

static inline bool _timer_is_periodic(tim_t dev, int channel)
{
return !!(_timer_flag_periodic[dev] & (1U << channel));
}

static inline bool _timer_reset_on_match(tim_t dev, int channel)
{
return !!(_timer_flag_reset[dev] & (1U << channel));
}

static inline void _irq_enable(tim_t dev)
{
for (uint8_t i = 0; i < timer_config[dev].ch_numof; i++) {
NVIC_EnableIRQ(timer_config[dev].ch[i].irqn);
io_reg_atomic_set(&DEV(dev)->INTE.reg, (1U << i));
}
}

static void _isr(tim_t dev, int channel)
{
/* clear latched interrupt */
io_reg_atomic_clear(&DEV(dev)->INTR.reg, 1U << channel);

if (_timer_is_periodic(dev, channel)) {
if (_timer_reset_on_match(dev, channel)) {
_timer_reset(dev);
}
/* rearm */
*ALARM(dev, channel) = *ALARM(dev, channel);
}
maribu marked this conversation as resolved.
Show resolved Hide resolved
if (_timer_ctx_cb[dev]) {
_timer_ctx_cb[dev](_timer_ctx_arg[dev], channel);
}

cortexm_isr_end();
}

int timer_init(tim_t dev, uint32_t freq, timer_cb_t cb, void *arg)
{
if (dev >= TIMER_NUMOF) {
return -ENODEV;
}
/* The timer must run at 1000000 Hz (µs precision)
because the number of cycles per µs is shared with the watchdog.
The reference clock (clk_ref) is divided by WATCHDOG->TICK.bits.CYCLES
to generate µs ticks.
*/
assert(freq == US_PER_SEC); (void)freq;
_timer_ctx_cb[dev] = cb;
_timer_ctx_arg[dev] = arg;
periph_reset(RESETS_RESET_timer_Msk);
periph_reset_done(RESETS_RESET_timer_Msk);
io_reg_write_dont_corrupt(&WATCHDOG->TICK.reg,
(CLOCK_XOSC / MHZ(1)) << WATCHDOG_TICK_CYCLES_Pos,
WATCHDOG_TICK_CYCLES_Msk);
_irq_enable(dev);
return 0;
}

int timer_set(tim_t dev, int channel, unsigned int timeout)
{
if (dev >= TIMER_NUMOF) {
return -ENODEV;
}
if (channel < 0 || channel >= timer_config[dev].ch_numof) {
return -EINVAL;
}
if (!timeout) {
/* execute callback immediately if timeout equals 0,
to ctach the case that a tick happens right before arming the alarm
and causes a full timer period to elaps */
if (_timer_ctx_cb[dev]) {
_timer_ctx_cb[dev](_timer_ctx_arg[dev], channel);
}
}
else {
unsigned state = irq_disable();
_timer_disable_periodic(dev, channel);
/* an alarm interrupt matches on the lower 32 bit of the 64 bit timer counter */
uint64_t target = DEV(dev)->TIMERAWL + timeout;
*ALARM(dev, channel) = (uint32_t)target;
irq_restore(state);
}
return 0;
}

int timer_set_absolute(tim_t dev, int channel, unsigned int value)
{
if (dev >= TIMER_NUMOF) {
return -ENODEV;
}
if (channel < 0 || channel >= timer_config[dev].ch_numof) {
return -EINVAL;
}
unsigned state = irq_disable();
_timer_disable_periodic(dev, channel);
*ALARM(dev, channel) = (uint32_t)value;
irq_restore(state);
return 0;
}

int timer_set_periodic(tim_t dev, int channel, unsigned int value, uint8_t flags)
{
if (dev >= TIMER_NUMOF) {
return -ENODEV;
}
if (channel < 0 || channel >= timer_config[dev].ch_numof) {
return -EINVAL;
}
if (flags & TIM_FLAG_RESET_ON_SET) {
_timer_reset(dev);
}
unsigned state = irq_disable();
_timer_enable_periodic(dev, channel, flags);
*ALARM(dev, channel) = (uint32_t)value;
irq_restore(state);
return 0;
}

int timer_clear(tim_t dev, int channel)
{
if (dev >= TIMER_NUMOF) {
return -ENODEV;
}
if (channel < 0 || channel >= timer_config[dev].ch_numof) {
return -EINVAL;
}
/* ARMED bits are write clear */
io_reg_atomic_set(&DEV(dev)->ARMED.reg, (1 << channel));
unsigned state = irq_disable();
_timer_disable_periodic(dev, channel);
irq_restore(state);
return 0;
}

unsigned int timer_read(tim_t dev)
{
if (dev >= TIMER_NUMOF) {
return -ENODEV;
}
return _timer_read_us(dev);
}

void timer_start(tim_t dev)
{
assert(dev < TIMER_NUMOF);
io_reg_atomic_clear(&DEV(dev)->PAUSE.reg, (1 << TIMER_PAUSE_PAUSE_Pos));
}

void timer_stop(tim_t dev)
{
assert(dev < TIMER_NUMOF);
io_reg_atomic_set(&DEV(dev)->PAUSE.reg, (1 << TIMER_PAUSE_PAUSE_Pos));
}

/* timer 0 IRQ0 */
void TIMER_0_ISRA(void)
{
_isr(0, 0);
}
/* timer 0 IRQ1 */
void TIMER_0_ISRB(void)
{
_isr(0, 1);
}
/* timer 0 IRQ2 */
void TIMER_0_ISRC(void)
{
_isr(0, 2);
}
/* timer 0 IRQ3 */
void TIMER_0_ISRD(void)
{
_isr(0, 3);
}
4 changes: 2 additions & 2 deletions tests/ztimer_periodic/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ static mutex_t _mutex = MUTEX_INIT_LOCKED;

static uint32_t _times[N];
static int _count;
#define CLOCKS { ZTIMER_MSEC, ZTIMER_USEC }
#define ZTIMER_CLOCKS { ZTIMER_MSEC, ZTIMER_USEC }
static const char *_names[] = { "ZTIMER_MSEC", "ZTIMER_USEC" };
static uint32_t _intervals[] = { 100, 10000 };
static uint32_t _max_offsets[] = { 2, 100 };
Expand Down Expand Up @@ -64,7 +64,7 @@ static int callback(void *arg)
int main(void)
{
ztimer_periodic_t t;
ztimer_clock_t * const clocks[] = CLOCKS;
ztimer_clock_t * const clocks[] = ZTIMER_CLOCKS;
int failed = 0;

for (size_t j = 0; j < ARRAY_SIZE(clocks); j++) {
Expand Down