From 33eabf6fc7de010c875bbac24e39199da5c5ad11 Mon Sep 17 00:00:00 2001 From: Filip Zajdel Date: Tue, 21 Jul 2020 16:34:03 +0200 Subject: [PATCH] drivers: counter: Add native_posix counter hardware model and driver Adds native_posix hw counter model and the counter driver. Functionality is needed by software which is tested on native_posix and has dependency on counter. Hardware model was developed similarly to HW timer model. The counter driver wraps HW counter functions and exposes basic functionalities: starting, stopping, setting and cancelling single channel alarms. Code was tested against: tests/drivers/counter/counter_basic_api. Signed-off-by: Filip Zajdel --- boards/posix/native_posix/CMakeLists.txt | 1 + boards/posix/native_posix/board_soc.h | 1 + boards/posix/native_posix/hw_counter.c | 100 +++++++++++ boards/posix/native_posix/hw_counter.h | 29 ++++ boards/posix/native_posix/hw_models_top.c | 8 + boards/posix/native_posix/native_posix.dts | 6 + drivers/counter/CMakeLists.txt | 1 + drivers/counter/Kconfig | 2 + drivers/counter/Kconfig.native_posix | 12 ++ drivers/counter/counter_native_posix.c | 159 ++++++++++++++++++ samples/drivers/counter/alarm/src/main.c | 2 + .../counter_basic_api/src/test_counter.c | 8 + 12 files changed, 329 insertions(+) create mode 100644 boards/posix/native_posix/hw_counter.c create mode 100644 boards/posix/native_posix/hw_counter.h create mode 100644 drivers/counter/Kconfig.native_posix create mode 100644 drivers/counter/counter_native_posix.c diff --git a/boards/posix/native_posix/CMakeLists.txt b/boards/posix/native_posix/CMakeLists.txt index 5fa77d6ca5..430518840f 100644 --- a/boards/posix/native_posix/CMakeLists.txt +++ b/boards/posix/native_posix/CMakeLists.txt @@ -14,6 +14,7 @@ zephyr_library_sources( tracing.c cmdline_common.c cmdline.c + hw_counter.c ) zephyr_library_include_directories( diff --git a/boards/posix/native_posix/board_soc.h b/boards/posix/native_posix/board_soc.h index e154b0d1d4..91b871e1d6 100644 --- a/boards/posix/native_posix/board_soc.h +++ b/boards/posix/native_posix/board_soc.h @@ -25,6 +25,7 @@ extern "C" { #define TIMER_TICK_IRQ 0 #define OFFLOAD_SW_IRQ 1 +#define COUNTER_EVENT_IRQ 2 /* * This interrupt will awake the CPU if IRQs are not locked, diff --git a/boards/posix/native_posix/hw_counter.c b/boards/posix/native_posix/hw_counter.c new file mode 100644 index 0000000000..77218a7ada --- /dev/null +++ b/boards/posix/native_posix/hw_counter.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "hw_models_top.h" +#include "board_soc.h" +#include "board_irq.h" +#include "irq_ctrl.h" + +uint64_t hw_counter_timer; + +static bool counter_running; +static uint64_t counter_value; +static uint64_t counter_target; +static uint64_t counter_period; + +/** + * Initialize the counter with prescaler of HW + */ +void hw_counter_init(void) +{ + hw_counter_timer = NEVER; + counter_target = NEVER; + counter_value = 0; + counter_running = false; + counter_period = NEVER; +} + +void hw_counter_triggered(void) +{ + if (!counter_running) { + hw_counter_timer = NEVER; + return; + } + + hw_counter_timer = hwm_get_time() + counter_period; + counter_value = counter_value + 1; + + if (counter_value == counter_target) { + hw_irq_ctrl_set_irq(COUNTER_EVENT_IRQ); + } +} + +/** + * Configures the counter period. + * The counter will be incremented every 'period' microseconds. + */ +void hw_counter_set_period(uint64_t period) +{ + counter_period = period; +} + +/** + * Starts the counter. It must be previously configured with + * hw_counter_set_period() and hw_counter_set_target(). + */ +void hw_counter_start(void) +{ + if (counter_running) { + return; + } + + counter_running = true; + + hw_counter_timer = hwm_get_time() + counter_period; + hwm_find_next_timer(); +} + +/** + * Stops the counter at current value. + * On the next call to hw_counter_start, the counter will + * start from the value at which it was stopped. + */ +void hw_counter_stop(void) +{ + counter_running = false; + hw_counter_timer = NEVER; + hwm_find_next_timer(); +} + +/** + * Returns the current counter value. + */ +uint64_t hw_counter_get_value(void) +{ + return counter_value; +} + +/** + * Configures the counter to generate an interrupt + * when its count value reaches target. + */ +void hw_counter_set_target(uint64_t target) +{ + counter_target = target; +} diff --git a/boards/posix/native_posix/hw_counter.h b/boards/posix/native_posix/hw_counter.h new file mode 100644 index 0000000000..5cd6dd2e57 --- /dev/null +++ b/boards/posix/native_posix/hw_counter.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _NATIVE_POSIX_HW_COUNTER_H +#define _NATIVE_POSIX_HW_COUNTER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void hw_counter_init(void); +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_start(void); +void hw_counter_stop(void); +uint64_t hw_counter_get_value(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _NATIVE_POSIX_HW_COUNTER_H */ diff --git a/boards/posix/native_posix/hw_models_top.c b/boards/posix/native_posix/hw_models_top.c index 629efe48c5..5f43dd6e88 100644 --- a/boards/posix/native_posix/hw_models_top.c +++ b/boards/posix/native_posix/hw_models_top.c @@ -18,6 +18,7 @@ #include "timer_model.h" #include "irq_ctrl.h" #include "posix_board_if.h" +#include "hw_counter.h" #include #include "posix_arch_internal.h" #include "sdl_events.h" @@ -30,6 +31,7 @@ static uint64_t end_of_time = NEVER; /* When will this device stop */ /* List of HW model timers: */ extern uint64_t hw_timer_timer; /* When should this timer_model be called */ extern uint64_t irq_ctrl_timer; +extern uint64_t hw_counter_timer; #ifdef CONFIG_HAS_SDL extern uint64_t sdl_event_timer; #endif @@ -37,6 +39,7 @@ extern uint64_t sdl_event_timer; static enum { HWTIMER = 0, IRQCNT, + HW_COUNTER, #ifdef CONFIG_HAS_SDL SDLEVENTTIMER, #endif @@ -47,6 +50,7 @@ static enum { static uint64_t *Timer_list[NUMBER_OF_TIMERS] = { &hw_timer_timer, &irq_ctrl_timer, + &hw_counter_timer, #ifdef CONFIG_HAS_SDL &sdl_event_timer, #endif @@ -149,6 +153,9 @@ void hwm_main_loop(void) case IRQCNT: hw_irq_ctrl_timer_triggered(); break; + case HW_COUNTER: + hw_counter_triggered(); + break; #ifdef CONFIG_HAS_SDL case SDLEVENTTIMER: sdl_handle_events(); @@ -194,6 +201,7 @@ void hwm_init(void) { hwm_set_sig_handler(); hwtimer_init(); + hw_counter_init(); hw_irq_ctrl_init(); hwm_find_next_timer(); diff --git a/boards/posix/native_posix/native_posix.dts b/boards/posix/native_posix/native_posix.dts index 35cb1649c9..136eb8347d 100644 --- a/boards/posix/native_posix/native_posix.dts +++ b/boards/posix/native_posix/native_posix.dts @@ -94,4 +94,10 @@ label = "ENTROPY_0"; }; + counter0: counter { + status = "okay"; + compatible = "zephyr,native-posix-counter"; + label = "COUNTER_0"; + }; + }; diff --git a/drivers/counter/CMakeLists.txt b/drivers/counter/CMakeLists.txt index a847380620..c748e6c84c 100644 --- a/drivers/counter/CMakeLists.txt +++ b/drivers/counter/CMakeLists.txt @@ -16,4 +16,5 @@ zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_GPT counter_mcux_gpt zephyr_library_sources_ifdef(CONFIG_COUNTER_XEC counter_mchp_xec.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_LPTMR counter_mcux_lptmr.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_MAXIM_DS3231 maxim_ds3231.c) +zephyr_library_sources_ifdef(CONFIG_COUNTER_NATIVE_POSIX counter_native_posix.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE counter_handlers.c) diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index dfbf607d48..0bcf6558b8 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -40,4 +40,6 @@ source "drivers/counter/Kconfig.mcux_lptmr" source "drivers/counter/Kconfig.maxim_ds3231" +source "drivers/counter/Kconfig.native_posix" + endif # COUNTER diff --git a/drivers/counter/Kconfig.native_posix b/drivers/counter/Kconfig.native_posix new file mode 100644 index 0000000000..b7ef0488da --- /dev/null +++ b/drivers/counter/Kconfig.native_posix @@ -0,0 +1,12 @@ +# Copyright (c) 2020, Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config COUNTER_NATIVE_POSIX + bool "Enable counter on COUNTER_0" + default y + depends on BOARD_NATIVE_POSIX + +config COUNTER_NATIVE_POSIX_FREQUENCY + int "native_posix counter frequency in Hz" + default 1000 + depends on COUNTER_NATIVE_POSIX diff --git a/drivers/counter/counter_native_posix.c b/drivers/counter/counter_native_posix.c new file mode 100644 index 0000000000..b22e036e99 --- /dev/null +++ b/drivers/counter/counter_native_posix.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2020, Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#define DT_COUNTER_LABEL counter0 +#define DRIVER_CONFIG_INFO_FLAGS (COUNTER_CONFIG_INFO_COUNT_UP) +#define DRIVER_CONFIG_INFO_CHANNELS 1 +#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 device *device; + +static void counter_isr(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); + } +} + +static int ctr_init(struct device *dev) +{ + device = dev; + is_alarm_pending = false; + + IRQ_CONNECT(COUNTER_EVENT_IRQ, COUNTER_NATIVE_POSIX_IRQ_PRIORITY, + counter_isr, NULL, COUNTER_NATIVE_POSIX_IRQ_FLAGS); + hw_counter_set_period(COUNTER_PERIOD); + hw_counter_set_target(TOP_VALUE); + + return 0; +} + +static int ctr_start(struct device *dev) +{ + ARG_UNUSED(dev); + + hw_counter_start(); + return 0; +} + +static int ctr_stop(struct device *dev) +{ + ARG_UNUSED(dev); + + hw_counter_stop(); + return 0; +} + +static int ctr_get_value(struct device *dev, uint32_t *ticks) +{ + ARG_UNUSED(dev); + + *ticks = hw_counter_get_value(); + return 0; +} + +static uint32_t ctr_get_pending_int(struct device *dev) +{ + ARG_UNUSED(dev); + return 0; +} + +static int ctr_set_top_value(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; +} + +static uint32_t ctr_get_top_value(struct device *dev) +{ + return TOP_VALUE; +} + +static uint32_t ctr_get_max_relative_alarm(struct device *dev) +{ + return TOP_VALUE; +} + +static int ctr_set_alarm(struct device *dev, uint8_t chan_id, + const struct counter_alarm_cfg *alarm_cfg) +{ + ARG_UNUSED(dev); + + if (chan_id >= DRIVER_CONFIG_INFO_CHANNELS) { + posix_print_warning("channel %u is not supported\n", chan_id); + return -ENOTSUP; + } + + pending_alarm = *alarm_cfg; + is_alarm_pending = true; + + if (!(alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE)) { + pending_alarm.ticks = + hw_counter_get_value() + pending_alarm.ticks; + } + + hw_counter_set_target(pending_alarm.ticks); + irq_enable(COUNTER_EVENT_IRQ); + + return 0; +} + +static int ctr_cancel_alarm(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; + } + + is_alarm_pending = false; + + return 0; +} + +static const struct counter_driver_api ctr_api = { + .start = ctr_start, + .stop = ctr_stop, + .get_value = ctr_get_value, + .set_alarm = ctr_set_alarm, + .cancel_alarm = ctr_cancel_alarm, + .set_top_value = ctr_set_top_value, + .get_pending_int = ctr_get_pending_int, + .get_top_value = ctr_get_top_value, + .get_max_relative_alarm = ctr_get_max_relative_alarm, +}; + +static const struct counter_config_info ctr_config = { + .max_top_value = UINT_MAX, + .freq = CONFIG_COUNTER_NATIVE_POSIX_FREQUENCY, + .channels = DRIVER_CONFIG_INFO_CHANNELS, + .flags = DRIVER_CONFIG_INFO_FLAGS +}; + +DEVICE_AND_API_INIT(posix_rtc0, DT_LABEL(DT_NODELABEL(DT_COUNTER_LABEL)), + &ctr_init, NULL, &ctr_config, PRE_KERNEL_1, + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &ctr_api); diff --git a/samples/drivers/counter/alarm/src/main.c b/samples/drivers/counter/alarm/src/main.c index 6be6662156..76f9536e54 100644 --- a/samples/drivers/counter/alarm/src/main.c +++ b/samples/drivers/counter/alarm/src/main.c @@ -21,6 +21,8 @@ struct counter_alarm_cfg alarm_cfg; #define TIMER DT_LABEL(DT_NODELABEL(rtc0)) #elif defined(CONFIG_COUNTER_RTC_STM32) #define TIMER DT_LABEL(DT_INST(0, st_stm32_rtc)) +#elif defined(CONFIG_COUNTER_NATIVE_POSIX) +#define TIMER DT_LABEL(DT_NODELABEL(counter0)) #endif static void test_counter_interrupt_fn(struct device *counter_dev, 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 05c7f40e3e..fa834082fc 100644 --- a/tests/drivers/counter/counter_basic_api/src/test_counter.c +++ b/tests/drivers/counter/counter_basic_api/src/test_counter.c @@ -57,6 +57,9 @@ static const char * const devices[] = { /* Nordic RTC1 is used for the system clock */ #ifdef CONFIG_COUNTER_RTC2 DT_LABEL(DT_NODELABEL(rtc2)), +#endif +#ifdef CONFIG_COUNTER_NATIVE_POSIX + DT_LABEL(DT_NODELABEL(counter0)), #endif /* NOTE: there is no trailing comma, as the DT_LABELS_FOR_COMPAT * handles it. @@ -875,6 +878,11 @@ static bool reliable_cancel_capable(const char *dev_name) if (strcmp(dev_name, DT_LABEL(DT_NODELABEL(timer4))) == 0) { return true; } +#endif +#ifdef CONFIG_COUNTER_NATIVE_POSIX + if (strcmp(dev_name, DT_LABEL(DT_NODELABEL(counter0))) == 0) { + return true; + } #endif return false; }