Skip to content
Merged
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
7 changes: 7 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,10 @@ 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
range 15 24

endif # NRF_RTC_TIMER
77 changes: 70 additions & 7 deletions drivers/timer/nrf_rtc_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,34 @@
#include <haly/nrfy_rtc.h>
#include <zephyr/irq.h>

#define RTC_BIT_WIDTH 24

#if (CONFIG_NRF_RTC_COUNTER_BIT_WIDTH < RTC_BIT_WIDTH)
#define CUSTOM_COUNTER_BIT_WIDTH 1
#define WRAP_CH 1
#include "nrfx_ppi.h"

Check notice on line 25 in drivers/timer/nrf_rtc_timer.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/timer/nrf_rtc_timer.c:25 -#define WRAP_CH 1 +#define WRAP_CH 1
#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)
#define RTC_LABEL rtc1
#define CHAN_COUNT_MAX (RTC1_CC_NUM - (RTC_PRETICK ? 1 : 0))
#define SYS_CLOCK_CH 0

Check notice on line 41 in drivers/timer/nrf_rtc_timer.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/timer/nrf_rtc_timer.c:41 -#define CHAN_COUNT (EXT_CHAN_COUNT + 1 + CUSTOM_COUNTER_BIT_WIDTH) +#define CHAN_COUNT (EXT_CHAN_COUNT + 1 + CUSTOM_COUNTER_BIT_WIDTH) #define RTC NRF_RTC1 #define RTC_IRQn NRFX_IRQ_NUMBER_GET(RTC) #define RTC_LABEL rtc1 #define CHAN_COUNT_MAX (RTC1_CC_NUM - (RTC_PRETICK ? 1 : 0)) -#define SYS_CLOCK_CH 0 +#define SYS_CLOCK_CH 0
BUILD_ASSERT(CHAN_COUNT <= CHAN_COUNT_MAX, "Not enough compare channels");
/* Ensure that counter driver for RTC1 is not enabled. */
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 @@ -139,7 +150,7 @@
{
#if defined(RTC_TASKS_CAPTURE_TASKS_CAPTURE_Msk)
__ASSERT_NO_MSG(chan >= 0 && chan < CHAN_COUNT);
if (chan == 0) {
if (chan == SYS_CLOCK_CH) {
return 0;
}

Expand Down Expand Up @@ -264,6 +275,15 @@
*/
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 @@ -427,6 +447,17 @@

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 @@ -565,8 +596,13 @@
rtc_pretick_rtc1_isr_hook();
}

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

Expand Down Expand Up @@ -625,7 +661,7 @@
uint64_t now = z_nrf_rtc_timer_read();

if (err == 0) {
sys_clock_timeout_handler(0, now, NULL);
sys_clock_timeout_handler(SYS_CLOCK_CH, now, NULL);
}
bail:
full_int_unlock(mcu_critical_state);
Expand Down Expand Up @@ -682,7 +718,7 @@

uint64_t target_time = cyc + last_count;

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

uint32_t sys_clock_elapsed(void)
Expand All @@ -702,12 +738,14 @@
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 |
NRF_RTC_INT_COMPARE3_MASK;

Check notice on line 748 in drivers/timer/nrf_rtc_timer.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/timer/nrf_rtc_timer.c:748 - uint32_t mask = NRF_RTC_INT_TICK_MASK | + 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 | - NRF_RTC_INT_COMPARE3_MASK; + NRF_RTC_INT_COMPARE0_MASK | NRF_RTC_INT_COMPARE1_MASK | + NRF_RTC_INT_COMPARE2_MASK | NRF_RTC_INT_COMPARE3_MASK;
/* Reset interrupt enabling to expected reset values */
nrfy_rtc_int_disable(RTC, mask);

Expand All @@ -734,7 +772,9 @@
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 @@ -747,13 +787,13 @@

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(CHAN_COUNT) & ~BIT(SYS_CLOCK_CH);
}

uint32_t initial_timeout = IS_ENABLED(CONFIG_TICKLESS_KERNEL) ?
MAX_CYCLES : CYC_PER_TICK;

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

#if defined(CONFIG_CLOCK_CONTROL_NRF)
static const enum nrf_lfclk_start_mode mode =
Expand All @@ -766,6 +806,29 @@
z_nrf_clock_control_lf_on(mode);
#endif

#if CUSTOM_COUNTER_BIT_WIDTH
/* WRAP_CH reserved for wrapping. */
alloc_mask &= ~BIT(WRAP_CH);

nrf_rtc_event_t evt = NRF_RTC_CHANNEL_EVENT_ADDR(WRAP_CH);
nrfx_err_t result;
nrf_ppi_channel_t ch;

nrfy_rtc_event_enable(RTC, NRF_RTC_CHANNEL_INT_MASK(WRAP_CH));
nrfy_rtc_cc_set(RTC, WRAP_CH, 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,10 +321,14 @@
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
};

Check notice on line 331 in tests/drivers/timer/nrf_rtc_timer/src/main.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

tests/drivers/timer/nrf_rtc_timer/src/main.c:331 - .err = -EINVAL - }; + .err = -EINVAL};
/* Set timer but expect that it will never expire because
* it will be later on reset.
*/
Expand Down Expand Up @@ -361,6 +365,11 @@
*/
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
Loading