Skip to content

drivers: counter: introduce counter capture api #89127

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

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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
8 changes: 8 additions & 0 deletions drivers/counter/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions drivers/counter/Kconfig.stm32_timer
Original file line number Diff line number Diff line change
Expand Up @@ -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.
2 changes: 1 addition & 1 deletion drivers/counter/counter_ite_it8xxx2.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
161 changes: 152 additions & 9 deletions drivers/counter/counter_ll_stm32_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <zephyr/drivers/reset.h>
#include <zephyr/irq.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/drivers/pinctrl.h>

#include <stm32_ll_tim.h>
#include <stm32_ll_rcc.h>
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
};

Expand All @@ -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);
};
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -540,6 +561,26 @@ 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 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;
Expand All @@ -550,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,
Expand All @@ -585,6 +717,13 @@ 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,
.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) \
Expand All @@ -595,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)

Expand Down Expand Up @@ -638,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]; \
\
Expand Down Expand Up @@ -669,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, \
Expand Down
2 changes: 1 addition & 1 deletion drivers/counter/counter_mcux_gpt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion drivers/counter/counter_xlnx_axi_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion dts/bindings/counter/st,stm32-counter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ description: STM32 counters

compatible: "st,stm32-counter"

include: base.yaml
include: [base.yaml, pinctrl-device.yaml]
Loading
Loading