Skip to content

Commit

Permalink
drivers: counter: add support for top value configuration on native_p…
Browse files Browse the repository at this point in the history
…osix

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`.

Signed-off-by: Jason Wright <[email protected]>
  • Loading branch information
jpwright committed Oct 18, 2023
1 parent 5cf5282 commit 1cd4adf
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 11 deletions.
13 changes: 13 additions & 0 deletions boards/posix/native_posix/hw_counter.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ void hw_counter_stop(void)
hwm_find_next_timer();
}

bool hw_counter_is_started(void)
{
return counter_running;
}

/**
* Returns the current counter value.
*/
Expand All @@ -90,6 +95,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.
Expand Down
2 changes: 2 additions & 0 deletions boards/posix/native_posix/hw_counter.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ 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);
bool hw_counter_is_started(void);
uint64_t hw_counter_get_value(void);
void hw_counter_reset(void);

#ifdef __cplusplus
}
Expand Down
1 change: 1 addition & 0 deletions boards/posix/native_posix/native_posix.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ supported:
- spi
- gpio
- rtc
- counter
testing:
default: true
vendor: zephyr
1 change: 1 addition & 0 deletions boards/posix/native_posix/native_posix_64.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ supported:
- adc
- gpio
- rtc
- counter
vendor: zephyr
111 changes: 100 additions & 11 deletions drivers/counter/counter_native_posix.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,63 @@

static struct counter_alarm_cfg pending_alarm;
static bool is_alarm_pending;
static struct counter_top_cfg top;
static bool is_top_set;
static const struct device *device;
static uint32_t last_top;
static uint32_t next_top;

static void counter_isr(const void *arg)
{
ARG_UNUSED(arg);
uint32_t current_value = hw_counter_get_value();

if (is_alarm_pending) {
/* Process `top` first to ensure new target is used in any subsequent
* calls to `ctr_set_alarm`.
*/
if (is_top_set && (current_value == (last_top + top.ticks))) {
if (top.callback) {
top.callback(device, top.user_data);
}
last_top = current_value;
next_top = current_value + top.ticks;

if (next_top < last_top) {
hw_counter_set_target(TOP_VALUE);
} else {
hw_counter_set_target(next_top);
}
}

if (is_alarm_pending && (current_value == pending_alarm.ticks)) {
is_alarm_pending = false;
pending_alarm.callback(device, 0, current_value,
pending_alarm.user_data);
if (pending_alarm.callback) {
pending_alarm.callback(device, 0, current_value,
pending_alarm.user_data);
}
}

/* Rollover when `TOP_VALUE` is reached, then set the next target */
if (current_value == TOP_VALUE) {
hw_counter_reset();

if (is_alarm_pending && is_top_set) {
hw_counter_set_target(MIN(pending_alarm.ticks, next_top));
} else if (is_alarm_pending) {
hw_counter_set_target(pending_alarm.ticks);
} else if (is_top_set) {
hw_counter_set_target(next_top);
} else {
hw_counter_set_target(TOP_VALUE);
}
}
}

static int ctr_init(const struct device *dev)
{
device = dev;
is_alarm_pending = false;
is_top_set = false;

IRQ_CONNECT(COUNTER_EVENT_IRQ, COUNTER_NATIVE_POSIX_IRQ_PRIORITY,
counter_isr, NULL, COUNTER_NATIVE_POSIX_IRQ_FLAGS);
Expand Down Expand Up @@ -84,15 +123,42 @@ 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_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;
last_top = current_value;

if ((cfg->ticks != TOP_VALUE) && cfg->callback) {
is_top_set = true;
hw_counter_set_target(current_value + cfg->ticks);
irq_enable(COUNTER_EVENT_IRQ);
} else {
is_top_set = false;
}

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,
Expand All @@ -105,15 +171,33 @@ static int ctr_set_alarm(const struct device *dev, uint8_t chan_id,
return -ENOTSUP;
}

if (is_alarm_pending)
return -EBUSY;

uint32_t ticks = alarm_cfg->ticks;
uint32_t current_value = hw_counter_get_value();

if (!(alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE)) {
ticks += current_value;
}

if (is_top_set && ((ticks - current_value) > (top.ticks - current_value))) {
posix_print_warning("Alarm ticks %u exceed top ticks %u\n", ticks,
top.ticks);
return -EINVAL;
}

pending_alarm = *alarm_cfg;
pending_alarm.ticks = ticks;
is_alarm_pending = true;

if (!(alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE)) {
pending_alarm.ticks =
hw_counter_get_value() + pending_alarm.ticks;
/* set target to sooner of `ticks` and `TOP_VALUE`, accounting for rollover */
if ((TOP_VALUE - current_value) < (ticks - current_value)) {
hw_counter_set_target(TOP_VALUE);
} else {
hw_counter_set_target(pending_alarm.ticks);
}

hw_counter_set_target(pending_alarm.ticks);
irq_enable(COUNTER_EVENT_IRQ);

return 0;
Expand All @@ -128,6 +212,11 @@ static int ctr_cancel_alarm(const struct device *dev, uint8_t chan_id)
return -ENOTSUP;
}

if (!hw_counter_is_started()) {
posix_print_warning("Counter not started\n");
return -ENOTSUP;
}

is_alarm_pending = false;

return 0;
Expand Down

0 comments on commit 1cd4adf

Please sign in to comment.