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 <jason@jpw.nyc> Signed-off-by: Alberto Escolar Piedras <alberto.escolar.piedras@nordicsemi.no>
This commit is contained in:
parent
7cf4eff731
commit
7e02a0379f
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ toolchain:
|
|||
- llvm
|
||||
supported:
|
||||
- can
|
||||
- counter
|
||||
- eeprom
|
||||
- netif:eth
|
||||
- usb_device
|
||||
|
|
|
@ -10,6 +10,7 @@ toolchain:
|
|||
- llvm
|
||||
supported:
|
||||
- can
|
||||
- counter
|
||||
- eeprom
|
||||
- netif:eth
|
||||
- usb_device
|
||||
|
|
|
@ -10,6 +10,7 @@ toolchain:
|
|||
- llvm
|
||||
supported:
|
||||
- can
|
||||
- counter
|
||||
- eeprom
|
||||
- netif:eth
|
||||
- usb_device
|
||||
|
|
|
@ -10,6 +10,7 @@ toolchain:
|
|||
- llvm
|
||||
supported:
|
||||
- can
|
||||
- counter
|
||||
- eeprom
|
||||
- netif:eth
|
||||
- usb_device
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#define DT_DRV_COMPAT zephyr_native_posix_counter
|
||||
|
||||
#include <string.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/counter.h>
|
||||
#include <zephyr/irq.h>
|
||||
|
@ -14,38 +15,80 @@
|
|||
#include <limits.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue