From bb30acf886dfc57a165693da6db3b601b46ab2a3 Mon Sep 17 00:00:00 2001 From: Ryan McClelland Date: Sat, 26 Apr 2025 00:01:13 -0500 Subject: [PATCH 1/5] drivers: counter: stm32: add counter reset Add a reset counter api to set the time back to 0. Signed-off-by: Ryan McClelland --- drivers/counter/counter_ll_stm32_timer.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/counter/counter_ll_stm32_timer.c b/drivers/counter/counter_ll_stm32_timer.c index c7a6216ac6642..83c4054d50caf 100644 --- a/drivers/counter/counter_ll_stm32_timer.c +++ b/drivers/counter/counter_ll_stm32_timer.c @@ -540,6 +540,16 @@ static uint32_t counter_stm32_get_freq(const struct device *dev) return data->freq; } +static int counter_stm32_reset_timer(const struct device *dev) +{ + const struct counter_stm32_config *config = dev->config; + TIM_TypeDef *timer = config->timer; + + LL_TIM_SetCounter(timer, 0); + + return 0; +} + static void counter_stm32_top_irq_handle(const struct device *dev) { struct counter_stm32_data *data = dev->data; @@ -585,6 +595,7 @@ 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, + .reset = counter_stm32_reset_timer, }; #define TIM_IRQ_HANDLE_CC(timx, cc) \ From 169d21e3a48e252df0e2a8bbd7ae07235d694135 Mon Sep 17 00:00:00 2001 From: Ryan McClelland Date: Sat, 26 Apr 2025 00:05:20 -0500 Subject: [PATCH 2/5] drivers: counter: stm32: introduce counter set value api Introduce a counter set value api to set the ticks for 32b and 64b. Signed-off-by: Ryan McClelland --- drivers/counter/counter_ll_stm32_timer.c | 11 +++++ include/zephyr/drivers/counter.h | 52 ++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/drivers/counter/counter_ll_stm32_timer.c b/drivers/counter/counter_ll_stm32_timer.c index 83c4054d50caf..09a34caf9fe85 100644 --- a/drivers/counter/counter_ll_stm32_timer.c +++ b/drivers/counter/counter_ll_stm32_timer.c @@ -550,6 +550,16 @@ static int counter_stm32_reset_timer(const struct device *dev) return 0; } +static int counter_stm32_set_value(const struct device *dev, uint32_t ticks) +{ + const struct counter_stm32_config *config = dev->config; + TIM_TypeDef *timer = config->timer; + + LL_TIM_SetCounter(timer, ticks); + + return 0; +} + static void counter_stm32_top_irq_handle(const struct device *dev) { struct counter_stm32_data *data = dev->data; @@ -596,6 +606,7 @@ static DEVICE_API(counter, counter_stm32_driver_api) = { .set_guard_period = counter_stm32_set_guard_period, .get_freq = counter_stm32_get_freq, .reset = counter_stm32_reset_timer, + .set_value = counter_stm32_set_value, }; #define TIM_IRQ_HANDLE_CC(timx, cc) \ diff --git a/include/zephyr/drivers/counter.h b/include/zephyr/drivers/counter.h index d929c0d8332a0..e332dfc956f93 100644 --- a/include/zephyr/drivers/counter.h +++ b/include/zephyr/drivers/counter.h @@ -216,6 +216,10 @@ typedef int (*counter_api_get_value)(const struct device *dev, typedef int (*counter_api_get_value_64)(const struct device *dev, uint64_t *ticks); typedef int (*counter_api_reset)(const struct device *dev); +typedef int (*counter_api_set_value)(const struct device *dev, + uint32_t ticks); +typedef int (*counter_api_set_value_64)(const struct device *dev, + uint64_t ticks); typedef int (*counter_api_set_alarm)(const struct device *dev, uint8_t chan_id, const struct counter_alarm_cfg *alarm_cfg); @@ -238,6 +242,8 @@ __subsystem struct counter_driver_api { counter_api_get_value get_value; counter_api_get_value_64 get_value_64; counter_api_reset reset; + counter_api_set_value set_value; + counter_api_set_value_64 set_value_64; counter_api_set_alarm set_alarm; counter_api_cancel_alarm cancel_alarm; counter_api_set_top_value set_top_value; @@ -454,6 +460,52 @@ static inline int z_impl_counter_reset(const struct device *dev) return api->reset(dev); } +/** + * @brief Set current counter value. + * @param dev Pointer to the device structure for the driver instance. + * @param ticks Tick value to set + * + * @retval 0 If successful. + * @retval Negative error code on failure getting the counter value + */ +__syscall int counter_set_value(const struct device *dev, uint32_t ticks); + +static inline int z_impl_counter_set_value(const struct device *dev, + uint32_t ticks) +{ + const struct counter_driver_api *api = + (struct counter_driver_api *)dev->api; + + if (!api->set_value) { + return -ENOSYS; + } + + return api->set_value(dev, ticks); +} + +/** + * @brief Set current counter 64-bit value. + * @param dev Pointer to the device structure for the driver instance. + * @param ticks Tick value to set + * + * @retval 0 If successful. + * @retval Negative error code on failure getting the counter value + */ +__syscall int counter_set_value_64(const struct device *dev, uint64_t ticks); + +static inline int z_impl_counter_set_value_64(const struct device *dev, + uint64_t ticks) +{ + const struct counter_driver_api *api = + (struct counter_driver_api *)dev->api; + + if (!api->set_value_64) { + return -ENOSYS; + } + + return api->set_value_64(dev, ticks); +} + /** * @brief Set a single shot alarm on a channel. * From 2b1968c1ae3f260d584c9c20f28c33fceb1e42bd Mon Sep 17 00:00:00 2001 From: Ryan McClelland Date: Sat, 26 Apr 2025 00:15:24 -0500 Subject: [PATCH 3/5] drivers: counter: stm32: introduce counter capture api This introduces apis for input capture on the STM32. This is something agnostic enough that is seen on other vendors such as Atmel's SAM line and others. A use case for this would be time stamping a rising and/or falling edge where precision and lack of jitter is important. The only way to currently do this in zephyr is to set up a GPIO interrupt and then read the counter or kernel time... but this can have limitations of software such as the processor may have a lot going on and can be subject to interrupt latencies. Having a hardware 'latch' done of the rising and/or falling edge with respect to a timer can eliminate these imprecisions. How this is to be used, is that an application is to call `capture_callback_set` with the configuration of the channel number, flags (such as falling, rising, or both edges), and then a callback function. Then the `capture_enable` function is to be called to enable this. The callback has the timestamp of the edge in ticks and the edge that it captured (rising or falling). Signed-off-by: Ryan McClelland --- drivers/counter/Kconfig | 8 ++ drivers/counter/Kconfig.stm32_timer | 1 + drivers/counter/counter_ll_stm32_timer.c | 139 +++++++++++++++++++-- dts/bindings/counter/st,stm32-counter.yaml | 2 +- include/zephyr/drivers/counter.h | 117 +++++++++++++++++ 5 files changed, 257 insertions(+), 10 deletions(-) diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index 2cff7c5f51e06..e8c53ec4d1bf7 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 09a34caf9fe85..f8a0b428b9ec5 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,12 @@ 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 +103,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; }; @@ -109,6 +119,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); }; @@ -497,6 +510,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); @@ -570,28 +591,119 @@ 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; +#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; + + 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; + + 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)); - if (cb) { - uint32_t cc_val = get_timer_compare[id](timer); + 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; - cb(dev, id, cc_val, chdata->user_data); + 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, @@ -607,6 +719,11 @@ static DEVICE_API(counter, counter_stm32_driver_api) = { .get_freq = counter_stm32_get_freq, .reset = counter_stm32_reset_timer, .set_value = counter_stm32_set_value, +#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) \ @@ -617,7 +734,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) @@ -660,6 +777,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]; \ \ @@ -691,6 +810,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 e332dfc956f93..e8035c101c1d4 100644 --- a/include/zephyr/drivers/counter.h +++ b/include/zephyr/drivers/counter.h @@ -112,6 +112,35 @@ 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) + + /**@} */ + +typedef void (*counter_capture_cb_t)(const struct device *dev, uint8_t chan, + uint32_t flags, uint64_t ticks, + void *user_data); + /** @brief Alarm callback * * @param dev Pointer to the device structure for the driver instance. @@ -235,6 +264,15 @@ 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, + uint32_t flags, + counter_capture_cb_t cb, + void *user_data); +typedef int (*counter_api_capture_enable)(const struct device *dev, + uint8_t chan); +typedef int (*counter_api_capture_disable)(const struct device *dev, + uint8_t chan); __subsystem struct counter_driver_api { counter_api_start start; @@ -252,6 +290,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 }; /** @@ -308,6 +351,80 @@ 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__) +__syscall int counter_capture_callback_set(const struct device *dev, + uint8_t chan, + 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, + 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) { + return -ENOTSUP; + } + + return api->capture_callback_set(dev, chan, flags, cb, user_data); +} + +/** + * @brief Enable capture on a channel. + * + * @param dev Pointer to the device structure for the driver instance. + * @param chan Channel ID. + * + * @retval 0 If successful. + * @retval Negative error code on failure + */ +__syscall int counter_capture_enable(const struct device *dev, + uint8_t chan); + +static inline int z_impl_counter_capture_enable(const struct device *dev, + uint8_t chan) +{ + const struct counter_driver_api *api = + (struct counter_driver_api *)dev->api; + + if (!api->capture_enable) { + return -ENOTSUP; + } + + return api->capture_enable(dev, chan); +} + +/** + * @brief Disable capture on a channel. + * + * @param dev Pointer to the device structure for the driver instance. + * @param chan Channel ID. + * + * @retval 0 If successful. + * @retval Negative error code on failure + */ +__syscall int counter_capture_disable(const struct device *dev, + uint8_t chan); + +static inline int z_impl_counter_capture_disable(const struct device *dev, + uint8_t chan) +{ + const struct counter_driver_api *api = + (struct counter_driver_api *)dev->api; + + if (!api->capture_disable) { + return -ENOTSUP; + } + + return api->capture_disable(dev, chan); +} +#endif + /** * @brief Function to convert microseconds to ticks. From d4e76b98cd9e6026c78b007a90411bd294697b38 Mon Sep 17 00:00:00 2001 From: Ryan McClelland Date: Sat, 26 Apr 2025 00:24:04 -0500 Subject: [PATCH 4/5] drivers: counter: change freq to 64 bits Some counters, such as those that use fractional adders, tick at resolutions greater than 4294967295Hz. This changes the freq in the common info to a uint64_t. This retains the existing `counter_get_freq` function which will still return a 32bit value. This also adds a `counter_get_freq_64` function to get a 64bit value for the frequency. Signed-off-by: Ryan McClelland --- drivers/counter/counter_ite_it8xxx2.c | 2 +- drivers/counter/counter_mcux_gpt.c | 2 +- drivers/counter/counter_xlnx_axi_timer.c | 2 +- include/zephyr/drivers/counter.h | 35 ++++++++++++++++++++++-- 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/drivers/counter/counter_ite_it8xxx2.c b/drivers/counter/counter_ite_it8xxx2.c index cbd34a605ffbc..a038c38068a92 100644 --- a/drivers/counter/counter_ite_it8xxx2.c +++ b/drivers/counter/counter_ite_it8xxx2.c @@ -275,7 +275,7 @@ static int counter_it8xxx2_init(const struct device *dev) const struct counter_it8xxx2_config *config = dev->config; LOG_DBG("max top value = 0x%08x", config->info.max_top_value); - LOG_DBG("frequency = %d", config->info.freq); + LOG_DBG("frequency = %llu", config->info.freq); LOG_DBG("channels = %d", config->info.channels); /* set the top value of top timer */ diff --git a/drivers/counter/counter_mcux_gpt.c b/drivers/counter/counter_mcux_gpt.c index c0e714ea41dc1..81ec115fd51cd 100644 --- a/drivers/counter/counter_mcux_gpt.c +++ b/drivers/counter/counter_mcux_gpt.c @@ -199,7 +199,7 @@ static int mcux_gpt_init(const struct device *dev) /* Adjust divider to match expected freq */ if (clock_freq % config->info.freq) { - LOG_ERR("Cannot Adjust GPT freq to %u\n", config->info.freq); + LOG_ERR("Cannot Adjust GPT freq to %llu\n", config->info.freq); LOG_ERR("clock src is %u\n", clock_freq); return -EINVAL; } diff --git a/drivers/counter/counter_xlnx_axi_timer.c b/drivers/counter/counter_xlnx_axi_timer.c index 9cb837ec73498..1a8c1e3f9db39 100644 --- a/drivers/counter/counter_xlnx_axi_timer.c +++ b/drivers/counter/counter_xlnx_axi_timer.c @@ -294,7 +294,7 @@ static int xlnx_axi_timer_init(const struct device *dev) const struct xlnx_axi_timer_config *config = dev->config; LOG_DBG("max top value = 0x%08x", config->info.max_top_value); - LOG_DBG("frequency = %d", config->info.freq); + LOG_DBG("frequency = %llu", config->info.freq); LOG_DBG("channels = %d", config->info.channels); xlnx_axi_timer_write32(dev, config->info.max_top_value, TLR0_OFFSET); diff --git a/include/zephyr/drivers/counter.h b/include/zephyr/drivers/counter.h index e8035c101c1d4..416d08d53d9bd 100644 --- a/include/zephyr/drivers/counter.h +++ b/include/zephyr/drivers/counter.h @@ -225,7 +225,7 @@ struct counter_config_info { /** * Frequency of the source clock if synchronous events are counted. */ - uint32_t freq; + uint64_t freq; /** * Flags (see @ref COUNTER_FLAGS). */ @@ -264,6 +264,7 @@ 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 uint64_t (*counter_api_get_freq_64)(const struct device *dev); typedef int (*counter_api_capture_callback_set)(const struct device *dev, uint8_t chan, uint32_t flags, @@ -290,6 +291,7 @@ __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; + counter_api_get_freq get_freq_64; #if defined(CONFIG_COUNTER_CAPTURE) || defined(__DOXYGEN__) counter_api_capture_callback_set capture_callback_set; counter_api_capture_enable capture_enable; @@ -349,8 +351,37 @@ static inline uint32_t z_impl_counter_get_frequency(const struct device *dev) const struct counter_driver_api *api = (struct counter_driver_api *)dev->api; - return api->get_freq ? api->get_freq(dev) : config->freq; + if (config->freq) { + return config->freq > UINT32_MAX ? UINT32_MAX : (uint32_t)config->freq; + } else { + return api->get_freq(dev); + } } + +/** + * @brief Function to get counter frequency in 64bits. + * + * @param[in] dev Pointer to the device structure for the driver instance. + * + * @return Frequency of the counter in Hz, or zero if the counter does + * not have a fixed frequency. + */ +__syscall uint64_t counter_get_frequency_64(const struct device *dev); + +static inline uint64_t z_impl_counter_get_frequency_64(const struct device *dev) +{ + const struct counter_config_info *config = + (const struct counter_config_info *)dev->config; + const struct counter_driver_api *api = + (struct counter_driver_api *)dev->api; + + if (config->freq) { + return config->freq; + } else { + return api->get_freq_64 ? api->get_freq_64(dev) : -ENOTSUP; + } +} + #if defined(CONFIG_COUNTER_CAPTURE) || defined(__DOXYGEN__) __syscall int counter_capture_callback_set(const struct device *dev, uint8_t chan, From 8b678d7853eb2a486f88114ea1b26c5d4a91475d Mon Sep 17 00:00:00 2001 From: Ryan McClelland Date: Sat, 26 Apr 2025 00:30:29 -0500 Subject: [PATCH 5/5] drivers: counter: add inline function for ns/us to ticks Add inline helper functions for converting ticks to nanoseconds. Also implement an inline helper for converting microseconds or nanoseconds with a 64bit tick value. Signed-off-by: Ryan McClelland --- include/zephyr/drivers/counter.h | 86 +++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 2 deletions(-) diff --git a/include/zephyr/drivers/counter.h b/include/zephyr/drivers/counter.h index 416d08d53d9bd..71a37725d225f 100644 --- a/include/zephyr/drivers/counter.h +++ b/include/zephyr/drivers/counter.h @@ -468,13 +468,63 @@ static inline int z_impl_counter_capture_disable(const struct device *dev, __syscall uint32_t counter_us_to_ticks(const struct device *dev, uint64_t us); static inline uint32_t z_impl_counter_us_to_ticks(const struct device *dev, - uint64_t us) + uint64_t us) { uint64_t ticks = (us * z_impl_counter_get_frequency(dev)) / USEC_PER_SEC; return (ticks > (uint64_t)UINT32_MAX) ? UINT32_MAX : ticks; } +/** + * @brief Function to convert microseconds to ticks with 64 bits. + * + * @param[in] dev Pointer to the device structure for the driver instance. + * @param[in] us Microseconds. + * + * @return Converted ticks. + */ +__syscall uint64_t counter_us_to_ticks_64(const struct device *dev, uint64_t us); + +static inline uint64_t z_impl_counter_us_to_ticks_64(const struct device *dev, + uint64_t us) +{ + return (us * z_impl_counter_get_frequency(dev)) / USEC_PER_SEC; +} + +/** + * @brief Function to convert nanoseconds to ticks. + * + * @param[in] dev Pointer to the device structure for the driver instance. + * @param[in] ns Nanoseconds. + * + * @return Converted ticks. Ticks will be saturated if exceed 32 bits. + */ +__syscall uint32_t counter_ns_to_ticks(const struct device *dev, uint64_t ns); + +static inline uint32_t z_impl_counter_ns_to_ticks(const struct device *dev, + uint64_t ns) +{ + uint64_t ticks = (ns * z_impl_counter_get_frequency(dev)) / NSEC_PER_SEC; + + return (ticks > (uint64_t)UINT32_MAX) ? UINT32_MAX : ticks; +} + +/** + * @brief Function to convert nanoseconds to ticks with 64 bits. + * + * @param[in] dev Pointer to the device structure for the driver instance. + * @param[in] ns Nanoseconds. + * + * @return Converted ticks. + */ +__syscall uint64_t counter_ns_to_ticks_64(const struct device *dev, uint64_t ns); + +static inline uint64_t z_impl_counter_ns_to_ticks_64(const struct device *dev, + uint64_t ns) +{ + return (ns * z_impl_counter_get_frequency(dev)) / NSEC_PER_SEC; +} + /** * @brief Function to convert ticks to microseconds. * @@ -486,11 +536,43 @@ static inline uint32_t z_impl_counter_us_to_ticks(const struct device *dev, __syscall uint64_t counter_ticks_to_us(const struct device *dev, uint32_t ticks); static inline uint64_t z_impl_counter_ticks_to_us(const struct device *dev, - uint32_t ticks) + uint32_t ticks) { return ((uint64_t)ticks * USEC_PER_SEC) / z_impl_counter_get_frequency(dev); } +/** + * @brief Function to convert ticks to nanoseconds. + * + * @param[in] dev Pointer to the device structure for the driver instance. + * @param[in] ticks Ticks. + * + * @return Converted nanoseconds. + */ +__syscall uint64_t counter_ticks_to_ns(const struct device *dev, uint32_t ticks); + +static inline uint64_t z_impl_counter_ticks_to_ns(const struct device *dev, + uint32_t ticks) +{ + return ((uint64_t)ticks * NSEC_PER_SEC) / z_impl_counter_get_frequency(dev); +} + +/** + * @brief Function to convert ticks to nanoseconds. + * + * @param[in] dev Pointer to the device structure for the driver instance. + * @param[in] ticks Ticks. + * + * @return Converted nanoseconds. + */ +__syscall uint64_t counter_ticks_to_ns_64(const struct device *dev, uint64_t ticks); + +static inline uint64_t z_impl_counter_ticks_to_ns_64(const struct device *dev, + uint64_t ticks) +{ + return (ticks * NSEC_PER_SEC) / z_impl_counter_get_frequency(dev); +} + /** * @brief Function to retrieve maximum top value that can be set. *