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 <filip.zajdel@nordicsemi.no>
This commit is contained in:
parent
33abbbfd85
commit
33eabf6fc7
|
@ -14,6 +14,7 @@ zephyr_library_sources(
|
|||
tracing.c
|
||||
cmdline_common.c
|
||||
cmdline.c
|
||||
hw_counter.c
|
||||
)
|
||||
|
||||
zephyr_library_include_directories(
|
||||
|
|
|
@ -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,
|
||||
|
|
100
boards/posix/native_posix/hw_counter.c
Normal file
100
boards/posix/native_posix/hw_counter.c
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#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;
|
||||
}
|
29
boards/posix/native_posix/hw_counter.h
Normal file
29
boards/posix/native_posix/hw_counter.h
Normal file
|
@ -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 <stdint.h>
|
||||
|
||||
#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 */
|
|
@ -18,6 +18,7 @@
|
|||
#include "timer_model.h"
|
||||
#include "irq_ctrl.h"
|
||||
#include "posix_board_if.h"
|
||||
#include "hw_counter.h"
|
||||
#include <arch/posix/posix_soc_if.h>
|
||||
#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();
|
||||
|
|
|
@ -94,4 +94,10 @@
|
|||
label = "ENTROPY_0";
|
||||
};
|
||||
|
||||
counter0: counter {
|
||||
status = "okay";
|
||||
compatible = "zephyr,native-posix-counter";
|
||||
label = "COUNTER_0";
|
||||
};
|
||||
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -40,4 +40,6 @@ source "drivers/counter/Kconfig.mcux_lptmr"
|
|||
|
||||
source "drivers/counter/Kconfig.maxim_ds3231"
|
||||
|
||||
source "drivers/counter/Kconfig.native_posix"
|
||||
|
||||
endif # COUNTER
|
||||
|
|
12
drivers/counter/Kconfig.native_posix
Normal file
12
drivers/counter/Kconfig.native_posix
Normal file
|
@ -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
|
159
drivers/counter/counter_native_posix.c
Normal file
159
drivers/counter/counter_native_posix.c
Normal file
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <device.h>
|
||||
#include <drivers/counter.h>
|
||||
#include <soc.h>
|
||||
#include <hw_counter.h>
|
||||
|
||||
#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);
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue