Skip to content

Commit 1ad606f

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 1fe09df commit 1ad606f

File tree

1 file changed

+63
-31
lines changed

1 file changed

+63
-31
lines changed

drivers/timer/nrf_grtc_timer.c

Lines changed: 63 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -49,25 +49,31 @@
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_REL_CYCLES BIT_MASK(31)
54+
#define MAX_REL_TICKS (MAX_REL_CYCLES / CYC_PER_TICK)
5655

5756
#if DT_NODE_HAS_STATUS_OKAY(LFCLK_NODE)
5857
#define LFCLK_FREQUENCY_HZ DT_PROP(LFCLK_NODE, clock_frequency)
5958
#else
6059
#define LFCLK_FREQUENCY_HZ CONFIG_CLOCK_CONTROL_NRF_K32SRC_FREQUENCY
6160
#endif
6261

62+
/* Threshold used to determine if there is a risk of unexpected GRTC COMPARE event coming
63+
* from previous CC value.
64+
*/
65+
#define LATENCY_THR_TICKS 200
66+
6367
#if defined(CONFIG_TEST)
6468
const int32_t z_sys_timer_irq_for_test = DT_IRQN(GRTC_NODE);
6569
#endif
6670

6771
static void sys_clock_timeout_handler(int32_t id, uint64_t cc_val, void *p_context);
6872

69-
static struct k_spinlock lock;
7073
static uint64_t last_count; /* Time (SYSCOUNTER value) @last sys_clock_announce() */
74+
static uint32_t last_elapsed;
75+
static uint64_t cc_value; /* Value that is expected to be in CC register. */
76+
static uint64_t expired_cc; /* Value that is expected to be in CC register. */
7177
static atomic_t int_mask;
7278
static uint8_t ext_channels_allocated;
7379
static uint64_t grtc_start_value;
@@ -150,17 +156,13 @@ static void compare_int_unlock(int32_t chan, bool key)
150156
static void sys_clock_timeout_handler(int32_t id, uint64_t cc_val, void *p_context)
151157
{
152158
ARG_UNUSED(id);
159+
ARG_UNUSED(cc_val);
153160
ARG_UNUSED(p_context);
154-
uint64_t dticks;
155-
uint64_t now = counter();
156-
157-
if (unlikely(now < cc_val)) {
158-
return;
159-
}
161+
uint32_t dticks;
160162

161163
dticks = counter_sub(cc_val, last_count) / CYC_PER_TICK;
162-
163-
last_count += dticks * CYC_PER_TICK;
164+
last_count += (dticks * CYC_PER_TICK);
165+
expired_cc = cc_val;
164166

165167
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
166168
/* protection is not needed because we are in the GRTC interrupt
@@ -169,6 +171,7 @@ static void sys_clock_timeout_handler(int32_t id, uint64_t cc_val, void *p_conte
169171
system_timeout_set_abs(last_count + CYC_PER_TICK);
170172
}
171173

174+
last_elapsed = 0;
172175
sys_clock_announce((int32_t)dticks);
173176
}
174177

@@ -372,6 +375,7 @@ uint64_t z_nrf_grtc_timer_startup_value_get(void)
372375
int z_nrf_grtc_wakeup_prepare(uint64_t wake_time_us)
373376
{
374377
nrfx_err_t err_code;
378+
static struct k_spinlock lock;
375379
static uint8_t systemoff_channel;
376380
uint64_t now = counter();
377381
nrfx_grtc_sleep_config_t sleep_cfg;
@@ -434,20 +438,12 @@ int z_nrf_grtc_wakeup_prepare(uint64_t wake_time_us)
434438

435439
uint32_t sys_clock_cycle_get_32(void)
436440
{
437-
k_spinlock_key_t key = k_spin_lock(&lock);
438-
uint32_t ret = (uint32_t)counter();
439-
440-
k_spin_unlock(&lock, key);
441-
return ret;
441+
return (uint32_t)counter();
442442
}
443443

444444
uint64_t sys_clock_cycle_get_64(void)
445445
{
446-
k_spinlock_key_t key = k_spin_lock(&lock);
447-
uint64_t ret = counter();
448-
449-
k_spin_unlock(&lock, key);
450-
return ret;
446+
return counter();
451447
}
452448

453449
uint32_t sys_clock_elapsed(void)
@@ -456,7 +452,9 @@ uint32_t sys_clock_elapsed(void)
456452
return 0;
457453
}
458454

459-
return (uint32_t)(counter_sub(counter(), last_count) / CYC_PER_TICK);
455+
last_elapsed = (uint32_t)counter_sub(counter(), last_count);
456+
457+
return last_elapsed / CYC_PER_TICK;
460458
}
461459

462460
#if !defined(CONFIG_GEN_SW_ISR_TABLE)
@@ -512,6 +510,10 @@ static int sys_clock_driver_init(void)
512510

513511
last_count = (counter() / CYC_PER_TICK) * CYC_PER_TICK;
514512
grtc_start_value = last_count;
513+
expired_cc = UINT64_MAX;
514+
nrfx_grtc_channel_callback_set(system_clock_channel_data.channel,
515+
sys_clock_timeout_handler, NULL);
516+
515517
int_mask = NRFX_GRTC_CONFIG_ALLOWED_CC_CHANNELS_MASK;
516518
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
517519
system_timeout_set_relative(CYC_PER_TICK);
@@ -570,18 +572,48 @@ void sys_clock_set_timeout(int32_t ticks, bool idle)
570572
return;
571573
}
572574

573-
ticks = (ticks == K_TICKS_FOREVER) ? MAX_TICKS : MIN(MAX_TICKS, MAX(ticks, 0));
575+
uint32_t ch = system_clock_channel_data.channel;
576+
577+
if ((cc_value == expired_cc) && (ticks < MAX_REL_TICKS)) {
578+
uint32_t cyc = ticks * CYC_PER_TICK;
579+
580+
if (cyc == 0) {
581+
/* GRTC will expire anyway since HW ensures that past value triggers an
582+
* event but we need to ensure to always progress the cc_value as this
583+
* if condition expects that cc_value will change after each call to
584+
* set_timeout function.
585+
*/
586+
cyc = 1;
587+
}
574588

575-
uint64_t delta_time = ticks * CYC_PER_TICK;
589+
/* If it's the first timeout setting after previous expiration and timeout
590+
* is short so fast method can be used which utilizes relative CC configuration.
591+
*/
592+
cc_value += cyc;
593+
nrfx_grtc_syscounter_cc_rel_set(ch, cyc, NRFX_GRTC_CC_RELATIVE_COMPARE);
594+
return;
595+
}
576596

577-
uint64_t target_time = counter() + delta_time;
597+
uint64_t cyc = (uint64_t)ticks * CYC_PER_TICK;
598+
bool safe_setting = false;
599+
int64_t prev_cc_val = cc_value;
578600

579-
/* Rounded down target_time to the tick boundary
580-
* (but not less than one tick after the last)
601+
cc_value = last_count + last_elapsed + cyc;
602+
603+
/* In case of timeout abort it may happen that CC is being set to a value
604+
* that later than previous CC. If previous CC value is not far in the
605+
* future, there is a risk that COMPARE event will be triggered for that
606+
* previous CC value. If there is such risk safe procedure must be applied
607+
* which is more time consuming but ensures that there will be no spurious
608+
* event.
581609
*/
582-
target_time = MAX((target_time - last_count)/CYC_PER_TICK, 1)*CYC_PER_TICK + last_count;
610+
if (prev_cc_val < cc_value) {
611+
int64_t now = last_count + last_elapsed;
612+
613+
safe_setting = (prev_cc_val - now) < LATENCY_THR_TICKS;
614+
}
583615

584-
system_timeout_set_abs(target_time);
616+
nrfx_grtc_syscounter_cc_abs_set(ch, cc_value, safe_setting);
585617
}
586618

587619
#if defined(CONFIG_NRF_GRTC_TIMER_APP_DEFINED_INIT)

0 commit comments

Comments
 (0)