18#include "nrf_802154_sl_timer.h"
19#include "platform/nrf_802154_platform_sl_lptimer.h"
20#include "timer/nrf_802154_timer_coord.h"
21#include "helpers/nrfx_gppi.h"
22#include "hal/nrf_timer.h"
28#define LP_TIMER NRF_TIMER20
29#define LP_TIMER_IRQn TIMER20_IRQn
30#define LP_TIMER_IRQ_PRIORITY 1
37#define COUNTER_HALF_SPAN (UINT64_C(1) << 31)
38#define COUNTER_WRAP (UINT64_C(1) << 32)
39#define MIN_TICKS_FROM_NOW 8U
40#define SYNC_MARGIN_TICKS 8U
42#define FORCE_MASK_ALARM (1UL << ALARM_CC)
43#define FORCE_MASK_SYNC (1UL << SYNC_CC)
45static volatile bool alarm_pending;
46static volatile bool sync_pending;
47static uint32_t critical_section_depth;
48static uint32_t force_isr_mask;
51static nrf_802154_sl_timer_t *alarm_head;
52static uint64_t alarm_target_lpticks;
53static uint64_t sync_fire_lpticks;
57 HW_TASK_STATE_SETTING_UP,
59 HW_TASK_STATE_UPDATING,
60 HW_TASK_STATE_CLEANING,
63static enum hw_task_state hw_task_state;
64static uint32_t hw_task_ppi_channel = NRF_802154_SL_HW_TASK_PPI_INVALID;
65static uint64_t hw_task_fire_lpticks;
67static bool timer_initialized;
68static uint64_t timer_time_upper;
69static uint32_t timer_last_low;
74 uint32_t primask = __get_PRIMASK();
83irq_unlock_local(uint32_t primask)
86 __set_PRIMASK(primask);
89static inline nrf_802154_sl_timer_t *
90timer_next_get(nrf_802154_sl_timer_t *
timer)
92 return (nrf_802154_sl_timer_t *)(uintptr_t)
timer->priv.placeholder[0];
96timer_next_set(nrf_802154_sl_timer_t *
timer, nrf_802154_sl_timer_t *next)
98 timer->priv.placeholder[0] = (uint64_t)(uintptr_t)next;
102timer_is_active(nrf_802154_sl_timer_t *
timer)
104 return timer->priv.placeholder[1] != 0U;
108timer_active_set(nrf_802154_sl_timer_t *
timer,
bool active)
110 timer->priv.placeholder[1] = active ? 1U : 0U;
113__attribute__((weak))
void
114nrf_802154_sl_timestamper_synchronized(
void)
119hw_task_state_set_locked(
enum hw_task_state expected,
enum hw_task_state new_state)
121 if(hw_task_state != expected) {
125 hw_task_state = new_state;
129static inline uint32_t
130timer_event_address_get(uint8_t cc_channel)
132 return nrf_timer_event_address_get(LP_TIMER, nrf_timer_compare_event_get(cc_channel));
136timer_event_check_cc(uint8_t cc_channel)
138 return nrf_timer_event_check(LP_TIMER, nrf_timer_compare_event_get(cc_channel));
142timer_event_clear_cc(uint8_t cc_channel)
144 nrf_timer_event_clear(LP_TIMER, nrf_timer_compare_event_get(cc_channel));
147static inline uint32_t
148timer_int_mask_get(uint8_t cc_channel)
150 return nrf_timer_compare_int_get(cc_channel);
154timer_compare_int_enable(uint8_t cc_channel)
156 nrf_timer_int_enable(LP_TIMER, timer_int_mask_get(cc_channel));
160timer_compare_int_disable(uint8_t cc_channel)
162 nrf_timer_int_disable(LP_TIMER, timer_int_mask_get(cc_channel));
166timer_compare_int_lock(uint8_t cc_channel)
168 bool enabled = (nrf_timer_int_enable_check(LP_TIMER, timer_int_mask_get(cc_channel)) != 0U);
170 timer_compare_int_disable(cc_channel);
178timer_compare_int_unlock(uint8_t cc_channel,
bool key)
181 timer_compare_int_enable(cc_channel);
182 if(force_isr_mask & (1UL << cc_channel)) {
183 NVIC_SetPendingIRQ(LP_TIMER_IRQn);
188static inline uint64_t
189timer_time_get_locked(
void)
193 nrf_timer_task_trigger(LP_TIMER, nrf_timer_capture_task_get(CAPTURE_CC));
194 low = nrf_timer_cc_get(LP_TIMER, NRF_TIMER_CC_CHANNEL0);
196 if(low < timer_last_low) {
197 timer_time_upper += COUNTER_WRAP;
200 timer_last_low = low;
202 return timer_time_upper | low;
208 uint32_t primask = irq_lock_local();
209 uint64_t now = timer_time_get_locked();
210 irq_unlock_local(primask);
215target_is_too_distant(uint64_t now, uint64_t target)
217 return (target > now) && ((target - now) > COUNTER_HALF_SPAN);
221timer_compare_set_locked(uint8_t cc_channel, uint64_t target,
bool exact)
223 uint64_t now = timer_time_get_locked();
225 if(target_is_too_distant(now, target)) {
234 force_isr_mask |= (1UL << cc_channel);
235 timer_event_clear_cc(cc_channel);
240 uint32_t low_now = (uint32_t)now;
241 uint32_t cc_value = (uint32_t)target;
242 uint32_t min_cc = low_now + MIN_TICKS_FROM_NOW;
244 if((int32_t)(cc_value - min_cc) <= 0) {
252 force_isr_mask &= ~(1UL << cc_channel);
253 timer_event_clear_cc(cc_channel);
254 nrf_timer_cc_set(LP_TIMER, (nrf_timer_cc_channel_t)cc_channel, cc_value);
261hw_task_triggered_check_locked(uint64_t now)
263 return timer_event_check_cc(HW_TASK_CC) || now >= hw_task_fire_lpticks;
267hw_task_ppi_bind_locked(uint32_t ppi_channel)
269 if(ppi_channel == NRF_802154_SL_HW_TASK_PPI_INVALID) {
273 nrfx_gppi_event_endpoint_setup((uint8_t)ppi_channel, timer_event_address_get(HW_TASK_CC));
277hw_task_ppi_unbind_locked(uint32_t ppi_channel)
279 if(ppi_channel == NRF_802154_SL_HW_TASK_PPI_INVALID) {
283 nrfx_gppi_event_endpoint_clear((uint8_t)ppi_channel, timer_event_address_get(HW_TASK_CC));
287alarm_reschedule_locked(
void)
289 if(alarm_head == NULL) {
290 alarm_pending =
false;
291 alarm_target_lpticks = 0;
292 force_isr_mask &= ~FORCE_MASK_ALARM;
293 timer_compare_int_disable(ALARM_CC);
294 timer_event_clear_cc(ALARM_CC);
298 alarm_pending =
true;
299 alarm_target_lpticks = alarm_head->trigger_time;
301 (void)timer_compare_set_locked(ALARM_CC, alarm_target_lpticks,
false);
303 if(critical_section_depth == 0U) {
304 timer_compare_int_enable(ALARM_CC);
305 if((force_isr_mask & FORCE_MASK_ALARM) != 0U) {
306 NVIC_SetPendingIRQ(LP_TIMER_IRQn);
312sync_schedule_locked(uint64_t fire_lpticks)
315 sync_fire_lpticks = fire_lpticks;
317 (void)timer_compare_set_locked(SYNC_CC, sync_fire_lpticks,
false);
319 if(critical_section_depth == 0U) {
320 timer_compare_int_enable(SYNC_CC);
321 if((force_isr_mask & FORCE_MASK_SYNC) != 0U) {
322 NVIC_SetPendingIRQ(LP_TIMER_IRQn);
327static nrf_802154_sl_timer_ret_t
328timer_remove_locked(nrf_802154_sl_timer_t *
timer)
330 nrf_802154_sl_timer_t *prev = NULL;
331 nrf_802154_sl_timer_t *curr = alarm_head;
333 while(curr != NULL) {
335 nrf_802154_sl_timer_t *next = timer_next_get(curr);
340 timer_next_set(prev, next);
343 timer_next_set(curr, NULL);
344 timer_active_set(curr,
false);
345 alarm_reschedule_locked();
346 return NRF_802154_SL_TIMER_RET_SUCCESS;
350 curr = timer_next_get(curr);
353 return NRF_802154_SL_TIMER_RET_INACTIVE;
360 nrf_802154_sl_timer_t *
timer;
361 uint32_t primask = irq_lock_local();
362 uint64_t now = timer_time_get_locked();
367 alarm_pending =
false;
368 alarm_target_lpticks = 0;
369 irq_unlock_local(primask);
373 if(
timer->trigger_time > now) {
374 alarm_reschedule_locked();
375 irq_unlock_local(primask);
379 alarm_head = timer_next_get(
timer);
380 timer_next_set(
timer, NULL);
381 timer_active_set(
timer,
false);
382 alarm_reschedule_locked();
384 irq_unlock_local(primask);
386 if((
timer->action_type & NRF_802154_SL_TIMER_ACTION_TYPE_CALLBACK) &&
387 timer->action.callback.callback != NULL) {
394sync_process(uint64_t now)
396 if(sync_pending && now >= sync_fire_lpticks) {
397 sync_pending =
false;
398 nrf_802154_sl_timestamper_synchronized();
403TIMER20_IRQHandler(
void)
405 bool alarm_forced = (force_isr_mask & FORCE_MASK_ALARM) != 0U;
406 bool sync_forced = (force_isr_mask & FORCE_MASK_SYNC) != 0U;
407 bool alarm_event = timer_event_check_cc(ALARM_CC);
408 bool sync_event = timer_event_check_cc(SYNC_CC);
411 timer_event_clear_cc(ALARM_CC);
414 timer_event_clear_cc(SYNC_CC);
417 force_isr_mask &= ~(FORCE_MASK_ALARM | FORCE_MASK_SYNC);
419 if(alarm_forced || alarm_event) {
423 if(sync_forced || sync_event) {
424 sync_process(timer_time_get());
429nrf_802154_platform_sl_lp_timer_init(
void)
433 primask = irq_lock_local();
435 alarm_pending =
false;
436 sync_pending =
false;
437 critical_section_depth = 0;
439 alarm_target_lpticks = 0;
440 sync_fire_lpticks = 0;
441 hw_task_state = HW_TASK_STATE_IDLE;
442 hw_task_ppi_channel = NRF_802154_SL_HW_TASK_PPI_INVALID;
443 hw_task_fire_lpticks = 0;
444 timer_time_upper = 0;
447 if(!timer_initialized) {
448 nrf_timer_task_trigger(LP_TIMER, NRF_TIMER_TASK_STOP);
449 nrf_timer_task_trigger(LP_TIMER, NRF_TIMER_TASK_CLEAR);
450 nrf_timer_shorts_set(LP_TIMER, 0);
451 nrf_timer_mode_set(LP_TIMER, NRF_TIMER_MODE_TIMER);
452 nrf_timer_bit_width_set(LP_TIMER, NRF_TIMER_BIT_WIDTH_32);
453 nrf_timer_prescaler_set(LP_TIMER, NRF_TIMER_FREQ_1MHz);
455 timer_event_clear_cc(ALARM_CC);
456 timer_event_clear_cc(SYNC_CC);
457 timer_event_clear_cc(HW_TASK_CC);
458 timer_compare_int_disable(ALARM_CC);
459 timer_compare_int_disable(SYNC_CC);
460 nrf_timer_task_trigger(LP_TIMER, NRF_TIMER_TASK_START);
462 NVIC_SetPriority(LP_TIMER_IRQn, LP_TIMER_IRQ_PRIORITY);
463 NVIC_ClearPendingIRQ(LP_TIMER_IRQn);
464 NVIC_EnableIRQ(LP_TIMER_IRQn);
466 timer_initialized =
true;
469 irq_unlock_local(primask);
473nrf_802154_platform_sl_lp_timer_deinit(
void)
475 uint32_t primask = irq_lock_local();
477 alarm_pending =
false;
478 sync_pending =
false;
479 critical_section_depth = 0;
481 hw_task_state = HW_TASK_STATE_IDLE;
482 hw_task_ppi_channel = NRF_802154_SL_HW_TASK_PPI_INVALID;
483 hw_task_fire_lpticks = 0;
485 if(timer_initialized) {
486 timer_compare_int_disable(ALARM_CC);
487 timer_compare_int_disable(SYNC_CC);
488 timer_event_clear_cc(ALARM_CC);
489 timer_event_clear_cc(SYNC_CC);
490 timer_event_clear_cc(HW_TASK_CC);
491 nrf_timer_task_trigger(LP_TIMER, NRF_TIMER_TASK_STOP);
492 nrf_timer_task_trigger(LP_TIMER, NRF_TIMER_TASK_CLEAR);
495 irq_unlock_local(primask);
499nrf_802154_platform_sl_lptimer_current_lpticks_get(
void)
501 return timer_time_get();
505nrf_802154_platform_sl_lptimer_us_to_lpticks_convert(uint64_t us,
bool round_up)
512nrf_802154_platform_sl_lptimer_lpticks_to_us_convert(uint64_t lpticks)
518nrf_802154_platform_sl_lptimer_schedule_at(uint64_t fire_lpticks)
520 uint32_t primask = irq_lock_local();
522 alarm_pending =
true;
523 alarm_target_lpticks = fire_lpticks;
524 (void)timer_compare_set_locked(ALARM_CC, fire_lpticks,
false);
526 if(critical_section_depth == 0U) {
527 timer_compare_int_enable(ALARM_CC);
528 if((force_isr_mask & FORCE_MASK_ALARM) != 0U) {
529 NVIC_SetPendingIRQ(LP_TIMER_IRQn);
533 irq_unlock_local(primask);
537nrf_802154_platform_sl_lptimer_disable(
void)
539 uint32_t primask = irq_lock_local();
541 alarm_pending =
false;
542 alarm_target_lpticks = 0;
543 force_isr_mask &= ~FORCE_MASK_ALARM;
544 timer_compare_int_disable(ALARM_CC);
545 timer_event_clear_cc(ALARM_CC);
547 irq_unlock_local(primask);
551nrf_802154_platform_sl_lptimer_critical_section_enter(
void)
553 uint32_t primask = irq_lock_local();
555 critical_section_depth++;
557 if(critical_section_depth == 1U) {
558 timer_compare_int_disable(ALARM_CC);
559 timer_compare_int_disable(SYNC_CC);
562 irq_unlock_local(primask);
566nrf_802154_platform_sl_lptimer_critical_section_exit(
void)
568 uint32_t primask = irq_lock_local();
570 if(critical_section_depth != 0U) {
571 critical_section_depth--;
574 if(critical_section_depth == 0U) {
576 timer_compare_int_enable(SYNC_CC);
577 if(timer_event_check_cc(SYNC_CC) || ((force_isr_mask & FORCE_MASK_SYNC) != 0U)) {
578 NVIC_SetPendingIRQ(LP_TIMER_IRQn);
583 timer_compare_int_enable(ALARM_CC);
584 if(timer_event_check_cc(ALARM_CC) || ((force_isr_mask & FORCE_MASK_ALARM) != 0U)) {
585 NVIC_SetPendingIRQ(LP_TIMER_IRQn);
590 irq_unlock_local(primask);
593nrf_802154_sl_lptimer_platform_result_t
594nrf_802154_platform_sl_lptimer_hw_task_prepare(uint64_t fire_lpticks,
595 uint32_t ppi_channel)
597 uint32_t primask = irq_lock_local();
598 uint64_t now = timer_time_get_locked();
601 if(!hw_task_state_set_locked(HW_TASK_STATE_IDLE, HW_TASK_STATE_SETTING_UP)) {
602 irq_unlock_local(primask);
603 return NRF_802154_SL_LPTIMER_PLATFORM_NO_RESOURCES;
606 hw_task_ppi_unbind_locked(hw_task_ppi_channel);
607 hw_task_ppi_channel = NRF_802154_SL_HW_TASK_PPI_INVALID;
608 hw_task_fire_lpticks = fire_lpticks;
609 timer_event_clear_cc(HW_TASK_CC);
611 if(target_is_too_distant(now, fire_lpticks)) {
612 hw_task_state = HW_TASK_STATE_IDLE;
613 irq_unlock_local(primask);
614 return NRF_802154_SL_LPTIMER_PLATFORM_TOO_DISTANT;
617 rc = timer_compare_set_locked(HW_TASK_CC, fire_lpticks,
true);
619 hw_task_state = HW_TASK_STATE_IDLE;
620 irq_unlock_local(primask);
621 return NRF_802154_SL_LPTIMER_PLATFORM_TOO_LATE;
624 if(hw_task_triggered_check_locked(timer_time_get_locked())) {
625 hw_task_state = HW_TASK_STATE_IDLE;
626 irq_unlock_local(primask);
627 return NRF_802154_SL_LPTIMER_PLATFORM_TOO_LATE;
630 hw_task_ppi_bind_locked(ppi_channel);
631 hw_task_ppi_channel = ppi_channel;
632 hw_task_state = HW_TASK_STATE_READY;
634 irq_unlock_local(primask);
636 return NRF_802154_SL_LPTIMER_PLATFORM_SUCCESS;
639nrf_802154_sl_lptimer_platform_result_t
640nrf_802154_platform_sl_lptimer_hw_task_cleanup(
void)
642 uint32_t primask = irq_lock_local();
644 if(!hw_task_state_set_locked(HW_TASK_STATE_READY, HW_TASK_STATE_CLEANING)) {
645 irq_unlock_local(primask);
646 return NRF_802154_SL_LPTIMER_PLATFORM_WRONG_STATE;
649 timer_event_clear_cc(HW_TASK_CC);
650 hw_task_ppi_unbind_locked(hw_task_ppi_channel);
651 hw_task_ppi_channel = NRF_802154_SL_HW_TASK_PPI_INVALID;
652 hw_task_state = HW_TASK_STATE_IDLE;
654 irq_unlock_local(primask);
656 return NRF_802154_SL_LPTIMER_PLATFORM_SUCCESS;
659nrf_802154_sl_lptimer_platform_result_t
660nrf_802154_platform_sl_lptimer_hw_task_update_ppi(uint32_t ppi_channel)
663 uint32_t primask = irq_lock_local();
665 if(!hw_task_state_set_locked(HW_TASK_STATE_READY, HW_TASK_STATE_UPDATING)) {
666 irq_unlock_local(primask);
667 return NRF_802154_SL_LPTIMER_PLATFORM_WRONG_STATE;
670 hw_task_ppi_unbind_locked(hw_task_ppi_channel);
671 hw_task_ppi_bind_locked(ppi_channel);
672 hw_task_ppi_channel = ppi_channel;
673 too_late = hw_task_triggered_check_locked(timer_time_get_locked());
674 hw_task_state = HW_TASK_STATE_READY;
676 irq_unlock_local(primask);
678 return too_late ? NRF_802154_SL_LPTIMER_PLATFORM_TOO_LATE :
679 NRF_802154_SL_LPTIMER_PLATFORM_SUCCESS;
683nrf_802154_platform_sl_lptimer_sync_schedule_now(
void)
685 uint32_t primask = irq_lock_local();
686 uint64_t now = timer_time_get_locked();
688 sync_schedule_locked(now + SYNC_MARGIN_TICKS);
690 irq_unlock_local(primask);
694nrf_802154_platform_sl_lptimer_sync_schedule_at(uint64_t fire_lpticks)
696 uint32_t primask = irq_lock_local();
698 sync_schedule_locked(fire_lpticks);
700 irq_unlock_local(primask);
704nrf_802154_platform_sl_lptimer_sync_abort(
void)
706 uint32_t primask = irq_lock_local();
708 sync_pending =
false;
709 force_isr_mask &= ~FORCE_MASK_SYNC;
710 timer_compare_int_disable(SYNC_CC);
711 timer_event_clear_cc(SYNC_CC);
713 irq_unlock_local(primask);
717nrf_802154_platform_sl_lptimer_sync_event_get(
void)
719 return timer_event_address_get(SYNC_CC);
723nrf_802154_platform_sl_lptimer_sync_lpticks_get(
void)
725 return sync_fire_lpticks;
729nrf_802154_platform_sl_lptimer_granularity_get(
void)
735nrf_802154_sl_timer_module_init(
void)
738 nrf_802154_platform_sl_lp_timer_init();
742nrf_802154_sl_timer_module_uninit(
void)
748nrf_802154_sl_timer_current_time_get(
void)
750 return timer_time_get();
754nrf_802154_sl_timer_init(nrf_802154_sl_timer_t *p_timer)
756 timer_next_set(p_timer, NULL);
757 timer_active_set(p_timer,
false);
761nrf_802154_sl_timer_deinit(nrf_802154_sl_timer_t *p_timer)
763 (void)nrf_802154_sl_timer_remove(p_timer);
766nrf_802154_sl_timer_ret_t
767nrf_802154_sl_timer_add(nrf_802154_sl_timer_t *p_timer)
769 uint32_t primask = irq_lock_local();
770 nrf_802154_sl_timer_t *prev = NULL;
771 nrf_802154_sl_timer_t *curr = alarm_head;
772 uint64_t now = timer_time_get_locked();
774 if(target_is_too_distant(now, p_timer->trigger_time)) {
775 irq_unlock_local(primask);
776 return NRF_802154_SL_TIMER_RET_TOO_DISTANT;
779 if(timer_is_active(p_timer)) {
780 (void)timer_remove_locked(p_timer);
783 while(curr != NULL && curr->trigger_time <= p_timer->trigger_time) {
785 curr = timer_next_get(curr);
788 timer_next_set(p_timer, curr);
789 timer_active_set(p_timer,
true);
792 alarm_head = p_timer;
793 alarm_reschedule_locked();
795 timer_next_set(prev, p_timer);
798 irq_unlock_local(primask);
800 return NRF_802154_SL_TIMER_RET_SUCCESS;
803nrf_802154_sl_timer_ret_t
804nrf_802154_sl_timer_remove(nrf_802154_sl_timer_t *p_timer)
806 nrf_802154_sl_timer_ret_t ret;
807 uint32_t primask = irq_lock_local();
809 ret = timer_remove_locked(p_timer);
811 irq_unlock_local(primask);
816nrf_802154_sl_timer_ret_t
817nrf_802154_sl_timer_update_ppi(nrf_802154_sl_timer_t *p_timer, uint32_t ppi_chn)
821 return NRF_802154_SL_TIMER_RET_SUCCESS;
825nrf_802154_timer_coord_init(
void)
830nrf_802154_timer_coord_uninit(
void)
835nrf_802154_timer_coord_start(
void)
840nrf_802154_timer_coord_stop(
void)
845nrf_802154_timer_coord_timestamp_prepare(
const nrf_802154_sl_event_handle_t *p_event)
851nrf_802154_timer_coord_timestamp_get(uint64_t *p_timestamp)