diff --git a/drivers/timer/Kconfig.nrf_rtc b/drivers/timer/Kconfig.nrf_rtc index 729dc8d362a7..30abf5edd2df 100644 --- a/drivers/timer/Kconfig.nrf_rtc +++ b/drivers/timer/Kconfig.nrf_rtc @@ -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 @@ -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 diff --git a/drivers/timer/nrf_rtc_timer.c b/drivers/timer/nrf_rtc_timer.c index 8cd24e17530f..a226dbf48856 100644 --- a/drivers/timer/nrf_rtc_timer.c +++ b/drivers/timer/nrf_rtc_timer.c @@ -17,11 +17,21 @@ #include #include +/* 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) @@ -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) @@ -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 @@ -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) { @@ -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++; } @@ -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 | @@ -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); @@ -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) ? @@ -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; } diff --git a/include/zephyr/drivers/timer/nrf_rtc_timer.h b/include/zephyr/drivers/timer/nrf_rtc_timer.h index 55b2bdcd5aeb..b6b2d8b85bd2 100644 --- a/include/zephyr/drivers/timer/nrf_rtc_timer.h +++ b/include/zephyr/drivers/timer/nrf_rtc_timer.h @@ -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. * diff --git a/tests/drivers/counter/counter_nrf_rtc/fixed_top/boards/nrf52dk_nrf52832.overlay b/tests/drivers/counter/counter_nrf_rtc/fixed_top/boards/nrf52dk_nrf52832.overlay new file mode 100644 index 000000000000..ebfed9cf9032 --- /dev/null +++ b/tests/drivers/counter/counter_nrf_rtc/fixed_top/boards/nrf52dk_nrf52832.overlay @@ -0,0 +1,8 @@ +&rtc0 { + status = "okay"; + fixed-top; +}; +&rtc2 { + status = "okay"; + fixed-top; +}; diff --git a/tests/drivers/counter/counter_nrf_rtc/fixed_top/testcase.yaml b/tests/drivers/counter/counter_nrf_rtc/fixed_top/testcase.yaml index 38dda6dc88ce..763c6896d079 100644 --- a/tests/drivers/counter/counter_nrf_rtc/fixed_top/testcase.yaml +++ b/tests/drivers/counter/counter_nrf_rtc/fixed_top/testcase.yaml @@ -5,6 +5,7 @@ tests: - counter depends_on: counter platform_allow: + - nrf52dk/nrf52832 - nrf52840dk/nrf52840 - nrf52_bsim - nrf54h20dk/nrf54h20/cpuapp diff --git a/tests/drivers/timer/nrf_rtc_timer/src/main.c b/tests/drivers/timer/nrf_rtc_timer/src/main.c index ebdb1c18a3f6..00393c8587d4 100644 --- a/tests/drivers/timer/nrf_rtc_timer/src/main.c +++ b/tests/drivers/timer/nrf_rtc_timer/src/main.c @@ -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 }; @@ -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());