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 uint32_t ccen_fix_count;
50static volatile uint64_t last_tick_syscounter;
51static volatile uint32_t recover_count;
52
53static void schedule_next_tick(void);
54/*---------------------------------------------------------------------------*/
55static void
56clock_update(void)
57{
58 ticks++;
59 if(etimer_pending() && !CLOCK_LT(ticks, etimer_next_expiration_time())) {
61 }
62}
63/*---------------------------------------------------------------------------*/
64static void
65grtc_tick_handler(int32_t id, uint64_t cc_value, void *context)
66{
67 (void)id;
68 (void)context;
69
70 grtc_irq_count++;
71 last_tick_syscounter = cc_value;
72 clock_update();
73 schedule_next_tick();
74}
75static void wait_for_lfclk_ready(void);
76static void wait_for_syscounter_ready(void);
77/*---------------------------------------------------------------------------*/
78static void
79schedule_next_tick(void)
80{
81 nrfx_err_t err = nrfx_grtc_syscounter_cc_relative_set(&tick_channel,
82 tick_interval_us,
83 true,
84 NRFX_GRTC_CC_RELATIVE_SYSCOUNTER);
85 last_schedule_err = err;
86 if(err == NRFX_ERROR_INTERNAL) {
87 wait_for_syscounter_ready();
88 err = nrfx_grtc_syscounter_cc_relative_set(&tick_channel,
89 tick_interval_us,
90 true,
91 NRFX_GRTC_CC_RELATIVE_SYSCOUNTER);
92 last_schedule_err = err;
93 }
94
95 if(err != NRFX_SUCCESS) {
96 schedule_failure_count++;
97 }
98
99 /* Defensive: ensure CCEN is active after scheduling.
100 * On nRF54L15, cc_channel_prepare() disables CCEN and the CCADD write
101 * should auto-enable it, but verify and fix if needed. */
102 if(NRF_GRTC->CC[tick_channel_id].CCEN !=
103 (GRTC_CC_CCEN_ACTIVE_Enable << GRTC_CC_CCEN_ACTIVE_Pos)) {
104 NRF_GRTC->CC[tick_channel_id].CCEN =
105 (GRTC_CC_CCEN_ACTIVE_Enable << GRTC_CC_CCEN_ACTIVE_Pos);
106 ccen_fix_count++;
107 }
108}
109/*---------------------------------------------------------------------------*/
110static void
111lfclk_init(void)
112{
113 nrfx_err_t err = nrfx_clock_init(NULL);
114 if(err != NRFX_SUCCESS && err != NRFX_ERROR_ALREADY) {
115 return;
116 }
117
118 nrfx_clock_enable();
119 nrfx_clock_lfclk_start();
120 wait_for_lfclk_ready();
121}
122/*---------------------------------------------------------------------------*/
123void
125{
126 if(is_initialized) {
127 return;
128 }
129
130 ticks = 0;
131 grtc_irq_count = 0;
132 schedule_failure_count = 0;
133 tick_interval_us = (uint32_t)(((uint64_t)GRTC_TICK_FREQUENCY_HZ +
134 (CLOCK_SECOND / 2)) / CLOCK_SECOND);
135 if(tick_interval_us == 0) {
136 tick_interval_us = 1;
137 }
138
139 lfclk_init();
140
141 init_error_code = NRFX_ERROR_INTERNAL;
142
143 nrfx_err_t err = nrfx_grtc_init(GRTC_IRQ_PRIORITY);
144 if(err != NRFX_SUCCESS && err != NRFX_ERROR_ALREADY) {
145 init_error_code = err;
146 return;
147 }
148
149 /* Ensure NVIC routes the interrupt to the Cortex-M33 core */
150 NVIC_SetPriority(GRTC_IRQn, GRTC_IRQ_PRIORITY);
151 NVIC_ClearPendingIRQ(GRTC_IRQn);
152 NVIC_EnableIRQ(GRTC_IRQn);
153
154 /* Start the GRTC syscounter and busy-wait until it is ready.
155 * This call also allocates a main CC channel automatically. */
156 uint8_t main_cc_channel = 0;
157 err = nrfx_grtc_syscounter_start(true, &main_cc_channel);
158 if(err != NRFX_SUCCESS && err != NRFX_ERROR_ALREADY) {
159 init_error_code = err;
160 return;
161 }
162 wait_for_syscounter_ready();
163 nrfx_grtc_active_request_set(true);
164
165 /* Now allocate a separate GRTC channel for our ticking */
166 uint8_t channel = 0;
167 err = nrfx_grtc_channel_alloc(&channel);
168 if(err != NRFX_SUCCESS) {
169 init_error_code = err;
170 return;
171 }
172
173 nrfx_grtc_syscounter_cc_int_enable(channel);
174
175 /* Setup channel structure */
176 tick_channel.channel = channel;
177 tick_channel.handler = grtc_tick_handler;
178 tick_channel.p_context = NULL;
179 tick_channel_id = channel;
180
181 schedule_next_tick();
182
183 is_initialized = true;
184 init_error_code = NRFX_SUCCESS;
185}
186/*---------------------------------------------------------------------------*/
187clock_time_t
189{
190 return ticks;
191}
192/*---------------------------------------------------------------------------*/
193unsigned long
195{
196 return (unsigned long)(ticks / CLOCK_SECOND);
197}
198/*---------------------------------------------------------------------------*/
199void
200clock_wait(clock_time_t i)
201{
202 clock_time_t start = clock_time();
203 while(clock_time() - start < i) {
204 __WFE();
205 }
206}
207/*---------------------------------------------------------------------------*/
208void
210{
211 nrfx_coredep_delay_us(dt);
212}
213/*---------------------------------------------------------------------------*/
214void
215clock_delay(unsigned int i)
216{
218}
219/*---------------------------------------------------------------------------*/
220uint32_t
221clock_arch_get_irq_count(void)
222{
223 return grtc_irq_count;
224}
225/*---------------------------------------------------------------------------*/
226nrfx_err_t
227clock_arch_get_last_schedule_err(void)
228{
229 return last_schedule_err;
230}
231/*---------------------------------------------------------------------------*/
232uint32_t
233clock_arch_get_schedule_failures(void)
234{
235 return schedule_failure_count;
236}
237/*---------------------------------------------------------------------------*/
238uint8_t
239clock_arch_get_tick_channel(void)
240{
241 return tick_channel_id;
242}
243/*---------------------------------------------------------------------------*/
244uint32_t
245clock_arch_get_tick_interval_us(void)
246{
247 return tick_interval_us;
248}
249/*---------------------------------------------------------------------------*/
250uint64_t
251clock_arch_get_syscounter(void)
252{
253 /* Read from the "active" domain (SYSCOUNTER[1]) to avoid the busy-wait
254 * retry loop in nrfx_grtc_syscounter_get() which can deadlock. */
255 uint32_t hi, lo;
256 do {
257 hi = NRF_GRTC->SYSCOUNTER[1].SYSCOUNTERH;
258 lo = NRF_GRTC->SYSCOUNTER[1].SYSCOUNTERL;
259 } while(hi != NRF_GRTC->SYSCOUNTER[1].SYSCOUNTERH);
260 return ((uint64_t)(hi & 0x001FFFFFUL) << 32) | lo;
261}
262/*---------------------------------------------------------------------------*/
263uint32_t
264clock_arch_get_ccen_fix_count(void)
265{
266 return ccen_fix_count;
267}
268/*---------------------------------------------------------------------------*/
269uint32_t
270clock_arch_get_grtc_inten(void)
271{
272 return NRF_GRTC->INTENSET2;
273}
274/*---------------------------------------------------------------------------*/
275uint32_t
276clock_arch_get_grtc_ccen(void)
277{
278 return NRF_GRTC->CC[tick_channel_id].CCEN;
279}
280/*---------------------------------------------------------------------------*/
281bool
282clock_arch_is_initialized(void)
283{
284 return is_initialized;
285}
286/*---------------------------------------------------------------------------*/
287nrfx_err_t
288clock_arch_get_init_error(void)
289{
290 return init_error_code;
291}
292/*---------------------------------------------------------------------------*/
293void
295{
296 static clock_time_t last_check_ticks;
297 static uint32_t idle_count;
298
299 if(!is_initialized) {
300 return;
301 }
302
303 clock_time_t current = ticks;
304
305 if(current != last_check_ticks) {
306 last_check_ticks = current;
307 idle_count = 0;
308 return;
309 }
310
311 idle_count++;
312
313 if(idle_count > 5000) {
314 recover_count++;
315 idle_count = 0;
316
317 /* Try to force-reschedule */
318 schedule_next_tick();
319 }
320}
321/*---------------------------------------------------------------------------*/
322static void
323wait_for_lfclk_ready(void)
324{
325 while(!nrfx_clock_lfclk_is_running()) {
326 __NOP();
327 }
328}
329/*---------------------------------------------------------------------------*/
330static void
331wait_for_syscounter_ready(void)
332{
333 while(!nrfx_grtc_ready_check()) {
334 __NOP();
335 }
336}
337/*---------------------------------------------------------------------------*/
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.