Contiki-NG
Loading...
Searching...
No Matches
clock-arch.c
1/*
2 * Copyright (c) 2026, RISE Research Institutes of Sweden AB
3 * All rights reserved.
4 *
5 * Author: Joakim Eriksson <joakim.eriksson@ri.se>
6 *
7 * SPDX-License-Identifier: BSD-3-Clause
8 *
9 * Minimal GRTC-backed clock implementation for nRF54L15.
10 *
11 * NOTE: This ignores advanced low-power tuning and only provides the
12 * tick functionality required for basic Contiki timers.
13 */
14
15#include "contiki.h"
16
17#include "nrfx_clock.h"
18#include "soc/nrfx_coredep.h" /* Delay functions in v3.x */
19
20/*
21 * nrfx HAL conditionally redefines GRTC_IRQn based on NRF_APPLICATION:
22 * - For nRF54L15 (LUMOS) application core: GRTC_IRQn = GRTC_2_IRQn
23 * - For nRF54L15 FLPR core: GRTC_IRQn = GRTC_0_IRQn
24 * This is defined in hal/nrf_grtc.h, which is included by nrfx_grtc.h
25 */
26#include "nrfx_grtc.h"
27#include "sys/etimer.h"
28
29#include <stdint.h>
30#include <stdbool.h>
31#include "nrf.h"
32
33#define GRTC_IRQ_PRIORITY 6
34#define GRTC_TICK_FREQUENCY_HZ 1000000UL
35
36#if CLOCK_SIZE != 4
37#error CLOCK_CONF_SIZE must be 4 (32 bit)
38#endif
39
40static volatile clock_time_t ticks;
41static uint32_t tick_interval_us;
42static nrfx_grtc_channel_t tick_channel;
43static bool is_initialized;
44static volatile nrfx_err_t last_schedule_err;
45static volatile uint32_t schedule_failure_count;
46static nrfx_err_t init_error_code;
47volatile uint8_t tick_channel_id;
48static volatile uint32_t grtc_irq_count;
49static volatile uint64_t last_tick_syscounter;
50static volatile uint32_t recover_count;
51
52static void schedule_next_tick(void);
53/*---------------------------------------------------------------------------*/
54static void
55clock_update(void)
56{
57 ticks++;
58 if(etimer_pending() && !CLOCK_LT(ticks, etimer_next_expiration_time())) {
60 }
61}
62/*---------------------------------------------------------------------------*/
63static void
64grtc_tick_handler(int32_t id, uint64_t cc_value, void *context)
65{
66 (void)id;
67 (void)context;
68
69 grtc_irq_count++;
70 last_tick_syscounter = cc_value;
71 clock_update();
72 schedule_next_tick();
73}
74static void wait_for_lfclk_ready(void);
75static void wait_for_syscounter_ready(void);
76uint64_t clock_arch_get_syscounter(void);
77/*---------------------------------------------------------------------------*/
78static void
79schedule_next_tick(void)
80{
81 /* Use absolute scheduling rather than relative. The relative form
82 * (nrfx_grtc_syscounter_cc_relative_set) writes CCADD, which on
83 * nRF54L15 LUMOS occasionally leaves CCEN Disabled when the deadline
84 * is very close to "now" — the channel then never fires and the
85 * kernel tick stops. Here we compute the deadline in code and write
86 * an absolute CC value instead.
87 *
88 * NOTE: the unconditional CCEN write at the end of this function is
89 * functionally required to start the channel — it only looks like a
90 * redundant sanity check. nrfx_grtc_syscounter_cc_absolute_set() calls
91 * cc_channel_prepare() (which disables CCEN) and then writes only
92 * CCL/CCH via nrf_grtc_sys_counter_cc_set(); it never re-enables CCEN.
93 * So on return from that call CCEN is Disabled, and without the write
94 * below the channel stays disabled and no tick ever fires. Do not
95 * remove it.
96 */
97 uint64_t deadline = clock_arch_get_syscounter() + tick_interval_us;
98 nrfx_err_t err = nrfx_grtc_syscounter_cc_absolute_set(&tick_channel,
99 deadline,
100 true);
101 last_schedule_err = err;
102 if(err == NRFX_ERROR_INTERNAL) {
103 wait_for_syscounter_ready();
104 deadline = clock_arch_get_syscounter() + tick_interval_us;
105 err = nrfx_grtc_syscounter_cc_absolute_set(&tick_channel,
106 deadline,
107 true);
108 last_schedule_err = err;
109 }
110
111 if(err != NRFX_SUCCESS) {
112 schedule_failure_count++;
113 }
114
115 /* Force CCEN active (see NOTE above — this is mandatory). CCEN ending
116 * up Disabled was the root cause of the "GRTC won't wake from WFI after
117 * soft-reset" hang that the arch/platform/nrf/lpm.h spin loop worked
118 * around: the IRQ never fired, so WFI slept forever. With absolute
119 * scheduling and this write that workaround is no longer needed, and
120 * this PR removes it. */
121 NRF_GRTC->CC[tick_channel_id].CCEN =
122 (GRTC_CC_CCEN_ACTIVE_Enable << GRTC_CC_CCEN_ACTIVE_Pos);
123}
124/*---------------------------------------------------------------------------*/
125static void
126lfclk_init(void)
127{
128 nrfx_err_t err = nrfx_clock_init(NULL);
129 if(err != NRFX_SUCCESS && err != NRFX_ERROR_ALREADY) {
130 return;
131 }
132
133 nrfx_clock_enable();
134 nrfx_clock_lfclk_start();
135 wait_for_lfclk_ready();
136}
137/*---------------------------------------------------------------------------*/
138void
140{
141 if(is_initialized) {
142 return;
143 }
144
145 ticks = 0;
146 grtc_irq_count = 0;
147 schedule_failure_count = 0;
148 tick_interval_us = (uint32_t)(((uint64_t)GRTC_TICK_FREQUENCY_HZ +
149 (CLOCK_SECOND / 2)) / CLOCK_SECOND);
150 if(tick_interval_us == 0) {
151 tick_interval_us = 1;
152 }
153
154 lfclk_init();
155
156 init_error_code = NRFX_ERROR_INTERNAL;
157
158 nrfx_err_t err = nrfx_grtc_init(GRTC_IRQ_PRIORITY);
159 if(err != NRFX_SUCCESS && err != NRFX_ERROR_ALREADY) {
160 init_error_code = err;
161 return;
162 }
163
164 /* Ensure NVIC routes the interrupt to the Cortex-M33 core */
165 NVIC_SetPriority(GRTC_IRQn, GRTC_IRQ_PRIORITY);
166 NVIC_ClearPendingIRQ(GRTC_IRQn);
167 NVIC_EnableIRQ(GRTC_IRQn);
168
169 /* Start the GRTC syscounter and busy-wait until it is ready.
170 * This call also allocates a main CC channel automatically. */
171 uint8_t main_cc_channel = 0;
172 err = nrfx_grtc_syscounter_start(true, &main_cc_channel);
173 if(err != NRFX_SUCCESS && err != NRFX_ERROR_ALREADY) {
174 init_error_code = err;
175 return;
176 }
177 wait_for_syscounter_ready();
178 nrfx_grtc_active_request_set(true);
179
180 /* Now allocate a separate GRTC channel for our ticking */
181 uint8_t channel = 0;
182 err = nrfx_grtc_channel_alloc(&channel);
183 if(err != NRFX_SUCCESS) {
184 init_error_code = err;
185 return;
186 }
187
188 nrfx_grtc_syscounter_cc_int_enable(channel);
189
190 /* Setup channel structure */
191 tick_channel.channel = channel;
192 tick_channel.handler = grtc_tick_handler;
193 tick_channel.p_context = NULL;
194 tick_channel_id = channel;
195
196 schedule_next_tick();
197
198 is_initialized = true;
199 init_error_code = NRFX_SUCCESS;
200}
201/*---------------------------------------------------------------------------*/
202clock_time_t
204{
205 return ticks;
206}
207/*---------------------------------------------------------------------------*/
208unsigned long
210{
211 return (unsigned long)(ticks / CLOCK_SECOND);
212}
213/*---------------------------------------------------------------------------*/
214void
215clock_wait(clock_time_t i)
216{
217 clock_time_t start = clock_time();
218 while(clock_time() - start < i) {
219 __WFE();
220 }
221}
222/*---------------------------------------------------------------------------*/
223void
225{
226 nrfx_coredep_delay_us(dt);
227}
228/*---------------------------------------------------------------------------*/
229void
230clock_delay(unsigned int i)
231{
233}
234/*---------------------------------------------------------------------------*/
235uint32_t
236clock_arch_get_irq_count(void)
237{
238 return grtc_irq_count;
239}
240/*---------------------------------------------------------------------------*/
241nrfx_err_t
242clock_arch_get_last_schedule_err(void)
243{
244 return last_schedule_err;
245}
246/*---------------------------------------------------------------------------*/
247uint32_t
248clock_arch_get_schedule_failures(void)
249{
250 return schedule_failure_count;
251}
252/*---------------------------------------------------------------------------*/
253uint8_t
254clock_arch_get_tick_channel(void)
255{
256 return tick_channel_id;
257}
258/*---------------------------------------------------------------------------*/
259uint32_t
260clock_arch_get_tick_interval_us(void)
261{
262 return tick_interval_us;
263}
264/*---------------------------------------------------------------------------*/
265uint64_t
266clock_arch_get_syscounter(void)
267{
268 /* Read from the "active" domain (SYSCOUNTER[1]) to avoid the busy-wait
269 * retry loop in nrfx_grtc_syscounter_get() which can deadlock. */
270 uint32_t hi, lo;
271 do {
272 hi = NRF_GRTC->SYSCOUNTER[1].SYSCOUNTERH;
273 lo = NRF_GRTC->SYSCOUNTER[1].SYSCOUNTERL;
274 } while(hi != NRF_GRTC->SYSCOUNTER[1].SYSCOUNTERH);
275 return ((uint64_t)(hi & 0x001FFFFFUL) << 32) | lo;
276}
277/*---------------------------------------------------------------------------*/
278uint32_t
279clock_arch_get_grtc_inten(void)
280{
281 return NRF_GRTC->INTENSET2;
282}
283/*---------------------------------------------------------------------------*/
284uint32_t
285clock_arch_get_grtc_ccen(void)
286{
287 return NRF_GRTC->CC[tick_channel_id].CCEN;
288}
289/*---------------------------------------------------------------------------*/
290bool
291clock_arch_is_initialized(void)
292{
293 return is_initialized;
294}
295/*---------------------------------------------------------------------------*/
296nrfx_err_t
297clock_arch_get_init_error(void)
298{
299 return init_error_code;
300}
301/*---------------------------------------------------------------------------*/
302void
304{
305 static clock_time_t last_check_ticks;
306 static uint32_t idle_count;
307
308 if(!is_initialized) {
309 return;
310 }
311
312 clock_time_t current = ticks;
313
314 if(current != last_check_ticks) {
315 last_check_ticks = current;
316 idle_count = 0;
317 return;
318 }
319
320 idle_count++;
321
322 if(idle_count > 5000) {
323 recover_count++;
324 idle_count = 0;
325
326 /* Try to force-reschedule */
327 schedule_next_tick();
328 }
329}
330/*---------------------------------------------------------------------------*/
331static void
332wait_for_lfclk_ready(void)
333{
334 while(!nrfx_clock_lfclk_is_running()) {
335 __NOP();
336 }
337}
338/*---------------------------------------------------------------------------*/
339static void
340wait_for_syscounter_ready(void)
341{
342 while(!nrfx_grtc_ready_check()) {
343 __NOP();
344 }
345}
346/*---------------------------------------------------------------------------*/
Event timer header file.
#define CLOCK_SECOND
A second, measured in system clock time.
Definition clock.h:105
int etimer_pending(void)
Check if there are any non-expired event timers.
Definition etimer.c:225
void etimer_request_poll(void)
Make the event timer aware that the clock has changed.
Definition etimer.c:145
clock_time_t etimer_next_expiration_time(void)
Get next event timer expiration time.
Definition etimer.c:231
unsigned long clock_seconds(void)
Get the current value of the platform seconds.
Definition clock-arch.c:98
void clock_init(void)
Initialize the clock library.
Definition clock-arch.c:77
void clock_delay_usec(uint16_t dt)
Delay a given number of microseconds.
Definition clock-arch.c:114
void clock_wait(clock_time_t i)
Wait for a given number of ticks.
Definition clock-arch.c:104
clock_time_t clock_time(void)
Get the current clock time.
Definition clock-arch.c:92
void clock_delay(unsigned int i)
Obsolete delay function but we implement it here since some code still uses it.
Definition clock-arch.c:139
#define clock_arch_check_and_recover()
Stop and wait for an interrupt.
Definition lpm.h:53
static void start(void)
Start measurement.