Commit 41d56fd4 authored by Michał Stasiak's avatar Michał Stasiak Committed by Fabio Baltieri
Browse files

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



Allowed use of counter bit width lower than hardware 24.
In that case, PPI connection is established to trigger
clear task once maximum value is reached.

Signed-off-by: default avatarMichał Stasiak <michal.stasiak@nordicsemi.no>
parent 77654856
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -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,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
+70 −7
Original line number Diff line number Diff line
@@ -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"
#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

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)
@@ -139,7 +150,7 @@ uint32_t z_nrf_rtc_timer_capture_task_address_get(int32_t chan)
{
#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;
	}

@@ -259,6 +270,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 +442,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 +591,13 @@ void rtc_nrf_isr(const void *arg)
		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++;
	}

@@ -620,7 +656,7 @@ int z_nrf_rtc_timer_trigger_overflow(void)
	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);
@@ -677,7 +713,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, false);
	compare_set(SYS_CLOCK_CH, target_time, sys_clock_timeout_handler, NULL, false);
}

uint32_t sys_clock_elapsed(void)
@@ -697,7 +733,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 +767,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,13 +782,13 @@ 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(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 =
@@ -761,6 +801,29 @@ static int sys_clock_driver_init(void)
	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;
}

+1 −1
Original line number Diff line number Diff line
@@ -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.
 *
+10 −1
Original line number Diff line number Diff line
@@ -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());