Skip to content

drivers: timer: nrf_rtc_timer: Allow use of custom bit width #92025

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions drivers/timer/Kconfig.nrf_rtc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ config NRF_RTC_TIMER
depends on SOC_COMPATIBLE_NRF
select TICKLESS_CAPABLE
select SYSTEM_TIMER_HAS_DISABLE_SUPPORT
select NRFX_PPI if SOC_NRF52832
depends on !$(dt_nodelabel_enabled,rtc1)
help
This module implements a kernel device driver for the nRF Real Time
Expand Down Expand Up @@ -42,4 +43,9 @@ config NRF_RTC_TIMER_TRIGGER_OVERFLOW
When enabled, a function can be used to trigger RTC overflow and
effectively shift time into the future.

config NRF_RTC_COUNTER_BIT_WIDTH
int
default 15 if SOC_NRF52832
default 24

endif # NRF_RTC_TIMER
71 changes: 66 additions & 5 deletions drivers/timer/nrf_rtc_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,21 @@
#include <haly/nrfy_rtc.h>
#include <zephyr/irq.h>

/* Ensure that selected counter bit width is within its maximum hardware width. */
BUILD_ASSERT(CONFIG_NRF_RTC_COUNTER_BIT_WIDTH <= 24, "Counter bit width exceeds maximum width.");

#if (CONFIG_NRF_RTC_COUNTER_BIT_WIDTH < 24)
#define CUSTOM_COUNTER_BIT_WIDTH 1
#include "nrfx_ppi.h"
#else
#define CUSTOM_COUNTER_BIT_WIDTH 0
#endif

#define RTC_PRETICK (IS_ENABLED(CONFIG_SOC_NRF53_RTC_PRETICK) && \
IS_ENABLED(CONFIG_SOC_NRF5340_CPUNET))

#define EXT_CHAN_COUNT CONFIG_NRF_RTC_TIMER_USER_CHAN_COUNT
#define CHAN_COUNT (EXT_CHAN_COUNT + 1)
#define CHAN_COUNT (EXT_CHAN_COUNT + 1 + CUSTOM_COUNTER_BIT_WIDTH)

#define RTC NRF_RTC1
#define RTC_IRQn NRFX_IRQ_NUMBER_GET(RTC)
Expand All @@ -33,7 +43,7 @@ BUILD_ASSERT(CHAN_COUNT <= CHAN_COUNT_MAX, "Not enough compare channels");
BUILD_ASSERT(DT_NODE_HAS_STATUS(DT_NODELABEL(RTC_LABEL), disabled),
"Counter for RTC1 must be disabled");

#define COUNTER_BIT_WIDTH 24U
#define COUNTER_BIT_WIDTH CONFIG_NRF_RTC_COUNTER_BIT_WIDTH
#define COUNTER_SPAN BIT(COUNTER_BIT_WIDTH)
#define COUNTER_MAX (COUNTER_SPAN - 1U)
#define COUNTER_HALF_SPAN (COUNTER_SPAN / 2U)
Expand Down Expand Up @@ -259,6 +269,15 @@ static int set_alarm(int32_t chan, uint32_t req_cc, bool exact)
*/
enum { MIN_CYCLES_FROM_NOW = 3 };
uint32_t cc_val = req_cc;

#if CUSTOM_COUNTER_BIT_WIDTH
/* If a CC value is 0 when a CLEAR task is set, this will not
* trigger a COMAPRE event. Need to use 1 instead.
*/
if (cc_val % COUNTER_MAX == 0) {
cc_val = 1;
}
#endif
uint32_t cc_inc = MIN_CYCLES_FROM_NOW;

/* Disable event routing for the channel to avoid getting a COMPARE
Expand Down Expand Up @@ -422,6 +441,17 @@ uint64_t z_nrf_rtc_timer_read(void)

uint32_t cntr = counter();

#if CUSTOM_COUNTER_BIT_WIDTH
/* If counter is equal to it maximum value while val is greater
* than anchor, then we can assume that overflow has been recorded
* in the overflow_cnt, but clear task has not been triggered yet.
* Treat counter as if it has been cleared.
*/
if ((cntr == COUNTER_MAX) && (val > anchor)) {
cntr = 0;
}
#endif

val += cntr;

if (cntr < OVERFLOW_RISK_RANGE_END) {
Expand Down Expand Up @@ -560,8 +590,13 @@ void rtc_nrf_isr(const void *arg)
rtc_pretick_rtc1_isr_hook();
}

if (nrfy_rtc_int_enable_check(RTC, NRF_RTC_INT_OVERFLOW_MASK) &&
nrfy_rtc_events_process(RTC, NRF_RTC_INT_OVERFLOW_MASK)) {
if ((nrfy_rtc_int_enable_check(RTC, NRF_RTC_INT_OVERFLOW_MASK) &&
nrfy_rtc_events_process(RTC, NRF_RTC_INT_OVERFLOW_MASK)) ||
#if CUSTOM_COUNTER_BIT_WIDTH
(nrfy_rtc_int_enable_check(RTC, NRF_RTC_INT_COMPARE1_MASK) &&
nrfy_rtc_events_process(RTC, NRF_RTC_INT_COMPARE1_MASK)) ||
#endif
0) {
overflow_cnt++;
}

Expand Down Expand Up @@ -697,7 +732,9 @@ uint32_t sys_clock_cycle_get_32(void)
static void int_event_disable_rtc(void)
{
uint32_t mask = NRF_RTC_INT_TICK_MASK |
#if !CUSTOM_COUNTER_BIT_WIDTH
NRF_RTC_INT_OVERFLOW_MASK |
#endif
NRF_RTC_INT_COMPARE0_MASK |
NRF_RTC_INT_COMPARE1_MASK |
NRF_RTC_INT_COMPARE2_MASK |
Expand Down Expand Up @@ -729,7 +766,9 @@ static int sys_clock_driver_init(void)
nrfy_rtc_int_enable(RTC, NRF_RTC_CHANNEL_INT_MASK(chan));
}

#if !CUSTOM_COUNTER_BIT_WIDTH
nrfy_rtc_int_enable(RTC, NRF_RTC_INT_OVERFLOW_MASK);
#endif

NVIC_ClearPendingIRQ(RTC_IRQn);

Expand All @@ -742,7 +781,7 @@ static int sys_clock_driver_init(void)

int_mask = BIT_MASK(CHAN_COUNT);
if (CONFIG_NRF_RTC_TIMER_USER_CHAN_COUNT) {
alloc_mask = BIT_MASK(EXT_CHAN_COUNT) << 1;
alloc_mask = BIT_MASK(EXT_CHAN_COUNT) << (1 + CUSTOM_COUNTER_BIT_WIDTH);
}

uint32_t initial_timeout = IS_ENABLED(CONFIG_TICKLESS_KERNEL) ?
Expand All @@ -761,6 +800,28 @@ static int sys_clock_driver_init(void)
z_nrf_clock_control_lf_on(mode);
#endif

#if CUSTOM_COUNTER_BIT_WIDTH
/* Use channel 1 for wrapping. */
uint8_t chan = 1;
nrf_rtc_event_t evt = NRF_RTC_CHANNEL_EVENT_ADDR(chan);
nrfx_err_t result;
nrf_ppi_channel_t ch;

nrfy_rtc_event_enable(RTC, NRF_RTC_CHANNEL_INT_MASK(chan));
nrfy_rtc_cc_set(RTC, chan, COUNTER_MAX);
uint32_t evt_addr;
uint32_t task_addr;

evt_addr = nrfy_rtc_event_address_get(RTC, evt);
task_addr = nrfy_rtc_task_address_get(RTC, NRF_RTC_TASK_CLEAR);

result = nrfx_ppi_channel_alloc(&ch);
if (result != NRFX_SUCCESS) {
return -ENODEV;
}
(void)nrfx_ppi_channel_assign(ch, evt_addr, task_addr);
(void)nrfx_ppi_channel_enable(ch);
#endif
return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion include/zephyr/drivers/timer/nrf_rtc_timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ extern "C" {

/** @brief Maximum allowed time span that is considered to be in the future.
*/
#define NRF_RTC_TIMER_MAX_SCHEDULE_SPAN BIT(23)
#define NRF_RTC_TIMER_MAX_SCHEDULE_SPAN BIT(CONFIG_NRF_RTC_COUNTER_BIT_WIDTH - 1)

/** @brief RTC timer compare event handler.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
&rtc0 {
status = "okay";
fixed-top;
};
&rtc2 {
status = "okay";
fixed-top;
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ tests:
- counter
depends_on: counter
platform_allow:
- nrf52dk/nrf52832
- nrf52840dk/nrf52840
- nrf52_bsim
- nrf54h20dk/nrf54h20/cpuapp
Expand Down
11 changes: 10 additions & 1 deletion tests/drivers/timer/nrf_rtc_timer/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,11 @@ ZTEST(nrf_rtc_timer, test_resetting_cc)
struct test_data test_data = {
.target_time = now + 5,
.window = 0,
.delay = 0,
/* For lower bit width, target_time may be equal to maximum counter value.
* In such case, due to PPI connection clearing the timer, counter value
* read in the handler may be slightly off the set counter value.
*/
.delay = (CONFIG_NRF_RTC_COUNTER_BIT_WIDTH < 24) ? 2 : 0,
.err = -EINVAL
};

Expand Down Expand Up @@ -361,6 +365,11 @@ static void overflow_sched_handler(int32_t id, uint64_t expire_time,
*/
ZTEST(nrf_rtc_timer, test_overflow)
{
/* For bit width lower than default 24, overflow injection is not possible. */
if (CONFIG_NRF_RTC_COUNTER_BIT_WIDTH < 24) {
ztest_test_skip();
}

PRINT("RTC ticks before overflow injection: %u\r\n",
(uint32_t)z_nrf_rtc_timer_read());

Expand Down