From 7e02a0379f73f8948ea9e10fe30f4ca9bc8f8a3c Mon Sep 17 00:00:00 2001 From: Jason Wright Date: Thu, 12 Oct 2023 22:47:18 -0400 Subject: [PATCH] drivers: counter native: Add top value conf and multi channel support The counter_native_posix driver currently does not support top value configuration, i.e. `ctr_set_top_value` returns `-ENOTSUP`. This commit adds support for top value configuration, and with the counter API now fully implemented, adds `counter` to `supported` peripherals for native_posix target. It also resolves an existing bug in which the counter ISR did not reset upon reaching `TOP_VALUE`. And adds support for multiple channels Signed-off-by: Jason Wright Signed-off-by: Alberto Escolar Piedras --- boards/posix/native_posix/hw_counter.c | 27 +++- boards/posix/native_posix/hw_counter.h | 3 + boards/posix/native_posix/native_posix.yaml | 1 + .../posix/native_posix/native_posix_64.yaml | 1 + boards/posix/native_sim/native_sim.yaml | 1 + boards/posix/native_sim/native_sim_64.yaml | 1 + drivers/counter/Kconfig.native_posix | 5 + drivers/counter/counter_native_posix.c | 145 ++++++++++++++---- 8 files changed, 157 insertions(+), 27 deletions(-) diff --git a/boards/posix/native_posix/hw_counter.c b/boards/posix/native_posix/hw_counter.c index 77218a7ada4..eb02fd00807 100644 --- a/boards/posix/native_posix/hw_counter.c +++ b/boards/posix/native_posix/hw_counter.c @@ -17,6 +17,7 @@ static bool counter_running; static uint64_t counter_value; static uint64_t counter_target; static uint64_t counter_period; +static uint64_t counter_wrap; /** * Initialize the counter with prescaler of HW @@ -28,6 +29,7 @@ void hw_counter_init(void) counter_value = 0; counter_running = false; counter_period = NEVER; + counter_wrap = NEVER; } void hw_counter_triggered(void) @@ -38,7 +40,7 @@ void hw_counter_triggered(void) } hw_counter_timer = hwm_get_time() + counter_period; - counter_value = counter_value + 1; + counter_value = (counter_value + 1) % counter_wrap; if (counter_value == counter_target) { hw_irq_ctrl_set_irq(COUNTER_EVENT_IRQ); @@ -54,6 +56,16 @@ void hw_counter_set_period(uint64_t period) counter_period = period; } +/* + * Set the count value at which the counter will wrap + * The counter will count up to (counter_wrap-1), i.e.: + * 0, 1, 2,.., (counter_wrap - 1), 0 + */ +void hw_counter_set_wrap_value(uint64_t wrap_value) +{ + counter_wrap = wrap_value; +} + /** * Starts the counter. It must be previously configured with * hw_counter_set_period() and hw_counter_set_target(). @@ -82,6 +94,11 @@ void hw_counter_stop(void) hwm_find_next_timer(); } +bool hw_counter_is_started(void) +{ + return counter_running; +} + /** * Returns the current counter value. */ @@ -90,6 +107,14 @@ uint64_t hw_counter_get_value(void) return counter_value; } +/** + * Resets the counter value. + */ +void hw_counter_reset(void) +{ + counter_value = 0; +} + /** * Configures the counter to generate an interrupt * when its count value reaches target. diff --git a/boards/posix/native_posix/hw_counter.h b/boards/posix/native_posix/hw_counter.h index 5cd6dd2e577..efbb7e39e29 100644 --- a/boards/posix/native_posix/hw_counter.h +++ b/boards/posix/native_posix/hw_counter.h @@ -18,9 +18,12 @@ void hw_counter_triggered(void); void hw_counter_set_period(uint64_t period); void hw_counter_set_target(uint64_t counter_target); +void hw_counter_set_wrap_value(uint64_t wrap_value); void hw_counter_start(void); void hw_counter_stop(void); +bool hw_counter_is_started(void); uint64_t hw_counter_get_value(void); +void hw_counter_reset(void); #ifdef __cplusplus } diff --git a/boards/posix/native_posix/native_posix.yaml b/boards/posix/native_posix/native_posix.yaml index 7d10c080474..a1fb897eb75 100644 --- a/boards/posix/native_posix/native_posix.yaml +++ b/boards/posix/native_posix/native_posix.yaml @@ -10,6 +10,7 @@ toolchain: - llvm supported: - can + - counter - eeprom - netif:eth - usb_device diff --git a/boards/posix/native_posix/native_posix_64.yaml b/boards/posix/native_posix/native_posix_64.yaml index 62d3b168497..56ad340f176 100644 --- a/boards/posix/native_posix/native_posix_64.yaml +++ b/boards/posix/native_posix/native_posix_64.yaml @@ -10,6 +10,7 @@ toolchain: - llvm supported: - can + - counter - eeprom - netif:eth - usb_device diff --git a/boards/posix/native_sim/native_sim.yaml b/boards/posix/native_sim/native_sim.yaml index daa76cdab92..e2defe1ce40 100644 --- a/boards/posix/native_sim/native_sim.yaml +++ b/boards/posix/native_sim/native_sim.yaml @@ -10,6 +10,7 @@ toolchain: - llvm supported: - can + - counter - eeprom - netif:eth - usb_device diff --git a/boards/posix/native_sim/native_sim_64.yaml b/boards/posix/native_sim/native_sim_64.yaml index 487f623894d..52841d23b2b 100644 --- a/boards/posix/native_sim/native_sim_64.yaml +++ b/boards/posix/native_sim/native_sim_64.yaml @@ -10,6 +10,7 @@ toolchain: - llvm supported: - can + - counter - eeprom - netif:eth - usb_device diff --git a/drivers/counter/Kconfig.native_posix b/drivers/counter/Kconfig.native_posix index 362eb00da1c..a342ed07a30 100644 --- a/drivers/counter/Kconfig.native_posix +++ b/drivers/counter/Kconfig.native_posix @@ -10,3 +10,8 @@ config COUNTER_NATIVE_POSIX_FREQUENCY int "native_posix counter frequency in Hz" default 1000 depends on COUNTER_NATIVE_POSIX + +config COUNTER_NATIVE_POSIX_NBR_CHANNELS + int "native counter, number of channels" + default 4 + depends on COUNTER_NATIVE_POSIX diff --git a/drivers/counter/counter_native_posix.c b/drivers/counter/counter_native_posix.c index aa221a11fdc..d03517b1b86 100644 --- a/drivers/counter/counter_native_posix.c +++ b/drivers/counter/counter_native_posix.c @@ -6,6 +6,7 @@ #define DT_DRV_COMPAT zephyr_native_posix_counter +#include #include #include #include @@ -14,38 +15,80 @@ #include #define DRIVER_CONFIG_INFO_FLAGS (COUNTER_CONFIG_INFO_COUNT_UP) -#define DRIVER_CONFIG_INFO_CHANNELS 1 +#define DRIVER_CONFIG_INFO_CHANNELS CONFIG_COUNTER_NATIVE_POSIX_NBR_CHANNELS #define COUNTER_NATIVE_POSIX_IRQ_FLAGS (0) #define COUNTER_NATIVE_POSIX_IRQ_PRIORITY (2) #define COUNTER_PERIOD (USEC_PER_SEC / CONFIG_COUNTER_NATIVE_POSIX_FREQUENCY) #define TOP_VALUE (UINT_MAX) -static struct counter_alarm_cfg pending_alarm; -static bool is_alarm_pending; +static struct counter_alarm_cfg pending_alarm[DRIVER_CONFIG_INFO_CHANNELS]; +static bool is_alarm_pending[DRIVER_CONFIG_INFO_CHANNELS]; +static struct counter_top_cfg top; +static bool is_top_set; static const struct device *device; +static void schedule_next_isr(void) +{ + int64_t current_value = hw_counter_get_value(); + uint32_t next_time = top.ticks; /* top.ticks is TOP_VALUE if is_top_set == false */ + + if (current_value == top.ticks) { + current_value = -1; + } + + for (int i = 0; i < DRIVER_CONFIG_INFO_CHANNELS; i++) { + if (is_alarm_pending[i]) { + if (pending_alarm[i].ticks > current_value) { + /* If the alarm is not after a wrap */ + next_time = MIN(pending_alarm[i].ticks, next_time); + } + } + } + + /* We will at least get an interrupt at top.ticks even if is_top_set == false, + * which is fine. We may use that to set the next alarm if needed + */ + hw_counter_set_target(next_time); +} + static void counter_isr(const void *arg) { ARG_UNUSED(arg); uint32_t current_value = hw_counter_get_value(); - if (is_alarm_pending) { - is_alarm_pending = false; - pending_alarm.callback(device, 0, current_value, - pending_alarm.user_data); + for (int i = 0; i < DRIVER_CONFIG_INFO_CHANNELS; i++) { + if (is_alarm_pending[i] && (current_value == pending_alarm[i].ticks)) { + is_alarm_pending[i] = false; + if (pending_alarm[i].callback) { + pending_alarm[i].callback(device, i, current_value, + pending_alarm[i].user_data); + } + } } + + if (is_top_set && (current_value == top.ticks)) { + if (top.callback) { + top.callback(device, top.user_data); + } + } + + schedule_next_isr(); } static int ctr_init(const struct device *dev) { device = dev; - is_alarm_pending = false; + memset(is_alarm_pending, 0, sizeof(is_alarm_pending)); + is_top_set = false; + top.ticks = TOP_VALUE; IRQ_CONNECT(COUNTER_EVENT_IRQ, COUNTER_NATIVE_POSIX_IRQ_PRIORITY, counter_isr, NULL, COUNTER_NATIVE_POSIX_IRQ_FLAGS); + irq_enable(COUNTER_EVENT_IRQ); hw_counter_set_period(COUNTER_PERIOD); - hw_counter_set_target(TOP_VALUE); + hw_counter_set_wrap_value((uint64_t)top.ticks + 1); + hw_counter_reset(); return 0; } @@ -54,6 +97,7 @@ static int ctr_start(const struct device *dev) { ARG_UNUSED(dev); + schedule_next_isr(); hw_counter_start(); return 0; } @@ -80,19 +124,56 @@ static uint32_t ctr_get_pending_int(const struct device *dev) return 0; } +static bool is_any_alarm_pending(void) +{ + for (int i = 0; i < DRIVER_CONFIG_INFO_CHANNELS; i++) { + if (is_alarm_pending[i]) { + return true; + } + } + return false; +} + static int ctr_set_top_value(const struct device *dev, const struct counter_top_cfg *cfg) { ARG_UNUSED(dev); - ARG_UNUSED(cfg); - posix_print_warning("%s not supported\n", __func__); - return -ENOTSUP; + if (is_any_alarm_pending()) { + posix_print_warning("Can't set top value while alarm is active\n"); + return -EBUSY; + } + + uint32_t current_value = hw_counter_get_value(); + + if (cfg->flags & COUNTER_TOP_CFG_DONT_RESET) { + if (current_value >= cfg->ticks) { + if (cfg->flags & COUNTER_TOP_CFG_RESET_WHEN_LATE) { + hw_counter_reset(); + } + return -ETIME; + } + } else { + hw_counter_reset(); + } + + top = *cfg; + hw_counter_set_wrap_value((uint64_t)top.ticks + 1); + + if ((cfg->ticks == TOP_VALUE) && !cfg->callback) { + is_top_set = false; + } else { + is_top_set = true; + } + + schedule_next_isr(); + + return 0; } static uint32_t ctr_get_top_value(const struct device *dev) { - return TOP_VALUE; + return top.ticks; } static int ctr_set_alarm(const struct device *dev, uint8_t chan_id, @@ -100,21 +181,31 @@ static int ctr_set_alarm(const struct device *dev, uint8_t chan_id, { ARG_UNUSED(dev); - if (chan_id >= DRIVER_CONFIG_INFO_CHANNELS) { - posix_print_warning("channel %u is not supported\n", chan_id); - return -ENOTSUP; - } + if (is_alarm_pending[chan_id]) + return -EBUSY; - pending_alarm = *alarm_cfg; - is_alarm_pending = true; + uint32_t ticks = alarm_cfg->ticks; + + if (ticks > top.ticks) { + posix_print_warning("Alarm ticks %u exceed top ticks %u\n", ticks, + top.ticks); + return -EINVAL; + } if (!(alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE)) { - pending_alarm.ticks = - hw_counter_get_value() + pending_alarm.ticks; + uint32_t current_value = hw_counter_get_value(); + + ticks += current_value; + if (ticks > top.ticks) { /* Handle wrap arounds */ + ticks -= (top.ticks + 1); /* The count period is top.ticks + 1 */ + } } - hw_counter_set_target(pending_alarm.ticks); - irq_enable(COUNTER_EVENT_IRQ); + pending_alarm[chan_id] = *alarm_cfg; + pending_alarm[chan_id].ticks = ticks; + is_alarm_pending[chan_id] = true; + + schedule_next_isr(); return 0; } @@ -123,12 +214,14 @@ static int ctr_cancel_alarm(const struct device *dev, uint8_t chan_id) { ARG_UNUSED(dev); - if (chan_id >= DRIVER_CONFIG_INFO_CHANNELS) { - posix_print_warning("channel %u is not supported\n", chan_id); + if (!hw_counter_is_started()) { + posix_print_warning("Counter not started\n"); return -ENOTSUP; } - is_alarm_pending = false; + is_alarm_pending[chan_id] = false; + + schedule_next_isr(); return 0; }