From b6a7815b48cc221ab40a6feaaf6104d645d8cb61 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Fri, 28 Apr 2023 12:01:12 +0200 Subject: [PATCH] cpu/sam0_common: implement periph_timer_query_freqs --- cpu/sam0_common/Kconfig | 1 + cpu/sam0_common/Makefile.features | 4 ++- cpu/sam0_common/periph/timer.c | 48 ++++++++++++++++++++++++------- 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/cpu/sam0_common/Kconfig b/cpu/sam0_common/Kconfig index fe4c1afc4200..a3c64947dfc1 100644 --- a/cpu/sam0_common/Kconfig +++ b/cpu/sam0_common/Kconfig @@ -25,6 +25,7 @@ config CPU_COMMON_SAM0 select HAS_PERIPH_SPI_RECONFIGURE select HAS_PERIPH_SPI_GPIO_MODE select HAS_PERIPH_TIMER_PERIODIC + select HAS_PERIPH_TIMER_QUERY_FREQS select HAS_PERIPH_UART_MODECFG select HAS_PERIPH_UART_NONBLOCKING select HAS_PERIPH_UART_RECONFIGURE diff --git a/cpu/sam0_common/Makefile.features b/cpu/sam0_common/Makefile.features index 2f01bb135ea7..dba45d96d92c 100644 --- a/cpu/sam0_common/Makefile.features +++ b/cpu/sam0_common/Makefile.features @@ -14,15 +14,17 @@ FEATURES_PROVIDED += periph_flashpage_pagewise FEATURES_PROVIDED += periph_flashpage_rwee FEATURES_PROVIDED += periph_gpio periph_gpio_irq FEATURES_PROVIDED += periph_i2c_reconfigure -FEATURES_PROVIDED += periph_rtt_set_counter FEATURES_PROVIDED += periph_rtt_overflow +FEATURES_PROVIDED += periph_rtt_set_counter FEATURES_PROVIDED += periph_sdmmc_auto_cmd12 FEATURES_PROVIDED += periph_sdmmc_hs FEATURES_PROVIDED += periph_sdmmc_mmc FEATURES_PROVIDED += periph_sdmmc_sdhc FEATURES_PROVIDED += periph_spi_reconfigure FEATURES_PROVIDED += periph_spi_gpio_mode +FEATURES_PROVIDED += periph_spi_reconfigure FEATURES_PROVIDED += periph_timer_periodic # implements timer_set_periodic() +FEATURES_PROVIDED += periph_timer_query_freqs FEATURES_PROVIDED += periph_uart_modecfg FEATURES_PROVIDED += periph_uart_nonblocking FEATURES_PROVIDED += periph_uart_reconfigure diff --git a/cpu/sam0_common/periph/timer.c b/cpu/sam0_common/periph/timer.c index 4c13bc2e91ab..368f6afd66cc 100644 --- a/cpu/sam0_common/periph/timer.c +++ b/cpu/sam0_common/periph/timer.c @@ -43,6 +43,22 @@ static timer_isr_ctx_t config[TIMER_NUMOF]; static uint32_t _oneshot; +/* Number of right-shifts to perform on the input frequency to get the output + * frequency the prescaler will divide it down to */ +static const uint8_t _prescaler_shifts[] = { + [TC_CTRLA_PRESCALER_DIV1_Val] = 0, + [TC_CTRLA_PRESCALER_DIV2_Val] = 1, + [TC_CTRLA_PRESCALER_DIV4_Val] = 2, + [TC_CTRLA_PRESCALER_DIV8_Val] = 3, + [TC_CTRLA_PRESCALER_DIV16_Val] = 4, + [TC_CTRLA_PRESCALER_DIV64_Val] = 6, + [TC_CTRLA_PRESCALER_DIV256_Val] = 8, + [TC_CTRLA_PRESCALER_DIV1024_Val] = 10, +}; + +static_assert(ARRAY_SIZE(_prescaler_shifts) == (TC_CTRLA_PRESCALER_DIV1024_Val + 1), + "_prescaler_shifts needs an update for the selected MCU"); + static inline void set_oneshot(tim_t tim, int chan) { _oneshot |= (1 << chan) << (TIMER_CHANNEL_NUMOF * tim); @@ -94,20 +110,13 @@ static inline void _irq_enable(tim_t tim) static uint8_t _get_prescaler(uint32_t freq_out, uint32_t freq_in) { - uint8_t scale = 0; - while (freq_in > freq_out) { - freq_in >>= 1; - - /* after DIV16 the prescaler gets more coarse */ - if (++scale > TC_CTRLA_PRESCALER_DIV16_Val) { - freq_in >>= 1; + for (uint8_t scale = 0; scale < ARRAY_SIZE(_prescaler_shifts); scale++) { + if ((freq_in >> _prescaler_shifts[scale]) == freq_out) { + return scale; } } - /* fail if output frequency can't be derived from input frequency */ - assert(freq_in == freq_out); - - return scale; + return UINT8_MAX; } /* TOP value is CC0 */ @@ -130,6 +139,23 @@ static inline void _set_nfrq(tim_t tim) #endif } +uword_t timer_query_freqs_numof(tim_t dev) +{ + assert(dev < TIMER_NUMOF); + (void)dev; + return ARRAY_SIZE(_prescaler_shifts); +} + +uint32_t timer_query_freqs(tim_t dev, uword_t index) +{ + assert(dev < TIMER_NUMOF); + const tc32_conf_t *cfg = &timer_config[dev]; + if (index >= ARRAY_SIZE(_prescaler_shifts)) { + return 0; + } + return sam0_gclk_freq(cfg->gclk_src) >> _prescaler_shifts[index]; +} + /** * @brief Setup the given timer */