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_value;
|
||||||
static uint64_t counter_target;
|
static uint64_t counter_target;
|
||||||
static uint64_t counter_period;
|
static uint64_t counter_period;
|
||||||
|
static uint64_t counter_wrap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the counter with prescaler of HW
|
* Initialize the counter with prescaler of HW
|
||||||
|
@ -28,6 +29,7 @@ void hw_counter_init(void)
|
||||||
counter_value = 0;
|
counter_value = 0;
|
||||||
counter_running = false;
|
counter_running = false;
|
||||||
counter_period = NEVER;
|
counter_period = NEVER;
|
||||||
|
counter_wrap = NEVER;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hw_counter_triggered(void)
|
void hw_counter_triggered(void)
|
||||||
|
@ -38,7 +40,7 @@ void hw_counter_triggered(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
hw_counter_timer = hwm_get_time() + counter_period;
|
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) {
|
if (counter_value == counter_target) {
|
||||||
hw_irq_ctrl_set_irq(COUNTER_EVENT_IRQ);
|
hw_irq_ctrl_set_irq(COUNTER_EVENT_IRQ);
|
||||||
|
@ -54,6 +56,16 @@ void hw_counter_set_period(uint64_t period)
|
||||||
counter_period = 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
|
* Starts the counter. It must be previously configured with
|
||||||
* hw_counter_set_period() and hw_counter_set_target().
|
* hw_counter_set_period() and hw_counter_set_target().
|
||||||
|
@ -82,6 +94,11 @@ void hw_counter_stop(void)
|
||||||
hwm_find_next_timer();
|
hwm_find_next_timer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool hw_counter_is_started(void)
|
||||||
|
{
|
||||||
|
return counter_running;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current counter value.
|
* Returns the current counter value.
|
||||||
*/
|
*/
|
||||||
|
@ -90,6 +107,14 @@ uint64_t hw_counter_get_value(void)
|
||||||
return counter_value;
|
return counter_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the counter value.
|
||||||
|
*/
|
||||||
|
void hw_counter_reset(void)
|
||||||
|
{
|
||||||
|
counter_value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the counter to generate an interrupt
|
* Configures the counter to generate an interrupt
|
||||||
* when its count value reaches target.
|
* 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_period(uint64_t period);
|
||||||
void hw_counter_set_target(uint64_t counter_target);
|
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_start(void);
|
||||||
void hw_counter_stop(void);
|
void hw_counter_stop(void);
|
||||||
|
bool hw_counter_is_started(void);
|
||||||
uint64_t hw_counter_get_value(void);
|
uint64_t hw_counter_get_value(void);
|
||||||
|
void hw_counter_reset(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ toolchain:
|
||||||
- llvm
|
- llvm
|
||||||
supported:
|
supported:
|
||||||
- can
|
- can
|
||||||
|
- counter
|
||||||
- eeprom
|
- eeprom
|
||||||
- netif:eth
|
- netif:eth
|
||||||
- usb_device
|
- usb_device
|
||||||
|
|
|
@ -10,6 +10,7 @@ toolchain:
|
||||||
- llvm
|
- llvm
|
||||||
supported:
|
supported:
|
||||||
- can
|
- can
|
||||||
|
- counter
|
||||||
- eeprom
|
- eeprom
|
||||||
- netif:eth
|
- netif:eth
|
||||||
- usb_device
|
- usb_device
|
||||||
|
|
|
@ -10,6 +10,7 @@ toolchain:
|
||||||
- llvm
|
- llvm
|
||||||
supported:
|
supported:
|
||||||
- can
|
- can
|
||||||
|
- counter
|
||||||
- eeprom
|
- eeprom
|
||||||
- netif:eth
|
- netif:eth
|
||||||
- usb_device
|
- usb_device
|
||||||
|
|
|
@ -10,6 +10,7 @@ toolchain:
|
||||||
- llvm
|
- llvm
|
||||||
supported:
|
supported:
|
||||||
- can
|
- can
|
||||||
|
- counter
|
||||||
- eeprom
|
- eeprom
|
||||||
- netif:eth
|
- netif:eth
|
||||||
- usb_device
|
- usb_device
|
||||||
|
|
|
@ -10,3 +10,8 @@ config COUNTER_NATIVE_POSIX_FREQUENCY
|
||||||
int "native_posix counter frequency in Hz"
|
int "native_posix counter frequency in Hz"
|
||||||
default 1000
|
default 1000
|
||||||
depends on COUNTER_NATIVE_POSIX
|
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
|
#define DT_DRV_COMPAT zephyr_native_posix_counter
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
#include <zephyr/device.h>
|
#include <zephyr/device.h>
|
||||||
#include <zephyr/drivers/counter.h>
|
#include <zephyr/drivers/counter.h>
|
||||||
#include <zephyr/irq.h>
|
#include <zephyr/irq.h>
|
||||||
|
@ -14,38 +15,80 @@
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
#define DRIVER_CONFIG_INFO_FLAGS (COUNTER_CONFIG_INFO_COUNT_UP)
|
#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_FLAGS (0)
|
||||||
#define COUNTER_NATIVE_POSIX_IRQ_PRIORITY (2)
|
#define COUNTER_NATIVE_POSIX_IRQ_PRIORITY (2)
|
||||||
|
|
||||||
#define COUNTER_PERIOD (USEC_PER_SEC / CONFIG_COUNTER_NATIVE_POSIX_FREQUENCY)
|
#define COUNTER_PERIOD (USEC_PER_SEC / CONFIG_COUNTER_NATIVE_POSIX_FREQUENCY)
|
||||||
#define TOP_VALUE (UINT_MAX)
|
#define TOP_VALUE (UINT_MAX)
|
||||||
|
|
||||||
static struct counter_alarm_cfg pending_alarm;
|
static struct counter_alarm_cfg pending_alarm[DRIVER_CONFIG_INFO_CHANNELS];
|
||||||
static bool is_alarm_pending;
|
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 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)
|
static void counter_isr(const void *arg)
|
||||||
{
|
{
|
||||||
ARG_UNUSED(arg);
|
ARG_UNUSED(arg);
|
||||||
uint32_t current_value = hw_counter_get_value();
|
uint32_t current_value = hw_counter_get_value();
|
||||||
|
|
||||||
if (is_alarm_pending) {
|
for (int i = 0; i < DRIVER_CONFIG_INFO_CHANNELS; i++) {
|
||||||
is_alarm_pending = false;
|
if (is_alarm_pending[i] && (current_value == pending_alarm[i].ticks)) {
|
||||||
pending_alarm.callback(device, 0, current_value,
|
is_alarm_pending[i] = false;
|
||||||
pending_alarm.user_data);
|
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)
|
static int ctr_init(const struct device *dev)
|
||||||
{
|
{
|
||||||
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,
|
IRQ_CONNECT(COUNTER_EVENT_IRQ, COUNTER_NATIVE_POSIX_IRQ_PRIORITY,
|
||||||
counter_isr, NULL, COUNTER_NATIVE_POSIX_IRQ_FLAGS);
|
counter_isr, NULL, COUNTER_NATIVE_POSIX_IRQ_FLAGS);
|
||||||
|
irq_enable(COUNTER_EVENT_IRQ);
|
||||||
hw_counter_set_period(COUNTER_PERIOD);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -54,6 +97,7 @@ static int ctr_start(const struct device *dev)
|
||||||
{
|
{
|
||||||
ARG_UNUSED(dev);
|
ARG_UNUSED(dev);
|
||||||
|
|
||||||
|
schedule_next_isr();
|
||||||
hw_counter_start();
|
hw_counter_start();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -80,19 +124,56 @@ static uint32_t ctr_get_pending_int(const struct device *dev)
|
||||||
return 0;
|
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,
|
static int ctr_set_top_value(const struct device *dev,
|
||||||
const struct counter_top_cfg *cfg)
|
const struct counter_top_cfg *cfg)
|
||||||
{
|
{
|
||||||
ARG_UNUSED(dev);
|
ARG_UNUSED(dev);
|
||||||
ARG_UNUSED(cfg);
|
|
||||||
|
|
||||||
posix_print_warning("%s not supported\n", __func__);
|
if (is_any_alarm_pending()) {
|
||||||
return -ENOTSUP;
|
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)
|
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,
|
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);
|
ARG_UNUSED(dev);
|
||||||
|
|
||||||
if (chan_id >= DRIVER_CONFIG_INFO_CHANNELS) {
|
if (is_alarm_pending[chan_id])
|
||||||
posix_print_warning("channel %u is not supported\n", chan_id);
|
return -EBUSY;
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
pending_alarm = *alarm_cfg;
|
uint32_t ticks = alarm_cfg->ticks;
|
||||||
is_alarm_pending = true;
|
|
||||||
|
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)) {
|
if (!(alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE)) {
|
||||||
pending_alarm.ticks =
|
uint32_t current_value = hw_counter_get_value();
|
||||||
hw_counter_get_value() + pending_alarm.ticks;
|
|
||||||
|
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);
|
pending_alarm[chan_id] = *alarm_cfg;
|
||||||
irq_enable(COUNTER_EVENT_IRQ);
|
pending_alarm[chan_id].ticks = ticks;
|
||||||
|
is_alarm_pending[chan_id] = true;
|
||||||
|
|
||||||
|
schedule_next_isr();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -123,12 +214,14 @@ static int ctr_cancel_alarm(const struct device *dev, uint8_t chan_id)
|
||||||
{
|
{
|
||||||
ARG_UNUSED(dev);
|
ARG_UNUSED(dev);
|
||||||
|
|
||||||
if (chan_id >= DRIVER_CONFIG_INFO_CHANNELS) {
|
if (!hw_counter_is_started()) {
|
||||||
posix_print_warning("channel %u is not supported\n", chan_id);
|
posix_print_warning("Counter not started\n");
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
is_alarm_pending = false;
|
is_alarm_pending[chan_id] = false;
|
||||||
|
|
||||||
|
schedule_next_isr();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue