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:
Filip Zajdel 2020-07-21 16:34:03 +02:00 committed by Carles Cufí
parent 33abbbfd85
commit 33eabf6fc7
12 changed files with 329 additions and 0 deletions

View file

@ -14,6 +14,7 @@ zephyr_library_sources(
tracing.c
cmdline_common.c
cmdline.c
hw_counter.c
)
zephyr_library_include_directories(

View file

@ -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,

View 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;
}

View 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 */

View file

@ -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();

View file

@ -94,4 +94,10 @@
label = "ENTROPY_0";
};
counter0: counter {
status = "okay";
compatible = "zephyr,native-posix-counter";
label = "COUNTER_0";
};
};

View file

@ -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)

View file

@ -40,4 +40,6 @@ source "drivers/counter/Kconfig.mcux_lptmr"
source "drivers/counter/Kconfig.maxim_ds3231"
source "drivers/counter/Kconfig.native_posix"
endif # COUNTER

View 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

View 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);

View file

@ -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,

View file

@ -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;
}