Skip to content

Commit 25d225b

Browse files
committed
[nrf fromlist] drivers: timer: nrf_grtc_timer: Optimize to reduce register access
Speed up execution of the interrupt handler and sys_clock_set_timeout(). Sys_clock_set_timeout() can be called in two scenarios: from previous timeout expiration handler or freely. If the former case fast path can be used since CC value in the GRTC register just expired and it can be used as a reference for CCADD setting. This is only a single register write so it's much faster. In the latter a longer procedure is applied which also happens in two variants. If value which is set in CC is further in the future (e.g. K_FOREVER was set before) then CC can be safely overwritten with a new value without a risk of triggering unexpected COMPARE event. If value in CC is earlier than the new CC value (if earlier timeout was aborted) then there is a risk of COMPARE event happening while it is being overwritten. That case requires long and safer procedure of setting CC. Update hal_nordic with changes in the nrfx_grtc driver which are needed for nrf_grtc_timer changes. Upstream PR #: 87944 Signed-off-by: Krzysztof Chruściński <[email protected]>
1 parent 09f9bef commit 25d225b

File tree

2 files changed

+71
-17
lines changed

2 files changed

+71
-17
lines changed

drivers/timer/nrf_grtc_timer.c

+70-16
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,17 @@
4949
#define COUNTER_SPAN (GRTC_SYSCOUNTERL_VALUE_Msk | ((uint64_t)GRTC_SYSCOUNTERH_VALUE_Msk << 32))
5050
#define MAX_ABS_TICKS (COUNTER_SPAN / CYC_PER_TICK)
5151

52-
#define MAX_TICKS \
53-
(((COUNTER_SPAN / CYC_PER_TICK) > INT_MAX) ? INT_MAX : (COUNTER_SPAN / CYC_PER_TICK))
54-
55-
#define MAX_CYCLES (MAX_TICKS * CYC_PER_TICK)
52+
/* To allow use of CCADD we need to limit max cycles to 31 bits. */
53+
#define MAX_CYCLES BIT_MASK(31)
54+
#define MAX_TICKS (MAX_CYCLES / CYC_PER_TICK)
5655

5756
#define LFCLK_FREQUENCY_HZ DT_PROP(LFCLK_NODE, clock_frequency)
5857

58+
/* Threshold used to determine if there is a risk of unexpected GRTC COMPARE event coming
59+
* from previous CC value.
60+
*/
61+
#define LATENCY_THR_TICKS 1000
62+
5963
#if defined(CONFIG_TEST)
6064
const int32_t z_sys_timer_irq_for_test = DT_IRQN(GRTC_NODE);
6165
#endif
@@ -64,8 +68,11 @@ static void sys_clock_timeout_handler(int32_t id, uint64_t cc_val, void *p_conte
6468

6569
static struct k_spinlock lock;
6670
static uint64_t last_count; /* Time (SYSCOUNTER value) @last sys_clock_announce() */
71+
static uint32_t last_elapsed;
72+
static uint64_t cc_value; /* Value that is expected to be in CC register. */
6773
static atomic_t int_mask;
6874
static uint8_t ext_channels_allocated;
75+
static bool in_announce;
6976
static nrfx_grtc_channel_t system_clock_channel_data = {
7077
.handler = sys_clock_timeout_handler,
7178
.p_context = NULL,
@@ -145,15 +152,18 @@ static void compare_int_unlock(int32_t chan, bool key)
145152
static void sys_clock_timeout_handler(int32_t id, uint64_t cc_val, void *p_context)
146153
{
147154
ARG_UNUSED(id);
155+
ARG_UNUSED(cc_val);
148156
ARG_UNUSED(p_context);
149157
uint64_t dticks;
150-
uint64_t now = counter();
151158

152-
if (unlikely(now < cc_val)) {
159+
/* It some corner cases past CC value may expire. If it is less than current
160+
* cc_value set by the driver it can be ignored.
161+
*/
162+
if (cc_val < cc_value) {
153163
return;
154164
}
155165

156-
dticks = counter_sub(cc_val, last_count) / CYC_PER_TICK;
166+
dticks = counter_sub(cc_value, last_count) / CYC_PER_TICK;
157167

158168
last_count += dticks * CYC_PER_TICK;
159169

@@ -164,7 +174,9 @@ static void sys_clock_timeout_handler(int32_t id, uint64_t cc_val, void *p_conte
164174
system_timeout_set_abs(last_count + CYC_PER_TICK);
165175
}
166176

177+
in_announce = true;
167178
sys_clock_announce((int32_t)dticks);
179+
in_announce = false;
168180
}
169181

170182
int32_t z_nrf_grtc_timer_chan_alloc(void)
@@ -446,7 +458,14 @@ uint32_t sys_clock_elapsed(void)
446458
return 0;
447459
}
448460

449-
return (uint32_t)(counter_sub(counter(), last_count) / CYC_PER_TICK);
461+
if (in_announce) {
462+
last_elapsed = 0;
463+
return 0;
464+
}
465+
466+
last_elapsed = (uint32_t)counter_sub(counter(), last_count);
467+
468+
return last_elapsed / CYC_PER_TICK;
450469
}
451470

452471
static int sys_clock_driver_init(void)
@@ -485,6 +504,9 @@ static int sys_clock_driver_init(void)
485504
}
486505
#endif /* CONFIG_NRF_GRTC_START_SYSCOUNTER */
487506

507+
nrfx_grtc_channel_callback_set(system_clock_channel_data.channel,
508+
sys_clock_timeout_handler, NULL);
509+
488510
int_mask = NRFX_GRTC_CONFIG_ALLOWED_CC_CHANNELS_MASK;
489511
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
490512
system_timeout_set_relative(CYC_PER_TICK);
@@ -543,18 +565,50 @@ void sys_clock_set_timeout(int32_t ticks, bool idle)
543565
return;
544566
}
545567

546-
ticks = (ticks == K_TICKS_FOREVER) ? MAX_TICKS : MIN(MAX_TICKS, MAX(ticks, 0));
568+
uint32_t cyc;
569+
uint32_t ch = system_clock_channel_data.channel;
547570

548-
uint64_t delta_time = ticks * CYC_PER_TICK;
571+
if ((uint32_t)ticks > MAX_TICKS) {
572+
cyc = MAX_CYCLES;
573+
} else {
574+
cyc = ticks * CYC_PER_TICK;
575+
}
549576

550-
uint64_t target_time = counter() + delta_time;
577+
if (in_announce) {
578+
/* CC contains just expired value. It can be used as a base for setting
579+
* new value. CCADD feature can be used. It requires only a single 32 bit
580+
* register write so it is faster than 64 bit CC value setting.
581+
*/
582+
cc_value += cyc;
583+
nrfx_grtc_syscounter_cc_rel_set(ch, cyc, NRFX_GRTC_CC_RELATIVE_COMPARE);
584+
/* Only first timeout from announcement context can be handled in the
585+
* optimized way.
586+
*/
587+
in_announce = false;
588+
} else {
589+
int64_t prev_cc_val = cc_value;
590+
bool safe_setting = false;
551591

552-
/* Rounded down target_time to the tick boundary
553-
* (but not less than one tick after the last)
554-
*/
555-
target_time = MAX((target_time - last_count)/CYC_PER_TICK, 1)*CYC_PER_TICK + last_count;
592+
/* timeout module is calling sys_clock_elapsed when adding a new timeout
593+
* so this value can be reused here.
594+
*/
595+
cc_value = last_count + last_elapsed + cyc;
596+
597+
/* In case of timeout abort it may happen that CC is being set to a value
598+
* that later than previous CC. If previous CC value is not far in the
599+
* future, there is a risk that COMPARE event will be triggered for that
600+
* previous CC value. If there is such risk safe procedure must be applied
601+
* which is more time consuming but ensures that there will be no spurious
602+
* event.
603+
*/
604+
if (prev_cc_val < cc_value) {
605+
int64_t now = last_count + last_elapsed;
556606

557-
system_timeout_set_abs(target_time);
607+
safe_setting = (int)(prev_cc_val - now) < LATENCY_THR_TICKS;
608+
}
609+
610+
nrfx_grtc_syscounter_cc_abs_set(ch, cc_value, safe_setting);
611+
}
558612
}
559613

560614
#if defined(CONFIG_NRF_GRTC_TIMER_APP_DEFINED_INIT)

west.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ manifest:
193193
groups:
194194
- hal
195195
- name: hal_nordic
196-
revision: 119ff5b5ec7a413ca2f64ae1928c79eee1e7b7b2
196+
revision: pull/288/head
197197
path: modules/hal/nordic
198198
groups:
199199
- hal

0 commit comments

Comments
 (0)