From 1248442b8bad5a88f3d8157ca7c44e6421666155 Mon Sep 17 00:00:00 2001 From: Kent Hall Date: Wed, 6 Oct 2021 11:55:26 -0400 Subject: [PATCH 1/2] drivers: counter: Counter API implementation for STM32F4 Series (TIMER). - Shim for counter API using LL_TIM driver. - Supports all general-purpose (TIMx) timers. Signed-off-by: Kent Hall --- drivers/counter/CMakeLists.txt | 1 + drivers/counter/Kconfig | 2 + drivers/counter/Kconfig.stm32_timer | 11 + drivers/counter/counter_ll_stm32_timer.c | 615 +++++++++++++++++++++ dts/arm/st/f4/stm32f4.dtsi | 42 ++ dts/arm/st/f4/stm32f405.dtsi | 30 + dts/arm/st/f4/stm32f410.dtsi | 6 + dts/arm/st/f4/stm32f412.dtsi | 30 + dts/bindings/counter/st,stm32-counter.yaml | 12 + 9 files changed, 749 insertions(+) create mode 100644 drivers/counter/Kconfig.stm32_timer create mode 100644 drivers/counter/counter_ll_stm32_timer.c create mode 100644 dts/bindings/counter/st,stm32-counter.yaml diff --git a/drivers/counter/CMakeLists.txt b/drivers/counter/CMakeLists.txt index 286e9b876d1df..3c59e43b94968 100644 --- a/drivers/counter/CMakeLists.txt +++ b/drivers/counter/CMakeLists.txt @@ -12,6 +12,7 @@ zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_LPC_RTC counter_mcux_lpc zephyr_library_sources_ifdef(CONFIG_COUNTER_NRF_TIMER counter_nrfx_timer.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_NRF_RTC counter_nrfx_rtc.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_RTC_STM32 counter_ll_stm32_rtc.c) +zephyr_library_sources_ifdef(CONFIG_COUNTER_TIMER_STM32 counter_ll_stm32_timer.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_SAM_TC counter_sam_tc.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_SAM0_TC32 counter_sam0_tc32.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_CMOS counter_cmos.c) diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index cfdf376b8a9ba..228b389461f2e 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -36,6 +36,8 @@ source "drivers/counter/Kconfig.imx_epit" source "drivers/counter/Kconfig.stm32_rtc" +source "drivers/counter/Kconfig.stm32_timer" + source "drivers/counter/Kconfig.sam" source "drivers/counter/Kconfig.sam0" diff --git a/drivers/counter/Kconfig.stm32_timer b/drivers/counter/Kconfig.stm32_timer new file mode 100644 index 0000000000000..2a75c1ab0de58 --- /dev/null +++ b/drivers/counter/Kconfig.stm32_timer @@ -0,0 +1,11 @@ +# Copyright (c) 2021 Kent Hall +# SPDX-License-Identifier: Apache-2.0 + +DT_COMPAT_ST_STM32_COUNTER := st,stm32-counter + +config COUNTER_TIMER_STM32 + bool "STM32 counter driver" + default $(dt_compat_enabled,$(DT_COMPAT_ST_STM32_COUNTER)) + select USE_STM32_LL_TIM + help + Enable the counter driver for STM32 family of processors. diff --git a/drivers/counter/counter_ll_stm32_timer.c b/drivers/counter/counter_ll_stm32_timer.c new file mode 100644 index 0000000000000..4e4dfeefef86a --- /dev/null +++ b/drivers/counter/counter_ll_stm32_timer.c @@ -0,0 +1,615 @@ +/* + * Copyright (c) 2021 Kent Hall. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT st_stm32_counter + +#include +#include +#include + +#include +#include + +#include +LOG_MODULE_REGISTER(counter_timer_stm32, CONFIG_COUNTER_LOG_LEVEL); + +/** Maximum number of timer channels. */ +#define TIMER_MAX_CH 4U + +/** Number of channels for timer by index. */ +#define NUM_CH(timx) \ + (IS_TIM_CCX_INSTANCE(timx, TIM_CHANNEL_4) ? 4U : \ + (IS_TIM_CCX_INSTANCE(timx, TIM_CHANNEL_3) ? 3U : \ + (IS_TIM_CCX_INSTANCE(timx, TIM_CHANNEL_2) ? 2U : \ + (IS_TIM_CCX_INSTANCE(timx, TIM_CHANNEL_1) ? 1U : \ + 0)))) + +/** Channel to compare set function mapping. */ +static void(*const set_timer_compare[TIMER_MAX_CH])(TIM_TypeDef *, + uint32_t) = { + LL_TIM_OC_SetCompareCH1, LL_TIM_OC_SetCompareCH2, + LL_TIM_OC_SetCompareCH3, LL_TIM_OC_SetCompareCH4, +}; + +/** Channel to compare get function mapping. */ +static uint32_t(*const get_timer_compare[TIMER_MAX_CH])(TIM_TypeDef *) = { + LL_TIM_OC_GetCompareCH1, LL_TIM_OC_GetCompareCH2, + LL_TIM_OC_GetCompareCH3, LL_TIM_OC_GetCompareCH4, +}; + +/** Channel to interrupt enable function mapping. */ +static void(*const enable_it[TIMER_MAX_CH])(TIM_TypeDef *) = { + LL_TIM_EnableIT_CC1, LL_TIM_EnableIT_CC2, + LL_TIM_EnableIT_CC3, LL_TIM_EnableIT_CC4, +}; + +/** Channel to interrupt enable function mapping. */ +static void(*const disable_it[TIMER_MAX_CH])(TIM_TypeDef *) = { + LL_TIM_DisableIT_CC1, LL_TIM_DisableIT_CC2, + LL_TIM_DisableIT_CC3, LL_TIM_DisableIT_CC4, +}; + +#ifdef CONFIG_ASSERT +/** Channel to interrupt enable check function mapping. */ +static uint32_t(*const check_it_enabled[TIMER_MAX_CH])(TIM_TypeDef *) = { + LL_TIM_IsEnabledIT_CC1, LL_TIM_IsEnabledIT_CC2, + LL_TIM_IsEnabledIT_CC3, LL_TIM_IsEnabledIT_CC4, +}; +#endif + +/** Channel to interrupt flag clear function mapping. */ +static void(*const clear_it_flag[TIMER_MAX_CH])(TIM_TypeDef *) = { + LL_TIM_ClearFlag_CC1, LL_TIM_ClearFlag_CC2, + LL_TIM_ClearFlag_CC3, LL_TIM_ClearFlag_CC4, +}; + +struct counter_stm32_data { + counter_top_callback_t top_cb; + void *top_user_data; + uint32_t guard_period; + atomic_t cc_int_pending; + uint32_t freq; +}; + +struct counter_stm32_ch_data { + counter_alarm_callback_t callback; + void *user_data; +}; + +struct counter_stm32_config { + struct counter_config_info info; + struct counter_stm32_ch_data *ch_data; + TIM_TypeDef *timer; + uint32_t prescaler; + struct stm32_pclken pclken; + void (*irq_config_func)(const struct device *dev); + uint32_t irqn; + + LOG_INSTANCE_PTR_DECLARE(log); +}; + +#define DEV_DATA(dev) ((struct counter_stm32_data *)(dev)->data) +#define DEV_CFG(dev) \ + ((const struct counter_stm32_config *const)(dev)->config) + +static int counter_stm32_start(const struct device *dev) +{ + TIM_TypeDef *timer = DEV_CFG(dev)->timer; + + /* enable counter */ + LL_TIM_EnableCounter(timer); + + return 0; +} + +static int counter_stm32_stop(const struct device *dev) +{ + TIM_TypeDef *timer = DEV_CFG(dev)->timer; + + /* disable counter */ + LL_TIM_DisableCounter(timer); + + return 0; +} + +static uint32_t counter_stm32_get_top_value(const struct device *dev) +{ + return LL_TIM_GetAutoReload(DEV_CFG(dev)->timer); +} + +static uint32_t counter_stm32_read(const struct device *dev) +{ + return LL_TIM_GetCounter(DEV_CFG(dev)->timer); +} + +static int counter_stm32_get_value(const struct device *dev, uint32_t *ticks) +{ + *ticks = counter_stm32_read(dev); + return 0; +} + +/* Return true if value equals 2^n - 1 */ +static inline bool counter_stm32_is_bit_mask(uint32_t val) +{ + return !(val & (val + 1U)); +} + +static uint32_t counter_stm32_ticks_add(uint32_t val1, uint32_t val2, uint32_t top) +{ + uint32_t to_top; + + if (likely(counter_stm32_is_bit_mask(top))) { + return (val1 + val2) & top; + } + + to_top = top - val1; + + return (val2 <= to_top) ? val1 + val2 : val2 - to_top - 1U; +} + +static uint32_t counter_stm32_ticks_sub(uint32_t val, uint32_t old, uint32_t top) +{ + if (likely(counter_stm32_is_bit_mask(top))) { + return (val - old) & top; + } + + /* if top is not 2^n-1 */ + return (val >= old) ? (val - old) : val + top + 1U - old; +} + +static void counter_stm32_counter_stm32_set_cc_int_pending(const struct device *dev, uint8_t chan) +{ + atomic_or(&DEV_DATA(dev)->cc_int_pending, BIT(chan)); + NVIC_SetPendingIRQ(DEV_CFG(dev)->irqn); +} + +static int counter_stm32_set_cc(const struct device *dev, uint8_t id, + const struct counter_alarm_cfg *alarm_cfg) +{ + __ASSERT_NO_MSG(DEV_DATA(dev)->guard_period < counter_stm32_get_top_value(dev)); + uint32_t val = alarm_cfg->ticks; + uint32_t flags = alarm_cfg->flags; + bool absolute = flags & COUNTER_ALARM_CFG_ABSOLUTE; + bool irq_on_late; + TIM_TypeDef *timer = DEV_CFG(dev)->timer; + uint32_t top = counter_stm32_get_top_value(dev); + int err = 0; + uint32_t prev_val; + uint32_t now; + uint32_t diff; + uint32_t max_rel_val; + + __ASSERT(!check_it_enabled[id](timer), + "Expected that CC interrupt is disabled."); + + /* First take care of a risk of an event coming from CC being set to + * next tick. Reconfigure CC to future (now tick is the furtherest + * future). + */ + now = counter_stm32_read(dev); + prev_val = get_timer_compare[id](timer); + set_timer_compare[id](timer, now); + clear_it_flag[id](timer); + + if (absolute) { + max_rel_val = top - DEV_DATA(dev)->guard_period; + irq_on_late = flags & COUNTER_ALARM_CFG_EXPIRE_WHEN_LATE; + } else { + /* If relative value is smaller than half of the counter range + * it is assumed that there is a risk of setting value too late + * and late detection algorithm must be applied. When late + * setting is detected, interrupt shall be triggered for + * immediate expiration of the timer. Detection is performed + * by limiting relative distance between CC and counter. + * + * Note that half of counter range is an arbitrary value. + */ + irq_on_late = val < (top / 2U); + /* limit max to detect short relative being set too late. */ + max_rel_val = irq_on_late ? top / 2U : top; + val = counter_stm32_ticks_add(now, val, top); + } + + set_timer_compare[id](timer, val); + + /* decrement value to detect also case when val == counter_stm32_read(dev). Otherwise, + * condition would need to include comparing diff against 0. + */ + diff = counter_stm32_ticks_sub(val - 1U, counter_stm32_read(dev), top); + if (diff > max_rel_val) { + if (absolute) { + err = -ETIME; + } + + /* Interrupt is triggered always for relative alarm and + * for absolute depending on the flag. + */ + if (irq_on_late) { + counter_stm32_counter_stm32_set_cc_int_pending(dev, id); + } else { + DEV_CFG(dev)->ch_data[id].callback = NULL; + } + } else { + enable_it[id](timer); + } + + return err; +} + +static int counter_stm32_set_alarm(const struct device *dev, uint8_t chan, + const struct counter_alarm_cfg *alarm_cfg) +{ + const struct counter_stm32_config *config = DEV_CFG(dev); + struct counter_stm32_ch_data *chdata = &config->ch_data[chan]; + + if (alarm_cfg->ticks > counter_stm32_get_top_value(dev)) { + return -EINVAL; + } + + if (chdata->callback) { + return -EBUSY; + } + + chdata->callback = alarm_cfg->callback; + chdata->user_data = alarm_cfg->user_data; + + return counter_stm32_set_cc(dev, chan, alarm_cfg); +} + +static int counter_stm32_cancel_alarm(const struct device *dev, uint8_t chan) +{ + const struct counter_stm32_config *config = DEV_CFG(dev); + + disable_it[chan](config->timer); + config->ch_data[chan].callback = NULL; + + return 0; +} + +static int counter_stm32_set_top_value(const struct device *dev, + const struct counter_top_cfg *cfg) +{ + const struct counter_stm32_config *config = DEV_CFG(dev); + TIM_TypeDef *timer = config->timer; + struct counter_stm32_data *data = DEV_DATA(dev); + int err = 0; + + for (int i = 0; i < counter_get_num_of_channels(dev); i++) { + /* Overflow can be changed only when all alarms are + * disabled. + */ + if (config->ch_data[i].callback) { + return -EBUSY; + } + } + + LL_TIM_DisableIT_UPDATE(timer); + LL_TIM_SetAutoReload(timer, cfg->ticks); + LL_TIM_ClearFlag_UPDATE(timer); + + data->top_cb = cfg->callback; + data->top_user_data = cfg->user_data; + + if (!(cfg->flags & COUNTER_TOP_CFG_DONT_RESET)) { + LL_TIM_SetCounter(timer, 0); + } else if (counter_stm32_read(dev) >= cfg->ticks) { + err = -ETIME; + if (cfg->flags & COUNTER_TOP_CFG_RESET_WHEN_LATE) { + LL_TIM_SetCounter(timer, 0); + } + } + + if (cfg->callback) { + LL_TIM_EnableIT_UPDATE(timer); + } + + return err; +} + +static uint32_t counter_stm32_get_pending_int(const struct device *dev) +{ + const struct counter_stm32_config *cfg = DEV_CFG(dev); + uint32_t pending = 0; + + switch (counter_get_num_of_channels(dev)) { + case 4U: + pending |= LL_TIM_IsActiveFlag_CC4(cfg->timer); + case 3U: + pending |= LL_TIM_IsActiveFlag_CC3(cfg->timer); + case 2U: + pending |= LL_TIM_IsActiveFlag_CC2(cfg->timer); + case 1U: + pending |= LL_TIM_IsActiveFlag_CC1(cfg->timer); + } + + return !!pending; +} + +/** + * Obtain timer clock speed. + * + * @param pclken Timer clock control subsystem. + * @param tim_clk Where computed timer clock will be stored. + * + * @return 0 on success, error code otherwise. + * + * This function is ripped from the PWM driver; TODO handle code duplication. + */ +static int counter_stm32_get_tim_clk(const struct stm32_pclken *pclken, uint32_t *tim_clk) +{ + int r; + const struct device *clk; + uint32_t bus_clk, apb_psc; + + clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE); + + r = clock_control_get_rate(clk, (clock_control_subsys_t *)pclken, + &bus_clk); + if (r < 0) { + return r; + } + +#if defined(CONFIG_SOC_SERIES_STM32H7X) + if (pclken->bus == STM32_CLOCK_BUS_APB1) { + apb_psc = STM32_D2PPRE1; + } else { + apb_psc = STM32_D2PPRE2; + } +#else + if (pclken->bus == STM32_CLOCK_BUS_APB1) { + apb_psc = STM32_APB1_PRESCALER; + } +#if !defined(CONFIG_SOC_SERIES_STM32F0X) && !defined(CONFIG_SOC_SERIES_STM32G0X) + else { + apb_psc = STM32_APB2_PRESCALER; + } +#endif +#endif + +#if defined(RCC_DCKCFGR_TIMPRE) || defined(RCC_DCKCFGR1_TIMPRE) || \ + defined(RCC_CFGR_TIMPRE) + /* + * There are certain series (some F4, F7 and H7) that have the TIMPRE + * bit to control the clock frequency of all the timers connected to + * APB1 and APB2 domains. + * + * Up to a certain threshold value of APB{1,2} prescaler, timer clock + * equals to HCLK. This threshold value depends on TIMPRE setting + * (2 if TIMPRE=0, 4 if TIMPRE=1). Above threshold, timer clock is set + * to a multiple of the APB domain clock PCLK{1,2} (2 if TIMPRE=0, 4 if + * TIMPRE=1). + */ + + if (LL_RCC_GetTIMPrescaler() == LL_RCC_TIM_PRESCALER_TWICE) { + /* TIMPRE = 0 */ + if (apb_psc <= 2u) { + LL_RCC_ClocksTypeDef clocks; + + LL_RCC_GetSystemClocksFreq(&clocks); + *tim_clk = clocks.HCLK_Frequency; + } else { + *tim_clk = bus_clk * 2u; + } + } else { + /* TIMPRE = 1 */ + if (apb_psc <= 4u) { + LL_RCC_ClocksTypeDef clocks; + + LL_RCC_GetSystemClocksFreq(&clocks); + *tim_clk = clocks.HCLK_Frequency; + } else { + *tim_clk = bus_clk * 4u; + } + } +#else + /* + * If the APB prescaler equals 1, the timer clock frequencies + * are set to the same frequency as that of the APB domain. + * Otherwise, they are set to twice (×2) the frequency of the + * APB domain. + */ + if (apb_psc == 1u) { + *tim_clk = bus_clk; + } else { + *tim_clk = bus_clk * 2u; + } +#endif + + return 0; +} + +static int counter_stm32_init_timer(const struct device *dev) +{ + const struct counter_stm32_config *cfg = DEV_CFG(dev); + TIM_TypeDef *timer = cfg->timer; + LL_TIM_InitTypeDef init; + uint32_t tim_clk; + int r; + + /* initialize clock and check its speed */ + r = clock_control_on(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE), + (clock_control_subsys_t *)&cfg->pclken); + if (r < 0) { + LOG_ERR("Could not initialize clock (%d)", r); + return r; + } + r = counter_stm32_get_tim_clk(&cfg->pclken, &tim_clk); + if (r < 0) { + LOG_ERR("Could not obtain timer clock (%d)", r); + return r; + } + DEV_DATA(dev)->freq = tim_clk / (cfg->prescaler + 1U); + + /* config/enable IRQ */ + cfg->irq_config_func(dev); + + /* initialize timer */ + LL_TIM_StructInit(&init); + + init.Prescaler = cfg->prescaler; + init.CounterMode = LL_TIM_COUNTERMODE_UP; + init.Autoreload = counter_get_max_top_value(dev); + init.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; + + if (LL_TIM_Init(timer, &init) != SUCCESS) { + LOG_ERR("Could not initialize timer"); + return -EIO; + } + + return 0; +} + +static uint32_t counter_stm32_get_guard_period(const struct device *dev, uint32_t flags) +{ + ARG_UNUSED(flags); + return DEV_DATA(dev)->guard_period; +} + +static int counter_stm32_set_guard_period(const struct device *dev, uint32_t guard, + uint32_t flags) +{ + ARG_UNUSED(flags); + __ASSERT_NO_MSG(guard < counter_stm32_get_top_value(dev)); + + DEV_DATA(dev)->guard_period = guard; + return 0; +} + +static uint32_t counter_stm32_get_freq(const struct device *dev) +{ + return DEV_DATA(dev)->freq; +} + +static void counter_stm32_top_irq_handle(const struct device *dev) +{ + counter_top_callback_t cb = DEV_DATA(dev)->top_cb; + + __ASSERT(cb != NULL, "top event enabled - expecting callback"); + cb(dev, DEV_DATA(dev)->top_user_data); +} + +static void counter_stm32_alarm_irq_handle(const struct device *dev, uint32_t id) +{ + TIM_TypeDef *timer = DEV_CFG(dev)->timer; + + struct counter_stm32_ch_data *chdata; + counter_alarm_callback_t cb; + + atomic_and(&DEV_DATA(dev)->cc_int_pending, ~BIT(id)); + disable_it[id](timer); + + chdata = &DEV_CFG(dev)->ch_data[id]; + cb = chdata->callback; + chdata->callback = NULL; + + if (cb) { + uint32_t cc_val = get_timer_compare[id](timer); + + cb(dev, id, cc_val, chdata->user_data); + } +} + +static const struct counter_driver_api counter_stm32_driver_api = { + .start = counter_stm32_start, + .stop = counter_stm32_stop, + .get_value = counter_stm32_get_value, + .set_alarm = counter_stm32_set_alarm, + .cancel_alarm = counter_stm32_cancel_alarm, + .set_top_value = counter_stm32_set_top_value, + .get_pending_int = counter_stm32_get_pending_int, + .get_top_value = counter_stm32_get_top_value, + .get_guard_period = counter_stm32_get_guard_period, + .set_guard_period = counter_stm32_set_guard_period, + .get_freq = counter_stm32_get_freq, +}; + +#define TIM_IRQ_HANDLE_CC(timx, cc) \ + do { \ + bool hw_irq = LL_TIM_IsActiveFlag_CC##cc(timer) && \ + LL_TIM_IsEnabledIT_CC##cc(timer); \ + if (hw_irq || (DEV_DATA(dev)->cc_int_pending & BIT(cc - 1U))) { \ + if (hw_irq) { \ + LL_TIM_ClearFlag_CC##cc(timer); \ + } \ + counter_stm32_alarm_irq_handle(dev, cc - 1U); \ + } \ + } while (0) + +void counter_stm32_irq_handler(const struct device *dev) +{ + TIM_TypeDef *timer = DEV_CFG(dev)->timer; + + /* Capture compare events */ + switch (counter_get_num_of_channels(dev)) { + case 4U: + TIM_IRQ_HANDLE_CC(timer, 4); + case 3U: + TIM_IRQ_HANDLE_CC(timer, 3); + case 2U: + TIM_IRQ_HANDLE_CC(timer, 2); + case 1U: + TIM_IRQ_HANDLE_CC(timer, 1); + } + + /* TIM Update event */ + if (LL_TIM_IsActiveFlag_UPDATE(timer) && LL_TIM_IsEnabledIT_UPDATE(timer)) { + LL_TIM_ClearFlag_UPDATE(timer); + counter_stm32_top_irq_handle(dev); + } +} + +#define TIMER(idx) DT_PARENT(DT_DRV_INST(idx)) + +/** TIMx instance from DT */ +#define TIM(idx) ((TIM_TypeDef *)DT_REG_ADDR(TIMER(idx))) + +#define COUNTER_DEVICE_INIT(idx) \ + BUILD_ASSERT(DT_PROP(TIMER(idx), st_prescaler) <= 0xFFFF, \ + "TIMER prescaler out of range"); \ + BUILD_ASSERT(NUM_CH(TIM(idx)) <= TIMER_MAX_CH, \ + "TIMER too many channels"); \ + \ + static struct counter_stm32_data counter##idx##_data; \ + static struct counter_stm32_ch_data counter##idx##_ch_data[TIMER_MAX_CH]; \ + \ + static void counter_##idx##_stm32_irq_config(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_IRQN(TIMER(idx)), \ + DT_IRQ(TIMER(idx), priority), \ + counter_stm32_irq_handler, \ + DEVICE_DT_GET(DT_DRV_INST(idx)), \ + 0); \ + irq_enable(DT_IRQN(TIMER(idx))); \ + } \ + \ + static const struct counter_stm32_config counter##idx##_config = { \ + .info = { \ + .max_top_value = \ + IS_TIM_32B_COUNTER_INSTANCE(TIM(idx)) ? \ + 0xFFFFFFFF : 0x0000FFFF, \ + .flags = COUNTER_CONFIG_INFO_COUNT_UP, \ + .channels = NUM_CH(TIM(idx)), \ + }, \ + .ch_data = counter##idx##_ch_data, \ + .timer = TIM(idx), \ + .prescaler = DT_PROP(TIMER(idx), st_prescaler), \ + .pclken = { \ + .bus = DT_CLOCKS_CELL(TIMER(idx), bus), \ + .enr = DT_CLOCKS_CELL(TIMER(idx), bits) \ + }, \ + .irq_config_func = counter_##idx##_stm32_irq_config, \ + .irqn = DT_IRQN(TIMER(idx)), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(idx, \ + counter_stm32_init_timer, \ + NULL, \ + &counter##idx##_data, \ + &counter##idx##_config, \ + PRE_KERNEL_1, CONFIG_COUNTER_INIT_PRIORITY, \ + &counter_stm32_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(COUNTER_DEVICE_INIT) diff --git a/dts/arm/st/f4/stm32f4.dtsi b/dts/arm/st/f4/stm32f4.dtsi index 7dc6a18e31835..fbab3ff4fbc8f 100644 --- a/dts/arm/st/f4/stm32f4.dtsi +++ b/dts/arm/st/f4/stm32f4.dtsi @@ -320,6 +320,12 @@ label = "PWM_2"; #pwm-cells = <3>; }; + + counter { + compatible = "st,stm32-counter"; + status = "disabled"; + label = "COUNTER_2"; + }; }; timers3: timers@40000400 { @@ -338,6 +344,12 @@ label = "PWM_3"; #pwm-cells = <3>; }; + + counter { + compatible = "st,stm32-counter"; + status = "disabled"; + label = "COUNTER_3"; + }; }; timers4: timers@40000800 { @@ -356,6 +368,12 @@ label = "PWM_4"; #pwm-cells = <3>; }; + + counter { + compatible = "st,stm32-counter"; + status = "disabled"; + label = "COUNTER_4"; + }; }; timers5: timers@40000c00 { @@ -374,6 +392,12 @@ label = "PWM_5"; #pwm-cells = <3>; }; + + counter { + compatible = "st,stm32-counter"; + status = "disabled"; + label = "COUNTER_5"; + }; }; timers9: timers@40014000 { @@ -392,6 +416,12 @@ label = "PWM_9"; #pwm-cells = <3>; }; + + counter { + compatible = "st,stm32-counter"; + status = "disabled"; + label = "COUNTER_9"; + }; }; timers10: timers@40014400 { @@ -410,6 +440,12 @@ label = "PWM_10"; #pwm-cells = <3>; }; + + counter { + compatible = "st,stm32-counter"; + status = "disabled"; + label = "COUNTER_10"; + }; }; timers11: timers@40014800 { @@ -428,6 +464,12 @@ label = "PWM_11"; #pwm-cells = <3>; }; + + counter { + compatible = "st,stm32-counter"; + status = "disabled"; + label = "COUNTER_11"; + }; }; rtc: rtc@40002800 { diff --git a/dts/arm/st/f4/stm32f405.dtsi b/dts/arm/st/f4/stm32f405.dtsi index 035975fce8d2c..10335a01cda90 100644 --- a/dts/arm/st/f4/stm32f405.dtsi +++ b/dts/arm/st/f4/stm32f405.dtsi @@ -79,6 +79,12 @@ st,prescaler = <0>; status = "disabled"; label = "TIMERS_6"; + + counter { + compatible = "st,stm32-counter"; + status = "disabled"; + label = "COUNTER_6"; + }; }; timers7: timers@40001400 { @@ -90,6 +96,12 @@ st,prescaler = <0>; status = "disabled"; label = "TIMERS_7"; + + counter { + compatible = "st,stm32-counter"; + status = "disabled"; + label = "COUNTER_7"; + }; }; timers8: timers@40010400 { @@ -126,6 +138,12 @@ label = "PWM_12"; #pwm-cells = <3>; }; + + counter { + compatible = "st,stm32-counter"; + status = "disabled"; + label = "COUNTER_12"; + }; }; timers13: timers@40001c00 { @@ -144,6 +162,12 @@ label = "PWM_13"; #pwm-cells = <3>; }; + + counter { + compatible = "st,stm32-counter"; + status = "disabled"; + label = "COUNTER_13"; + }; }; timers14: timers@40002000 { @@ -162,6 +186,12 @@ label = "PWM_14"; #pwm-cells = <3>; }; + + counter { + compatible = "st,stm32-counter"; + status = "disabled"; + label = "COUNTER_14"; + }; }; usbotg_hs: usb@40040000 { diff --git a/dts/arm/st/f4/stm32f410.dtsi b/dts/arm/st/f4/stm32f410.dtsi index aeca24accf886..5ec6f224e0af0 100644 --- a/dts/arm/st/f4/stm32f410.dtsi +++ b/dts/arm/st/f4/stm32f410.dtsi @@ -81,6 +81,12 @@ st,prescaler = <0>; status = "disabled"; label = "TIMERS_6"; + + counter { + compatible = "st,stm32-counter"; + status = "disabled"; + label = "COUNTER_6"; + }; }; dac1: dac@40007400 { diff --git a/dts/arm/st/f4/stm32f412.dtsi b/dts/arm/st/f4/stm32f412.dtsi index 2a535c0c37686..9c6f1734b8c40 100644 --- a/dts/arm/st/f4/stm32f412.dtsi +++ b/dts/arm/st/f4/stm32f412.dtsi @@ -52,6 +52,12 @@ st,prescaler = <0>; status = "disabled"; label = "TIMERS_6"; + + counter { + compatible = "st,stm32-counter"; + status = "disabled"; + label = "COUNTER_6"; + }; }; timers7: timers@40001400 { @@ -63,6 +69,12 @@ st,prescaler = <0>; status = "disabled"; label = "TIMERS_7"; + + counter { + compatible = "st,stm32-counter"; + status = "disabled"; + label = "COUNTER_7"; + }; }; timers8: timers@40010400 { @@ -99,6 +111,12 @@ label = "PWM_12"; #pwm-cells = <3>; }; + + counter { + compatible = "st,stm32-counter"; + status = "disabled"; + label = "COUNTER_12"; + }; }; timers13: timers@40001c00 { @@ -117,6 +135,12 @@ label = "PWM_13"; #pwm-cells = <3>; }; + + counter { + compatible = "st,stm32-counter"; + status = "disabled"; + label = "COUNTER_13"; + }; }; timers14: timers@40002000 { @@ -135,6 +159,12 @@ label = "PWM_14"; #pwm-cells = <3>; }; + + counter { + compatible = "st,stm32-counter"; + status = "disabled"; + label = "COUNTER_14"; + }; }; rng: rng@50060800 { diff --git a/dts/bindings/counter/st,stm32-counter.yaml b/dts/bindings/counter/st,stm32-counter.yaml new file mode 100644 index 0000000000000..a7e9bfb4cd0a1 --- /dev/null +++ b/dts/bindings/counter/st,stm32-counter.yaml @@ -0,0 +1,12 @@ +# Copyright (c) 2021, Kent Hall +# SPDX-License-Identifier: Apache-2.0 + +description: STM32 counters + +compatible: "st,stm32-counter" + +include: base.yaml + +properties: + label: + required: true From cf04bdd74d72f714ebd7555e4f9bd2fa57aedf67 Mon Sep 17 00:00:00 2001 From: Kent Hall Date: Wed, 13 Oct 2021 15:14:13 -0400 Subject: [PATCH 2/2] tests: drivers: counter: Update tests for STM32F4 timer counter driver Updates counter driver test suite to support STM32F4 timers. Signed-off-by: Kent Hall --- .../boards/stm32f4_disco.overlay | 87 +++++++++++++++++++ .../counter_basic_api/src/test_counter.c | 17 ++++ 2 files changed, 104 insertions(+) create mode 100644 tests/drivers/counter/counter_basic_api/boards/stm32f4_disco.overlay diff --git a/tests/drivers/counter/counter_basic_api/boards/stm32f4_disco.overlay b/tests/drivers/counter/counter_basic_api/boards/stm32f4_disco.overlay new file mode 100644 index 0000000000000..b9715f0d8f610 --- /dev/null +++ b/tests/drivers/counter/counter_basic_api/boards/stm32f4_disco.overlay @@ -0,0 +1,87 @@ +&timers2 { + st,prescaler = <83>; + counter { + status = "okay"; + }; +}; + +&timers3 { + st,prescaler = <83>; + counter { + status = "okay"; + }; +}; + +&timers4 { + st,prescaler = <83>; + counter { + status = "okay"; + }; +}; + +&timers5 { + st,prescaler = <83>; + counter { + status = "okay"; + }; +}; + +&timers6 { + st,prescaler = <83>; + counter { + status = "okay"; + }; +}; + +&timers7 { + st,prescaler = <83>; + counter { + status = "okay"; + }; +}; + +&timers9 { + st,prescaler = <167>; + counter { + status = "okay"; + }; +}; + +&timers10 { + st,prescaler = <167>; + counter { + status = "okay"; + }; +}; + +&timers11 { + st,prescaler = <167>; + counter { + status = "okay"; + }; +}; + +&timers12 { + st,prescaler = <83>; + counter { + status = "okay"; + }; +}; + +&timers13 { + st,prescaler = <83>; + counter { + status = "okay"; + }; +}; + +&timers14 { + st,prescaler = <83>; + counter { + status = "okay"; + }; +}; + +&rtc { + status = "disabled"; +}; diff --git a/tests/drivers/counter/counter_basic_api/src/test_counter.c b/tests/drivers/counter/counter_basic_api/src/test_counter.c index 748a7127ef00c..588fab3f115c7 100644 --- a/tests/drivers/counter/counter_basic_api/src/test_counter.c +++ b/tests/drivers/counter/counter_basic_api/src/test_counter.c @@ -61,6 +61,14 @@ static const char * const devices[] = { #ifdef CONFIG_COUNTER_RTC2 DT_LABEL(DT_NODELABEL(rtc2)), #endif +#ifdef CONFIG_COUNTER_TIMER_STM32 +#define STM32_COUNTER_LABEL(idx) \ + DT_LABEL(DT_INST(idx, st_stm32_counter)), +#define DT_DRV_COMPAT st_stm32_counter + DT_INST_FOREACH_STATUS_OKAY(STM32_COUNTER_LABEL) +#undef DT_DRV_COMPAT +#undef STM32_COUNTER_LABEL +#endif #ifdef CONFIG_COUNTER_NATIVE_POSIX DT_LABEL(DT_NODELABEL(counter0)), #endif @@ -762,6 +770,10 @@ static bool late_detection_capable(const char *dev_name) return false; } + if (single_channel_alarm_capable(dev_name) == false) { + return false; + } + return true; } @@ -978,6 +990,11 @@ static bool reliable_cancel_capable(const char *dev_name) return true; } #endif +#ifdef CONFIG_COUNTER_TIMER_STM32 + if (single_channel_alarm_capable(dev_name)) { + return true; + } +#endif #ifdef CONFIG_COUNTER_NATIVE_POSIX if (strcmp(dev_name, DT_LABEL(DT_NODELABEL(counter0))) == 0) { return true;