Contiki-NG
Loading...
Searching...
No Matches
nrf_802154_platform_sl_lptimer.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2026, RISE Research Institutes of Sweden AB.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the copyright holder nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28 * OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30/*---------------------------------------------------------------------------*/
31/**
32 * \file
33 * Low Power Timer + SL Timer + Timer Coordinator backend for the
34 * nrf_802154 library on the nRF5340 network core.
35 *
36 * The open-source nrf_802154 SL ships only a Zephyr implementation of
37 * nrf_802154_sl_timer.c, so on bare-metal Contiki-NG we must provide
38 * both the SL timer service (the software timer list) and the platform
39 * low-power-timer HW backend. The structure mirrors the nRF54L15 port
40 * (arch/cpu/nrf/nrf54l15/nrf_802154_platform_sl_lptimer.c) but the HW
41 * backend is RTC-based instead of TIMER-based.
42 *
43 * Hardware notes for the nRF5340 network core:
44 * - The network core has only RTC0 and RTC1 (no RTC2). RTC0 is the
45 * Contiki-NG system clock (clock-arch.c), so this backend uses
46 * RTC1. nrf_802154_project_config.h sets NRF_802154_RTC_INSTANCE_NO=1
47 * to match (the nrf53 library default is RTC2, which does not exist
48 * on this core).
49 * - One lptick is one 32.768 kHz RTC tick (~30.5 us). The contract
50 * allows lpticks > 1 us; fine radio timing uses the separate
51 * high-frequency TIMER, not this timer.
52 * - The RTC COUNTER is 24-bit; overflow to a 64-bit lptick count is
53 * handled here. LFCLK must already be running (started by
54 * clock-arch.c for RTC0).
55 */
56/*---------------------------------------------------------------------------*/
57#include "nrf_802154_sl_timer.h"
58#include "platform/nrf_802154_platform_sl_lptimer.h"
59#include "timer/nrf_802154_timer_coord.h"
60#include "helpers/nrfx_gppi.h"
61#include "hal/nrf_rtc.h"
62#include "nrf.h"
63
64#include <stdbool.h>
65#include <stdint.h>
66/*---------------------------------------------------------------------------*/
67/* RTC1 backs the low-power timer (RTC0 is the Contiki-NG system clock). */
68#define LP_RTC NRF_RTC1
69#define LP_RTC_IRQn RTC1_IRQn
70#define LP_RTC_IRQHandler RTC1_IRQHandler
71#define LP_RTC_IRQ_PRIORITY 1
72
73/* Compare-channel assignment. The RTC counter is read directly, so unlike
74 * the TIMER backend no capture channel is needed. */
75#define ALARM_CC 0
76#define SYNC_CC 1
77#define HW_TASK_CC 2
78
79/* 24-bit RTC counter. */
80#define COUNTER_WRAP (UINT64_C(1) << 24)
81#define COUNTER_HALF_SPAN (UINT64_C(1) << 23)
82
83/* The RTC compare hardware needs the compare value to be at least two ticks
84 * ahead of COUNTER to guarantee a match; use three for margin. */
85#define MIN_TICKS_FROM_NOW 3U
86#define SYNC_MARGIN_TICKS 3U
87
88/* lptick <-> microsecond conversion. 1000000 / 32768 == 15625 / 512. */
89#define US_NUM UINT64_C(15625)
90#define US_DEN UINT64_C(512)
91
92#define FORCE_MASK_ALARM (1UL << ALARM_CC)
93#define FORCE_MASK_SYNC (1UL << SYNC_CC)
94
95static volatile bool alarm_pending;
96static volatile bool sync_pending;
97static uint32_t critical_section_depth;
98static uint32_t force_isr_mask;
99
100/* Active software timers, ordered by trigger_time. */
101static nrf_802154_sl_timer_t *alarm_head;
102static uint64_t alarm_target_lpticks;
103static uint64_t sync_fire_lpticks;
104
105enum hw_task_state {
106 HW_TASK_STATE_IDLE,
107 HW_TASK_STATE_SETTING_UP,
108 HW_TASK_STATE_READY,
109 HW_TASK_STATE_UPDATING,
110 HW_TASK_STATE_CLEANING,
111};
112
113static enum hw_task_state hw_task_state;
114static uint32_t hw_task_ppi_channel = NRF_802154_SL_HW_TASK_PPI_INVALID;
115static uint64_t hw_task_fire_lpticks;
116
117static bool timer_initialized;
118static uint64_t timer_time_upper;
119static uint32_t timer_last_low;
120/*---------------------------------------------------------------------------*/
121static inline uint32_t
122irq_lock_local(void)
123{
124 uint32_t primask = __get_PRIMASK();
125
126 __disable_irq();
127 __DMB();
128
129 return primask;
130}
131/*---------------------------------------------------------------------------*/
132static inline void
133irq_unlock_local(uint32_t primask)
134{
135 __DMB();
136 __set_PRIMASK(primask);
137}
138/*---------------------------------------------------------------------------*/
139static inline nrf_802154_sl_timer_t *
140timer_next_get(nrf_802154_sl_timer_t *timer)
141{
142 return (nrf_802154_sl_timer_t *)(uintptr_t)timer->priv.placeholder[0];
143}
144/*---------------------------------------------------------------------------*/
145static inline void
146timer_next_set(nrf_802154_sl_timer_t *timer, nrf_802154_sl_timer_t *next)
147{
148 timer->priv.placeholder[0] = (uint64_t)(uintptr_t)next;
149}
150/*---------------------------------------------------------------------------*/
151static inline bool
152timer_is_active(nrf_802154_sl_timer_t *timer)
153{
154 return timer->priv.placeholder[1] != 0U;
155}
156/*---------------------------------------------------------------------------*/
157static inline void
158timer_active_set(nrf_802154_sl_timer_t *timer, bool active)
159{
160 timer->priv.placeholder[1] = active ? 1U : 0U;
161}
162/*---------------------------------------------------------------------------*/
163__attribute__((weak)) void
164nrf_802154_sl_timestamper_synchronized(void)
165{
166}
167/*---------------------------------------------------------------------------*/
168static inline bool
169hw_task_state_set_locked(enum hw_task_state expected, enum hw_task_state new_state)
170{
171 if(hw_task_state != expected) {
172 return false;
173 }
174
175 hw_task_state = new_state;
176 return true;
177}
178/*---------------------------------------------------------------------------*/
179static inline nrf_rtc_event_t
180compare_event(uint8_t cc_channel)
181{
182 return NRF_RTC_CHANNEL_EVENT_ADDR(cc_channel);
183}
184/*---------------------------------------------------------------------------*/
185static inline uint32_t
186rtc_event_address_get(uint8_t cc_channel)
187{
188 return nrf_rtc_event_address_get(LP_RTC, compare_event(cc_channel));
189}
190/*---------------------------------------------------------------------------*/
191static inline bool
192rtc_event_check_cc(uint8_t cc_channel)
193{
194 return nrf_rtc_event_check(LP_RTC, compare_event(cc_channel));
195}
196/*---------------------------------------------------------------------------*/
197static inline void
198rtc_event_clear_cc(uint8_t cc_channel)
199{
200 nrf_rtc_event_clear(LP_RTC, compare_event(cc_channel));
201}
202/*---------------------------------------------------------------------------*/
203static inline uint32_t
204rtc_int_mask_get(uint8_t cc_channel)
205{
206 return NRF_RTC_CHANNEL_INT_MASK(cc_channel);
207}
208/*---------------------------------------------------------------------------*/
209static inline void
210rtc_compare_int_enable(uint8_t cc_channel)
211{
212 nrf_rtc_int_enable(LP_RTC, rtc_int_mask_get(cc_channel));
213}
214/*---------------------------------------------------------------------------*/
215static inline void
216rtc_compare_int_disable(uint8_t cc_channel)
217{
218 nrf_rtc_int_disable(LP_RTC, rtc_int_mask_get(cc_channel));
219}
220/*---------------------------------------------------------------------------*/
221static inline uint64_t
222timer_time_get_locked(void)
223{
224 uint32_t low = nrf_rtc_counter_get(LP_RTC);
225
226 if(low < timer_last_low) {
227 timer_time_upper += COUNTER_WRAP;
228 }
229
230 timer_last_low = low;
231
232 /* timer_time_upper is always a multiple of COUNTER_WRAP. */
233 return timer_time_upper + low;
234}
235/*---------------------------------------------------------------------------*/
236static uint64_t
237timer_time_get(void)
238{
239 uint32_t primask = irq_lock_local();
240 uint64_t now = timer_time_get_locked();
241 irq_unlock_local(primask);
242 return now;
243}
244/*---------------------------------------------------------------------------*/
245static inline bool
246target_is_too_distant(uint64_t now, uint64_t target)
247{
248 return (target > now) && ((target - now) > COUNTER_HALF_SPAN);
249}
250/*---------------------------------------------------------------------------*/
251/*
252 * Program compare channel cc_channel to fire at absolute lptick time target.
253 * Arithmetic is done in the full 64-bit lptick space; only the value written
254 * to the 24-bit CC register is masked. When exact is false, a target at or
255 * just past "now" is handled by forcing the lptimer ISR instead.
256 */
257static int
258timer_compare_set_locked(uint8_t cc_channel, uint64_t target, bool exact)
259{
260 uint64_t now = timer_time_get_locked();
261 uint64_t min_target;
262
263 if(target_is_too_distant(now, target)) {
264 return -1;
265 }
266
267 if(target <= now) {
268 if(exact) {
269 return -1;
270 }
271
272 force_isr_mask |= (1UL << cc_channel);
273 rtc_event_clear_cc(cc_channel);
274 return 0;
275 }
276
277 min_target = now + MIN_TICKS_FROM_NOW;
278 if(target < min_target) {
279 if(exact) {
280 return -1;
281 }
282 target = min_target;
283 }
284
285 force_isr_mask &= ~(1UL << cc_channel);
286 rtc_event_clear_cc(cc_channel);
287 nrf_rtc_cc_set(LP_RTC, cc_channel, NRF_RTC_WRAP((uint32_t)target));
288
289 /*
290 * Guard against the RTC counter having already reached the freshly written
291 * compare value (which would otherwise be missed until the next wrap). The
292 * caller of the exact path detects "too late" itself, so only force here
293 * for the non-exact (alarm/sync) path.
294 */
295 if(!exact && timer_time_get_locked() >= target) {
296 force_isr_mask |= (1UL << cc_channel);
297 }
298
299 return 0;
300}
301/*---------------------------------------------------------------------------*/
302static inline bool
303hw_task_triggered_check_locked(uint64_t now)
304{
305 return rtc_event_check_cc(HW_TASK_CC) || now >= hw_task_fire_lpticks;
306}
307/*---------------------------------------------------------------------------*/
308static inline void
309hw_task_ppi_bind_locked(uint32_t ppi_channel)
310{
311 if(ppi_channel == NRF_802154_SL_HW_TASK_PPI_INVALID) {
312 return;
313 }
314
315 nrfx_gppi_event_endpoint_setup((uint8_t)ppi_channel,
316 rtc_event_address_get(HW_TASK_CC));
317}
318/*---------------------------------------------------------------------------*/
319static inline void
320hw_task_ppi_unbind_locked(uint32_t ppi_channel)
321{
322 if(ppi_channel == NRF_802154_SL_HW_TASK_PPI_INVALID) {
323 return;
324 }
325
326 nrfx_gppi_event_endpoint_clear((uint8_t)ppi_channel,
327 rtc_event_address_get(HW_TASK_CC));
328}
329/*---------------------------------------------------------------------------*/
330/*
331 * Enable a compare channel's interrupt, unless a critical section is active
332 * (in which case critical_section_exit() re-enables it on exit). If the
333 * deadline was already in the past, timer_compare_set_locked() recorded that
334 * in force_isr_mask, so make the IRQ pending to run the handler promptly. The
335 * caller must hold the local IRQ lock.
336 */
337static inline void
338cc_enable_locked(uint8_t cc_channel, uint32_t force_mask)
339{
340 if(critical_section_depth != 0U) {
341 return;
342 }
343
344 rtc_compare_int_enable(cc_channel);
345 if((force_isr_mask & force_mask) != 0U) {
346 NVIC_SetPendingIRQ(LP_RTC_IRQn);
347 }
348}
349/*---------------------------------------------------------------------------*/
350static void
351alarm_reschedule_locked(void)
352{
353 if(alarm_head == NULL) {
354 alarm_pending = false;
355 alarm_target_lpticks = 0;
356 force_isr_mask &= ~FORCE_MASK_ALARM;
357 rtc_compare_int_disable(ALARM_CC);
358 rtc_event_clear_cc(ALARM_CC);
359 return;
360 }
361
362 alarm_pending = true;
363 alarm_target_lpticks = alarm_head->trigger_time;
364
365 (void)timer_compare_set_locked(ALARM_CC, alarm_target_lpticks, false);
366
367 cc_enable_locked(ALARM_CC, FORCE_MASK_ALARM);
368}
369/*---------------------------------------------------------------------------*/
370static void
371sync_schedule_locked(uint64_t fire_lpticks)
372{
373 sync_pending = true;
374 sync_fire_lpticks = fire_lpticks;
375
376 (void)timer_compare_set_locked(SYNC_CC, sync_fire_lpticks, false);
377
378 cc_enable_locked(SYNC_CC, FORCE_MASK_SYNC);
379}
380/*---------------------------------------------------------------------------*/
381static nrf_802154_sl_timer_ret_t
382timer_remove_locked(nrf_802154_sl_timer_t *timer)
383{
384 nrf_802154_sl_timer_t *prev = NULL;
385 nrf_802154_sl_timer_t *curr = alarm_head;
386
387 while(curr != NULL) {
388 if(curr == timer) {
389 nrf_802154_sl_timer_t *next = timer_next_get(curr);
390
391 if(prev == NULL) {
392 alarm_head = next;
393 } else {
394 timer_next_set(prev, next);
395 }
396
397 timer_next_set(curr, NULL);
398 timer_active_set(curr, false);
399 alarm_reschedule_locked();
400 return NRF_802154_SL_TIMER_RET_SUCCESS;
401 }
402
403 prev = curr;
404 curr = timer_next_get(curr);
405 }
406
407 return NRF_802154_SL_TIMER_RET_INACTIVE;
408}
409/*---------------------------------------------------------------------------*/
410static void
411alarm_process(void)
412{
413 while(true) {
414 nrf_802154_sl_timer_t *timer;
415 uint32_t primask = irq_lock_local();
416 uint64_t now = timer_time_get_locked();
417
418 timer = alarm_head;
419
420 if(timer == NULL) {
421 alarm_pending = false;
422 alarm_target_lpticks = 0;
423 irq_unlock_local(primask);
424 break;
425 }
426
427 if(timer->trigger_time > now) {
428 alarm_reschedule_locked();
429 irq_unlock_local(primask);
430 break;
431 }
432
433 alarm_head = timer_next_get(timer);
434 timer_next_set(timer, NULL);
435 timer_active_set(timer, false);
436 alarm_reschedule_locked();
437
438 irq_unlock_local(primask);
439
440 if((timer->action_type & NRF_802154_SL_TIMER_ACTION_TYPE_CALLBACK) &&
441 timer->action.callback.callback != NULL) {
442 timer->action.callback.callback(timer);
443 }
444 }
445}
446/*---------------------------------------------------------------------------*/
447static void
448sync_process(uint64_t now)
449{
450 if(sync_pending && now >= sync_fire_lpticks) {
451 sync_pending = false;
452 nrf_802154_sl_timestamper_synchronized();
453 }
454}
455/*---------------------------------------------------------------------------*/
456void
457LP_RTC_IRQHandler(void)
458{
459 uint32_t primask = irq_lock_local();
460
461 /*
462 * Bookkeep the 24-bit counter wrap. The OVRFLW interrupt guarantees that
463 * timer_time_get_locked() runs at least once per wrap period (~512 s),
464 * which its single-wrap detection requires; without it, an idle radio
465 * would silently lose whole wrap periods of absolute time.
466 */
467 if(nrf_rtc_event_check(LP_RTC, NRF_RTC_EVENT_OVERFLOW)) {
468 nrf_rtc_event_clear(LP_RTC, NRF_RTC_EVENT_OVERFLOW);
469 (void)timer_time_get_locked();
470 }
471
472 bool alarm_forced = (force_isr_mask & FORCE_MASK_ALARM) != 0U;
473 bool sync_forced = (force_isr_mask & FORCE_MASK_SYNC) != 0U;
474 bool alarm_event = rtc_event_check_cc(ALARM_CC);
475 bool sync_event = rtc_event_check_cc(SYNC_CC);
476
477 if(alarm_event) {
478 rtc_event_clear_cc(ALARM_CC);
479 }
480 if(sync_event) {
481 rtc_event_clear_cc(SYNC_CC);
482 }
483
484 /*
485 * Clear only the bits captured above, and do it under the IRQ lock: a
486 * higher-priority interrupt (e.g. RADIO, priority 0) can preempt this
487 * handler and schedule a new past-deadline alarm, and a blanket unlocked
488 * clear would silently discard that force request while this invocation
489 * has already decided not to process it.
490 */
491 force_isr_mask &= ~((alarm_forced ? FORCE_MASK_ALARM : 0U) |
492 (sync_forced ? FORCE_MASK_SYNC : 0U));
493
494 irq_unlock_local(primask);
495
496 if(alarm_forced || alarm_event) {
497 alarm_process();
498 }
499
500 if(sync_forced || sync_event) {
501 sync_process(timer_time_get());
502 }
503}
504/*---------------------------------------------------------------------------*/
505void
506nrf_802154_platform_sl_lp_timer_init(void)
507{
508 uint32_t primask;
509
510 primask = irq_lock_local();
511
512 alarm_pending = false;
513 sync_pending = false;
514 critical_section_depth = 0;
515 force_isr_mask = 0;
516 alarm_target_lpticks = 0;
517 sync_fire_lpticks = 0;
518 hw_task_state = HW_TASK_STATE_IDLE;
519 hw_task_ppi_channel = NRF_802154_SL_HW_TASK_PPI_INVALID;
520 hw_task_fire_lpticks = 0;
521 timer_time_upper = 0;
522 timer_last_low = 0;
523
524 if(!timer_initialized) {
525 /* LFCLK is assumed to be running already (started by clock-arch.c). */
526 nrf_rtc_task_trigger(LP_RTC, NRF_RTC_TASK_STOP);
527 nrf_rtc_task_trigger(LP_RTC, NRF_RTC_TASK_CLEAR);
528 nrf_rtc_prescaler_set(LP_RTC, 0); /* 32.768 kHz, 1 lptick per RTC tick. */
529
530 rtc_event_clear_cc(ALARM_CC);
531 rtc_event_clear_cc(SYNC_CC);
532 rtc_event_clear_cc(HW_TASK_CC);
533 rtc_compare_int_disable(ALARM_CC);
534 rtc_compare_int_disable(SYNC_CC);
535 rtc_compare_int_disable(HW_TASK_CC);
536 /* The OVRFLW interrupt keeps the 64-bit lptick time base correct while
537 * the timer is otherwise unused; see LP_RTC_IRQHandler(). */
538 nrf_rtc_event_clear(LP_RTC, NRF_RTC_EVENT_OVERFLOW);
539 nrf_rtc_int_enable(LP_RTC, NRF_RTC_INT_OVERFLOW_MASK);
540 nrf_rtc_task_trigger(LP_RTC, NRF_RTC_TASK_START);
541
542 NVIC_SetPriority(LP_RTC_IRQn, LP_RTC_IRQ_PRIORITY);
543 NVIC_ClearPendingIRQ(LP_RTC_IRQn);
544 NVIC_EnableIRQ(LP_RTC_IRQn);
545
546 timer_initialized = true;
547 }
548
549 irq_unlock_local(primask);
550}
551/*---------------------------------------------------------------------------*/
552void
553nrf_802154_platform_sl_lp_timer_deinit(void)
554{
555 uint32_t primask = irq_lock_local();
556
557 alarm_pending = false;
558 sync_pending = false;
559 critical_section_depth = 0;
560 force_isr_mask = 0;
561 hw_task_state = HW_TASK_STATE_IDLE;
562 hw_task_ppi_channel = NRF_802154_SL_HW_TASK_PPI_INVALID;
563 hw_task_fire_lpticks = 0;
564
565 if(timer_initialized) {
566 rtc_compare_int_disable(ALARM_CC);
567 rtc_compare_int_disable(SYNC_CC);
568 nrf_rtc_int_disable(LP_RTC, NRF_RTC_INT_OVERFLOW_MASK);
569 nrf_rtc_event_clear(LP_RTC, NRF_RTC_EVENT_OVERFLOW);
570 rtc_event_clear_cc(ALARM_CC);
571 rtc_event_clear_cc(SYNC_CC);
572 rtc_event_clear_cc(HW_TASK_CC);
573 nrf_rtc_task_trigger(LP_RTC, NRF_RTC_TASK_STOP);
574 nrf_rtc_task_trigger(LP_RTC, NRF_RTC_TASK_CLEAR);
575 }
576
577 irq_unlock_local(primask);
578}
579/*---------------------------------------------------------------------------*/
580uint64_t
581nrf_802154_platform_sl_lptimer_current_lpticks_get(void)
582{
583 return timer_time_get();
584}
585/*---------------------------------------------------------------------------*/
586uint64_t
587nrf_802154_platform_sl_lptimer_us_to_lpticks_convert(uint64_t us, bool round_up)
588{
589 uint64_t numerator = us * US_DEN;
590
591 if(round_up) {
592 numerator += US_NUM - 1U;
593 }
594
595 return numerator / US_NUM;
596}
597/*---------------------------------------------------------------------------*/
598uint64_t
599nrf_802154_platform_sl_lptimer_lpticks_to_us_convert(uint64_t lpticks)
600{
601 return (lpticks * US_NUM) / US_DEN;
602}
603/*---------------------------------------------------------------------------*/
604/*
605 * NOTE: nrf_802154_platform_sl_lptimer_schedule_at() and
606 * nrf_802154_platform_sl_lptimer_disable() are unused in this port: their
607 * only caller in the open-source SL, nrf_802154_sl_timer.c, is excluded
608 * from the build (nrf802154.mk) because this file provides the SL timer
609 * service directly on top of the same RTC compare channel. They are kept
610 * for platform API completeness.
611 */
612void
613nrf_802154_platform_sl_lptimer_schedule_at(uint64_t fire_lpticks)
614{
615 uint32_t primask = irq_lock_local();
616
617 alarm_pending = true;
618 alarm_target_lpticks = fire_lpticks;
619 (void)timer_compare_set_locked(ALARM_CC, fire_lpticks, false);
620
621 cc_enable_locked(ALARM_CC, FORCE_MASK_ALARM);
622
623 irq_unlock_local(primask);
624}
625/*---------------------------------------------------------------------------*/
626void
627nrf_802154_platform_sl_lptimer_disable(void)
628{
629 uint32_t primask = irq_lock_local();
630
631 alarm_pending = false;
632 alarm_target_lpticks = 0;
633 force_isr_mask &= ~FORCE_MASK_ALARM;
634 rtc_compare_int_disable(ALARM_CC);
635 rtc_event_clear_cc(ALARM_CC);
636
637 irq_unlock_local(primask);
638}
639/*---------------------------------------------------------------------------*/
640void
641nrf_802154_platform_sl_lptimer_critical_section_enter(void)
642{
643 uint32_t primask = irq_lock_local();
644
645 critical_section_depth++;
646
647 if(critical_section_depth == 1U) {
648 rtc_compare_int_disable(ALARM_CC);
649 rtc_compare_int_disable(SYNC_CC);
650 }
651
652 irq_unlock_local(primask);
653}
654/*---------------------------------------------------------------------------*/
655void
656nrf_802154_platform_sl_lptimer_critical_section_exit(void)
657{
658 uint32_t primask = irq_lock_local();
659
660 if(critical_section_depth != 0U) {
661 critical_section_depth--;
662 }
663
664 if(critical_section_depth == 0U) {
665 if(sync_pending) {
666 rtc_compare_int_enable(SYNC_CC);
667 if(rtc_event_check_cc(SYNC_CC) || ((force_isr_mask & FORCE_MASK_SYNC) != 0U)) {
668 NVIC_SetPendingIRQ(LP_RTC_IRQn);
669 }
670 }
671
672 if(alarm_pending) {
673 rtc_compare_int_enable(ALARM_CC);
674 if(rtc_event_check_cc(ALARM_CC) || ((force_isr_mask & FORCE_MASK_ALARM) != 0U)) {
675 NVIC_SetPendingIRQ(LP_RTC_IRQn);
676 }
677 }
678 }
679
680 irq_unlock_local(primask);
681}
682/*---------------------------------------------------------------------------*/
683nrf_802154_sl_lptimer_platform_result_t
684nrf_802154_platform_sl_lptimer_hw_task_prepare(uint64_t fire_lpticks,
685 uint32_t ppi_channel)
686{
687 uint32_t primask = irq_lock_local();
688 uint64_t now = timer_time_get_locked();
689 int rc;
690
691 if(!hw_task_state_set_locked(HW_TASK_STATE_IDLE, HW_TASK_STATE_SETTING_UP)) {
692 irq_unlock_local(primask);
693 return NRF_802154_SL_LPTIMER_PLATFORM_NO_RESOURCES;
694 }
695
696 hw_task_ppi_unbind_locked(hw_task_ppi_channel);
697 hw_task_ppi_channel = NRF_802154_SL_HW_TASK_PPI_INVALID;
698 hw_task_fire_lpticks = fire_lpticks;
699 rtc_event_clear_cc(HW_TASK_CC);
700
701 if(target_is_too_distant(now, fire_lpticks)) {
702 hw_task_state = HW_TASK_STATE_IDLE;
703 irq_unlock_local(primask);
704 return NRF_802154_SL_LPTIMER_PLATFORM_TOO_DISTANT;
705 }
706
707 rc = timer_compare_set_locked(HW_TASK_CC, fire_lpticks, true);
708 if(rc != 0) {
709 hw_task_state = HW_TASK_STATE_IDLE;
710 irq_unlock_local(primask);
711 return NRF_802154_SL_LPTIMER_PLATFORM_TOO_LATE;
712 }
713
714 if(hw_task_triggered_check_locked(timer_time_get_locked())) {
715 hw_task_state = HW_TASK_STATE_IDLE;
716 irq_unlock_local(primask);
717 return NRF_802154_SL_LPTIMER_PLATFORM_TOO_LATE;
718 }
719
720 hw_task_ppi_bind_locked(ppi_channel);
721 hw_task_ppi_channel = ppi_channel;
722 hw_task_state = HW_TASK_STATE_READY;
723
724 irq_unlock_local(primask);
725
726 return NRF_802154_SL_LPTIMER_PLATFORM_SUCCESS;
727}
728/*---------------------------------------------------------------------------*/
729nrf_802154_sl_lptimer_platform_result_t
730nrf_802154_platform_sl_lptimer_hw_task_cleanup(void)
731{
732 uint32_t primask = irq_lock_local();
733
734 if(!hw_task_state_set_locked(HW_TASK_STATE_READY, HW_TASK_STATE_CLEANING)) {
735 irq_unlock_local(primask);
736 return NRF_802154_SL_LPTIMER_PLATFORM_WRONG_STATE;
737 }
738
739 rtc_event_clear_cc(HW_TASK_CC);
740 hw_task_ppi_unbind_locked(hw_task_ppi_channel);
741 hw_task_ppi_channel = NRF_802154_SL_HW_TASK_PPI_INVALID;
742 hw_task_state = HW_TASK_STATE_IDLE;
743
744 irq_unlock_local(primask);
745
746 return NRF_802154_SL_LPTIMER_PLATFORM_SUCCESS;
747}
748/*---------------------------------------------------------------------------*/
749nrf_802154_sl_lptimer_platform_result_t
750nrf_802154_platform_sl_lptimer_hw_task_update_ppi(uint32_t ppi_channel)
751{
752 bool too_late;
753 uint32_t primask = irq_lock_local();
754
755 if(!hw_task_state_set_locked(HW_TASK_STATE_READY, HW_TASK_STATE_UPDATING)) {
756 irq_unlock_local(primask);
757 return NRF_802154_SL_LPTIMER_PLATFORM_WRONG_STATE;
758 }
759
760 hw_task_ppi_unbind_locked(hw_task_ppi_channel);
761 hw_task_ppi_bind_locked(ppi_channel);
762 hw_task_ppi_channel = ppi_channel;
763 too_late = hw_task_triggered_check_locked(timer_time_get_locked());
764 hw_task_state = HW_TASK_STATE_READY;
765
766 irq_unlock_local(primask);
767
768 return too_late ? NRF_802154_SL_LPTIMER_PLATFORM_TOO_LATE :
769 NRF_802154_SL_LPTIMER_PLATFORM_SUCCESS;
770}
771/*---------------------------------------------------------------------------*/
772void
773nrf_802154_platform_sl_lptimer_sync_schedule_now(void)
774{
775 uint32_t primask = irq_lock_local();
776 uint64_t now = timer_time_get_locked();
777
778 sync_schedule_locked(now + SYNC_MARGIN_TICKS);
779
780 irq_unlock_local(primask);
781}
782/*---------------------------------------------------------------------------*/
783void
784nrf_802154_platform_sl_lptimer_sync_schedule_at(uint64_t fire_lpticks)
785{
786 uint32_t primask = irq_lock_local();
787
788 sync_schedule_locked(fire_lpticks);
789
790 irq_unlock_local(primask);
791}
792/*---------------------------------------------------------------------------*/
793void
794nrf_802154_platform_sl_lptimer_sync_abort(void)
795{
796 uint32_t primask = irq_lock_local();
797
798 sync_pending = false;
799 force_isr_mask &= ~FORCE_MASK_SYNC;
800 rtc_compare_int_disable(SYNC_CC);
801 rtc_event_clear_cc(SYNC_CC);
802
803 irq_unlock_local(primask);
804}
805/*---------------------------------------------------------------------------*/
806uint32_t
807nrf_802154_platform_sl_lptimer_sync_event_get(void)
808{
809 return rtc_event_address_get(SYNC_CC);
810}
811/*---------------------------------------------------------------------------*/
812uint64_t
813nrf_802154_platform_sl_lptimer_sync_lpticks_get(void)
814{
815 return sync_fire_lpticks;
816}
817/*---------------------------------------------------------------------------*/
818uint32_t
819nrf_802154_platform_sl_lptimer_granularity_get(void)
820{
821 /* One lptick rounded up to whole microseconds: ceil(15625 / 512) == 31. */
822 return (uint32_t)((US_NUM + US_DEN - 1U) / US_DEN);
823}
824/*---------------------------------------------------------------------------*/
825/* SL timer service (replaces the library's Zephyr-only implementation). */
826/*---------------------------------------------------------------------------*/
827void
828nrf_802154_sl_timer_module_init(void)
829{
830 alarm_head = NULL;
831 nrf_802154_platform_sl_lp_timer_init();
832}
833/*---------------------------------------------------------------------------*/
834void
835nrf_802154_sl_timer_module_uninit(void)
836{
837 alarm_head = NULL;
838}
839/*---------------------------------------------------------------------------*/
840uint64_t
841nrf_802154_sl_timer_current_time_get(void)
842{
843 return timer_time_get();
844}
845/*---------------------------------------------------------------------------*/
846void
847nrf_802154_sl_timer_init(nrf_802154_sl_timer_t *p_timer)
848{
849 timer_next_set(p_timer, NULL);
850 timer_active_set(p_timer, false);
851}
852/*---------------------------------------------------------------------------*/
853void
854nrf_802154_sl_timer_deinit(nrf_802154_sl_timer_t *p_timer)
855{
856 (void)nrf_802154_sl_timer_remove(p_timer);
857}
858/*---------------------------------------------------------------------------*/
859nrf_802154_sl_timer_ret_t
860nrf_802154_sl_timer_add(nrf_802154_sl_timer_t *p_timer)
861{
862 uint32_t primask = irq_lock_local();
863 nrf_802154_sl_timer_t *prev = NULL;
864 nrf_802154_sl_timer_t *curr;
865 uint64_t now = timer_time_get_locked();
866
867 if(target_is_too_distant(now, p_timer->trigger_time)) {
868 irq_unlock_local(primask);
869 return NRF_802154_SL_TIMER_RET_TOO_DISTANT;
870 }
871
872 if(timer_is_active(p_timer)) {
873 (void)timer_remove_locked(p_timer);
874 }
875
876 /* Read the head only after the removal above; removing an active timer can
877 * change alarm_head (and unlinks p_timer), so a head captured earlier would
878 * be stale and could splice p_timer into a self-referencing loop. */
879 curr = alarm_head;
880 while(curr != NULL && curr->trigger_time <= p_timer->trigger_time) {
881 prev = curr;
882 curr = timer_next_get(curr);
883 }
884
885 timer_next_set(p_timer, curr);
886 timer_active_set(p_timer, true);
887
888 if(prev == NULL) {
889 alarm_head = p_timer;
890 alarm_reschedule_locked();
891 } else {
892 timer_next_set(prev, p_timer);
893 }
894
895 irq_unlock_local(primask);
896
897 return NRF_802154_SL_TIMER_RET_SUCCESS;
898}
899/*---------------------------------------------------------------------------*/
900nrf_802154_sl_timer_ret_t
901nrf_802154_sl_timer_remove(nrf_802154_sl_timer_t *p_timer)
902{
903 nrf_802154_sl_timer_ret_t ret;
904 uint32_t primask = irq_lock_local();
905
906 ret = timer_remove_locked(p_timer);
907
908 irq_unlock_local(primask);
909
910 return ret;
911}
912/*---------------------------------------------------------------------------*/
913nrf_802154_sl_timer_ret_t
914nrf_802154_sl_timer_update_ppi(nrf_802154_sl_timer_t *p_timer, uint32_t ppi_chn)
915{
916 (void)p_timer;
917 (void)ppi_chn;
918 return NRF_802154_SL_TIMER_RET_SUCCESS;
919}
920/*---------------------------------------------------------------------------*/
921void
922nrf_802154_timer_coord_init(void)
923{
924}
925/*---------------------------------------------------------------------------*/
926void
927nrf_802154_timer_coord_uninit(void)
928{
929}
930/*---------------------------------------------------------------------------*/
931void
932nrf_802154_timer_coord_start(void)
933{
934}
935/*---------------------------------------------------------------------------*/
936void
937nrf_802154_timer_coord_stop(void)
938{
939}
940/*---------------------------------------------------------------------------*/
941void
942nrf_802154_timer_coord_timestamp_prepare(const nrf_802154_sl_event_handle_t *p_event)
943{
944 (void)p_event;
945}
946/*---------------------------------------------------------------------------*/
947bool
948nrf_802154_timer_coord_timestamp_get(uint64_t *p_timestamp)
949{
950 (void)p_timestamp;
951 return false;
952}
953/*---------------------------------------------------------------------------*/
A timer.
Definition timer.h:84