diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index b8a8962f0fb83..84daf6a3151b9 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -10,6 +10,14 @@ menuconfig COUNTER if COUNTER +config COUNTER_CAPTURE + bool "Counter capture" + help + Enable support for counter capture. This is a feature that allows + hardware timestamping of external events as well as a software + interrupt on the event. This feature is not supported by all + counter drivers. + config COUNTER_INIT_PRIORITY int "Counter init priority" default 60 diff --git a/drivers/counter/Kconfig.stm32_timer b/drivers/counter/Kconfig.stm32_timer index a4539911f091a..d26360775c64e 100644 --- a/drivers/counter/Kconfig.stm32_timer +++ b/drivers/counter/Kconfig.stm32_timer @@ -7,5 +7,6 @@ config COUNTER_TIMER_STM32 depends on DT_HAS_ST_STM32_COUNTER_ENABLED select USE_STM32_LL_TIM select RESET + select PINCTRL if COUNTER_CAPTURE 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 index 3a25b73132ebf..4b5146190f04f 100644 --- a/drivers/counter/counter_ll_stm32_timer.c +++ b/drivers/counter/counter_ll_stm32_timer.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -53,6 +54,14 @@ static uint32_t(*const get_timer_compare[TIMER_MAX_CH])(TIM_TypeDef *) = { LL_TIM_OC_GetCompareCH3, LL_TIM_OC_GetCompareCH4, }; #endif + +#ifdef CONFIG_COUNTER_CAPTURE +static uint32_t (*const get_timer_capture[TIMER_MAX_CH])(const TIM_TypeDef *) = { + LL_TIM_IC_GetCaptureCH1, LL_TIM_IC_GetCaptureCH2, + LL_TIM_IC_GetCaptureCH3, LL_TIM_IC_GetCaptureCH4, +}; +#endif /* CONFIG_COUNTER_CAPTURE */ + /** Channel to interrupt enable function mapping. */ static void(*const enable_it[TIMER_MAX_CH])(TIM_TypeDef *) = { LL_TIM_EnableIT_CC1, LL_TIM_EnableIT_CC2, @@ -96,6 +105,9 @@ struct counter_stm32_data { struct counter_stm32_ch_data { counter_alarm_callback_t callback; +#ifdef CONFIG_COUNTER_CAPTURE + counter_capture_cb_t capture; +#endif /* CONFIG_COUNTER_CAPTURE */ void *user_data; }; @@ -110,6 +122,9 @@ struct counter_stm32_config { uint32_t irqn; /* Reset controller device configuration */ const struct reset_dt_spec reset; +#ifdef CONFIG_COUNTER_CAPTURE + const struct pinctrl_dev_config *pcfg; +#endif /* CONFIG_COUNTER_CAPTURE */ LOG_INSTANCE_PTR_DECLARE(log); }; @@ -412,6 +427,14 @@ static int counter_stm32_init_timer(const struct device *dev) /* config/enable IRQ */ cfg->irq_config_func(dev); +#ifdef CONFIG_COUNTER_CAPTURE + r = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); + if (r < 0) { + LOG_ERR("%s: Counter Capture pinctrl setup failed (%d)", dev->name, r); + return r; + } +#endif /* CONFIG_COUNTER_CAPTURE */ + /* initialize timer */ LL_TIM_StructInit(&init); @@ -465,29 +488,121 @@ static void counter_stm32_top_irq_handle(const struct device *dev) cb(dev, data->top_user_data); } -static void counter_stm32_alarm_irq_handle(const struct device *dev, uint32_t id) +static void counter_stm32_irq_handle(const struct device *dev, uint32_t id) { const struct counter_stm32_config *config = dev->config; struct counter_stm32_data *data = dev->data; TIM_TypeDef *timer = config->timer; struct counter_stm32_ch_data *chdata; - counter_alarm_callback_t cb; atomic_and(&data->cc_int_pending, ~BIT(id)); - disable_it[id](timer); chdata = &config->ch_data[id]; - cb = chdata->callback; - chdata->callback = NULL; - if (cb) { - uint32_t cc_val = get_timer_compare[id](timer); +#ifdef CONFIG_COUNTER_CAPTURE + /* With Counter Capture, we need to check which mode it was configured for */ + if (LL_TIM_IC_GetActiveInput(timer, LL_TIM_CHANNEL_CH1 << (4 * id)) == + LL_TIM_ACTIVEINPUT_DIRECTTI) { + counter_capture_cb_t cb = chdata->capture; + + /* TODO: check overcapture flag, then print error? + * CC1OF is also set if at least two consecutive captures + * occurred whereas the flag was not cleared + */ + + if (cb) { + uint32_t cc_val = get_timer_capture[id](timer); + uint32_t pol = LL_TIM_IC_GetPolarity(timer, LL_TIM_CHANNEL_CH1 << (4 * id)); + uint32_t flags = 0; + + /* translate stm32 flags to zephyr flags */ + if (pol == LL_TIM_IC_POLARITY_RISING) { + flags = COUNTER_CAPTURE_RISING_EDGE; + } else if (pol == LL_TIM_IC_POLARITY_FALLING) { + flags = COUNTER_CAPTURE_FALLING_EDGE; + } else if (pol == LL_TIM_IC_POLARITY_BOTHEDGE) { + flags = COUNTER_CAPTURE_BOTH_EDGES; + } + + cb(dev, id, flags, cc_val, chdata->user_data); + } + } else { +#endif + counter_alarm_callback_t cb; + + /* Alarm is One-Shot, so disable the Interrupt */ + disable_it[id](timer); + + cb = chdata->callback; + chdata->callback = NULL; - cb(dev, id, cc_val, chdata->user_data); + if (cb) { + uint32_t cc_val = get_timer_compare[id](timer); + + cb(dev, id, cc_val, chdata->user_data); + } +#ifdef CONFIG_COUNTER_CAPTURE } +#endif +} + +#ifdef CONFIG_COUNTER_CAPTURE +static int counter_stm32_capture_enable(const struct device *dev, uint8_t chan) +{ + const struct counter_stm32_config *config = dev->config; + TIM_TypeDef *timer = config->timer; + + LL_TIM_CC_EnableChannel(timer, LL_TIM_CHANNEL_CH1 << (4 * chan)); + + return 0; } +static int counter_stm32_capture_disable(const struct device *dev, uint8_t chan) +{ + const struct counter_stm32_config *config = dev->config; + TIM_TypeDef *timer = config->timer; + + LL_TIM_CC_DisableChannel(timer, LL_TIM_CHANNEL_CH1 << (4 * chan)); + + return 0; +} + +static int counter_stm32_capture_callback_set(const struct device *dev, uint8_t chan, + uint32_t flags, counter_capture_cb_t cb, + void *user_data) +{ + const struct counter_stm32_config *config = dev->config; + struct counter_stm32_ch_data *chdata = &config->ch_data[chan]; + TIM_TypeDef *timer = config->timer; + uint32_t config_flags = LL_TIM_ACTIVEINPUT_DIRECTTI; + + chdata->capture = cb; + chdata->user_data = user_data; + + if ((flags & COUNTER_CAPTURE_BOTH_EDGES) == COUNTER_CAPTURE_BOTH_EDGES) { + config_flags |= LL_TIM_IC_POLARITY_BOTHEDGE; + } else if (flags & COUNTER_CAPTURE_FALLING_EDGE) { + config_flags |= LL_TIM_IC_POLARITY_FALLING; + } else if (flags & COUNTER_CAPTURE_RISING_EDGE) { + config_flags |= LL_TIM_IC_POLARITY_RISING; + } + /* TODO: add 'vendor' config flags for filter, prescaler div */ + config_flags |= LL_TIM_ICPSC_DIV1 | LL_TIM_IC_FILTER_FDIV1; + + /* Config is only writable when it is off */ + LL_TIM_CC_DisableChannel(timer, LL_TIM_CHANNEL_CH1 << (4 * chan)); + + LL_TIM_IC_Config(timer, LL_TIM_CHANNEL_CH1 << (4 * chan), config_flags); + + LL_TIM_CC_EnableChannel(timer, LL_TIM_CHANNEL_CH1 << (4 * chan)); + /* enable interrupt */ + enable_it[chan](timer); + + return 0; +} +#endif + static DEVICE_API(counter, counter_stm32_driver_api) = { .start = counter_stm32_start, .stop = counter_stm32_stop, @@ -501,6 +616,11 @@ static DEVICE_API(counter, counter_stm32_driver_api) = { .get_guard_period = counter_stm32_get_guard_period, .set_guard_period = counter_stm32_set_guard_period, .get_freq = counter_stm32_get_freq, +#ifdef CONFIG_COUNTER_CAPTURE + .capture_enable = counter_stm32_capture_enable, + .capture_disable = counter_stm32_capture_disable, + .capture_callback_set = counter_stm32_capture_callback_set, +#endif }; #define TIM_IRQ_HANDLE_CC(timx, cc) \ @@ -511,7 +631,7 @@ static DEVICE_API(counter, counter_stm32_driver_api) = { if (hw_irq) { \ LL_TIM_ClearFlag_CC##cc(timer); \ } \ - counter_stm32_alarm_irq_handle(dev, cc - 1U); \ + counter_stm32_irq_handle(dev, cc - 1U); \ } \ } while (0) @@ -562,6 +682,8 @@ void counter_stm32_irq_handler(const struct device *dev) BUILD_ASSERT(NUM_CH(TIM(idx)) <= TIMER_MAX_CH, \ "TIMER too many channels"); \ \ + IF_ENABLED(CONFIG_COUNTER_CAPTURE, (PINCTRL_DT_INST_DEFINE(idx);)) \ + \ static struct counter_stm32_data counter##idx##_data; \ static struct counter_stm32_ch_data counter##idx##_ch_data[TIMER_MAX_CH]; \ \ @@ -593,6 +715,8 @@ void counter_stm32_irq_handler(const struct device *dev) .irq_config_func = counter_##idx##_stm32_irq_config, \ .irqn = DT_IRQN(TIMER(idx)), \ .reset = RESET_DT_SPEC_GET(TIMER(idx)), \ + IF_ENABLED(CONFIG_COUNTER_CAPTURE, \ + (.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx),)) \ }; \ \ DEVICE_DT_INST_DEFINE(idx, \ diff --git a/dts/bindings/counter/st,stm32-counter.yaml b/dts/bindings/counter/st,stm32-counter.yaml index 74ab8e220c59c..cd5104a094174 100644 --- a/dts/bindings/counter/st,stm32-counter.yaml +++ b/dts/bindings/counter/st,stm32-counter.yaml @@ -5,4 +5,4 @@ description: STM32 counters compatible: "st,stm32-counter" -include: base.yaml +include: [base.yaml, pinctrl-device.yaml] diff --git a/include/zephyr/drivers/counter.h b/include/zephyr/drivers/counter.h index f8d19bc69dafa..acb087ccd2cc6 100644 --- a/include/zephyr/drivers/counter.h +++ b/include/zephyr/drivers/counter.h @@ -113,6 +113,43 @@ extern "C" { /**@} */ +/** + * @anchor COUNTER_CAPTURE_FLAGS + * @name Counter capture flags + * + * @brief Used by @ref counter_capture_callback_set. + * @{ + */ + +/** + * @brief Capture rising edge of an external input signal + */ + #define COUNTER_CAPTURE_RISING_EDGE BIT(0) + +/** + * @brief Capture falling edge of an external input signal + */ + #define COUNTER_CAPTURE_FALLING_EDGE BIT(1) + +/** + * @brief Capture both falling and rising edge of an external input signal + */ + #define COUNTER_CAPTURE_BOTH_EDGES (COUNTER_CAPTURE_FALLING_EDGE | COUNTER_CAPTURE_RISING_EDGE) + + /**@} */ + +/** @brief Counter capture callback + * + * @param dev Pointer to the device structure for the driver instance + * @param chan_id Channel ID + * @param flags Configuration flags (@ref COUNTER_CAPTURE_FLAGS) + * @param ticks Counter value that triggered the capture + * @param user_data User data + */ +typedef void (*counter_capture_cb_t)(const struct device *dev, uint8_t chan_id, + uint32_t flags, uint64_t ticks, + void *user_data); + /** @brief Alarm callback * * @param dev Pointer to the device structure for the driver instance. @@ -232,6 +269,13 @@ typedef int (*counter_api_set_guard_period)(const struct device *dev, uint32_t ticks, uint32_t flags); typedef uint32_t (*counter_api_get_freq)(const struct device *dev); +typedef int (*counter_api_capture_callback_set)(const struct device *dev, + uint8_t chan_id, + uint32_t flags, + counter_capture_cb_t cb, + void *user_data); +typedef int (*counter_api_capture_enable)(const struct device *dev, uint8_t chan_id); +typedef int (*counter_api_capture_disable)(const struct device *dev, uint8_t chan_id); __subsystem struct counter_driver_api { counter_api_start start; @@ -247,6 +291,11 @@ __subsystem struct counter_driver_api { counter_api_get_guard_period get_guard_period; counter_api_set_guard_period set_guard_period; counter_api_get_freq get_freq; +#if defined(CONFIG_COUNTER_CAPTURE) || defined(__DOXYGEN__) + counter_api_capture_callback_set capture_callback_set; + counter_api_capture_enable capture_enable; + counter_api_capture_disable capture_disable; +#endif }; /** @@ -304,6 +353,92 @@ static inline uint32_t z_impl_counter_get_frequency(const struct device *dev) return api->get_freq ? api->get_freq(dev) : config->freq; } +#if defined(CONFIG_COUNTER_CAPTURE) || defined(__DOXYGEN__) +/** + * @brief Set callback function for a counter timestamp capture + * + * @param dev Pointer to the device structure for the driver instance + * @param chan_id Channel ID + * @param flags Configuration flags (@ref COUNTER_CAPTURE_FLAGS) + * @param cb Callback function reference + * @param user_data Argument passed to the callback function + * + * @retval 0 If successful. + * @retval Negative error code on failure + */ +__syscall int counter_capture_callback_set(const struct device *dev, + uint8_t chan_id, + uint32_t flags, + counter_capture_cb_t cb, + void *user_data); + +static inline int z_impl_counter_capture_callback_set(const struct device *dev, + uint8_t chan_id, + uint32_t flags, + counter_capture_cb_t cb, + void *user_data) +{ + const struct counter_driver_api *api = + (struct counter_driver_api *)dev->api; + + if (api->capture_callback_set == NULL) { + return -ENOTSUP; + } + + return api->capture_callback_set(dev, chan_id, flags, cb, user_data); +} + +/** + * @brief Enable capture on a channel. + * + * @param dev Pointer to the device structure for the driver instance. + * @param chan_id Channel ID. + * + * @retval 0 If successful. + * @retval Negative error code on failure + */ +__syscall int counter_capture_enable(const struct device *dev, + uint8_t chan_id); + +static inline int z_impl_counter_capture_enable(const struct device *dev, + uint8_t chan_id) +{ + const struct counter_driver_api *api = + (struct counter_driver_api *)dev->api; + + if (!api->capture_enable) { + return -ENOTSUP; + } + + return api->capture_enable(dev, chan_id); +} + +/** + * @brief Disable capture on a channel. + * + * @param dev Pointer to the device structure for the driver instance. + * @param chan_id Channel ID. + * + * @retval 0 If successful. + * @retval Negative error code on failure + */ +__syscall int counter_capture_disable(const struct device *dev, + uint8_t chan_id); + +static inline int z_impl_counter_capture_disable(const struct device *dev, + uint8_t chan_id) +{ + const struct counter_driver_api *api = + (struct counter_driver_api *)dev->api; + + if (!api->capture_disable) { + return -ENOTSUP; + } + + return api->capture_disable(dev, chan_id); +} +#endif + /** * @brief Function to convert microseconds to ticks. *