Skip to content

Commit

Permalink
[nrf fromtree] drivers: timer: add z_nrf_rtc_timer_exact_set
Browse files Browse the repository at this point in the history
The function `z_nrf_rtc_timer_exact_set` is added to allow
setting compare channel without possible creeping of cc val.

Signed-off-by: Andrzej Kuros <andrzej.kuros@nordicsemi.no>
(cherry picked from commit a6615ac)
  • Loading branch information
ankuns authored and gmarull committed Aug 20, 2024
1 parent b10f4b8 commit 7967848
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 13 deletions.
59 changes: 46 additions & 13 deletions drivers/timer/nrf_rtc_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,20 @@ uint64_t z_nrf_rtc_timer_get_ticks(k_timeout_t t)
* @param[in] chan A channel for which a new CC value is to be set.
*
* @param[in] req_cc Requested CC register value to be set.
*
* @param[in] exact Use @c false to allow CC adjustment if @c req_cc value is
* close to the current value of the timer.
* Use @c true to disallow CC adjustment. The function can
* fail with -EINVAL result if @p req_cc is too close to the
* current value.
*
* @retval 0 The requested CC has been set successfully.
* @retval -EINVAL The requested CC value could not be reliably set.
*/
static void set_alarm(int32_t chan, uint32_t req_cc)
static int set_alarm(int32_t chan, uint32_t req_cc, bool exact)
{
int ret = 0;

/* Ensure that the value exposed in this driver API is consistent with
* assumptions of this function.
*/
Expand Down Expand Up @@ -305,9 +316,16 @@ static void set_alarm(int32_t chan, uint32_t req_cc)
now = counter();
if (counter_sub(now, req_cc) > COUNTER_HALF_SPAN) {
event_clear(chan);
if (exact) {
ret = -EINVAL;
break;
}
} else {
break;
}
} else if (exact) {
ret = -EINVAL;
break;
}

cc_val = now + cc_inc;
Expand All @@ -316,11 +334,13 @@ static void set_alarm(int32_t chan, uint32_t req_cc)
break;
}
}

return ret;
}

static int compare_set_nolocks(int32_t chan, uint64_t target_time,
z_nrf_rtc_timer_compare_handler_t handler,
void *user_data)
void *user_data, bool exact)
{
int ret = 0;
uint32_t cc_value = absolute_time_to_cc(target_time);
Expand All @@ -336,29 +356,33 @@ static int compare_set_nolocks(int32_t chan, uint64_t target_time,
/* Target time is valid and is different than currently set.
* Set CC value.
*/
set_alarm(chan, cc_value);
ret = set_alarm(chan, cc_value, exact);
}
} else {
} else if (!exact) {
/* Force ISR handling when exiting from critical section. */
atomic_or(&force_isr_mask, BIT(chan));
} else {
ret = -EINVAL;
}

cc_data[chan].target_time = target_time;
cc_data[chan].callback = handler;
cc_data[chan].user_context = user_data;
if (ret == 0) {
cc_data[chan].target_time = target_time;
cc_data[chan].callback = handler;
cc_data[chan].user_context = user_data;
}

return ret;
}

static int compare_set(int32_t chan, uint64_t target_time,
z_nrf_rtc_timer_compare_handler_t handler,
void *user_data)
void *user_data, bool exact)
{
bool key;

key = compare_int_lock(chan);

int ret = compare_set_nolocks(chan, target_time, handler, user_data);
int ret = compare_set_nolocks(chan, target_time, handler, user_data, exact);

compare_int_unlock(chan, key);

Expand All @@ -371,7 +395,16 @@ int z_nrf_rtc_timer_set(int32_t chan, uint64_t target_time,
{
__ASSERT_NO_MSG(chan > 0 && chan < CHAN_COUNT);

return compare_set(chan, target_time, handler, user_data);
return compare_set(chan, target_time, handler, user_data, false);
}

int z_nrf_rtc_timer_exact_set(int32_t chan, uint64_t target_time,
z_nrf_rtc_timer_compare_handler_t handler,
void *user_data)
{
__ASSERT_NO_MSG(chan > 0 && chan < CHAN_COUNT);

return compare_set(chan, target_time, handler, user_data, true);
}

void z_nrf_rtc_timer_abort(int32_t chan)
Expand Down Expand Up @@ -452,7 +485,7 @@ static void sys_clock_timeout_handler(int32_t chan,
* so it won't get preempted by the interrupt.
*/
compare_set(chan, last_count + CYC_PER_TICK,
sys_clock_timeout_handler, NULL);
sys_clock_timeout_handler, NULL, false);
}

sys_clock_announce(dticks);
Expand Down Expand Up @@ -652,7 +685,7 @@ void sys_clock_set_timeout(int32_t ticks, bool idle)

uint64_t target_time = cyc + last_count;

compare_set(0, target_time, sys_clock_timeout_handler, NULL);
compare_set(0, target_time, sys_clock_timeout_handler, NULL, false);
}

uint32_t sys_clock_elapsed(void)
Expand Down Expand Up @@ -730,7 +763,7 @@ static int sys_clock_driver_init(void)
uint32_t initial_timeout = IS_ENABLED(CONFIG_TICKLESS_KERNEL) ?
MAX_CYCLES : CYC_PER_TICK;

compare_set(0, initial_timeout, sys_clock_timeout_handler, NULL);
compare_set(0, initial_timeout, sys_clock_timeout_handler, NULL, false);

z_nrf_clock_control_lf_on(mode);

Expand Down
31 changes: 31 additions & 0 deletions include/zephyr/drivers/timer/nrf_rtc_timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,42 @@ uint32_t z_nrf_rtc_timer_compare_read(int32_t chan);
* @retval 0 if the compare channel was set successfully.
* @retval -EINVAL if provided target time was further than
* @c NRF_RTC_TIMER_MAX_SCHEDULE_SPAN ticks in the future.
*
* @sa @ref z_nrf_rtc_timer_exact_set
*/
int z_nrf_rtc_timer_set(int32_t chan, uint64_t target_time,
z_nrf_rtc_timer_compare_handler_t handler,
void *user_data);

/** @brief Try to set compare channel exactly to given value.
*
* @note This function is similar to @ref z_nrf_rtc_timer_set, but the compare
* channel will be set to expected value only when it can be guaranteed that
* the hardware event will be generated exactly at expected @c target_time in
* the future. If the @c target_time is in the past or so close in the future
* that the reliable generation of event would require adjustment of compare
* value (as would @ref z_nrf_rtc_timer_set function do), neither the hardware
* event nor interrupt will be generated and the function fails.
*
* @param chan Channel ID between 1 and CONFIG_NRF_RTC_TIMER_USER_CHAN_COUNT.
*
* @param target_time Absolute target time in ticks.
*
* @param handler User function called in the context of the RTC interrupt.
*
* @param user_data Data passed to the handler.
*
* @retval 0 if the compare channel was set successfully.
* @retval -EINVAL if provided target time was further than
* @c NRF_RTC_TIMER_MAX_SCHEDULE_SPAN ticks in the future
* or the target time is in the past or is so close in the future that
* event generation could not be guaranteed without adjusting
* compare value of that channel.
*/
int z_nrf_rtc_timer_exact_set(int32_t chan, uint64_t target_time,
z_nrf_rtc_timer_compare_handler_t handler,
void *user_data);

/** @brief Abort a timer requested with @ref z_nrf_rtc_timer_set.
*
* If an abort operation is performed too late it is still possible for an event
Expand Down

0 comments on commit 7967848

Please sign in to comment.