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"
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
80#define COUNTER_WRAP (UINT64_C(1) << 24)
81#define COUNTER_HALF_SPAN (UINT64_C(1) << 23)
85#define MIN_TICKS_FROM_NOW 3U
86#define SYNC_MARGIN_TICKS 3U
89#define US_NUM UINT64_C(15625)
90#define US_DEN UINT64_C(512)
92#define FORCE_MASK_ALARM (1UL << ALARM_CC)
93#define FORCE_MASK_SYNC (1UL << SYNC_CC)
95static volatile bool alarm_pending;
96static volatile bool sync_pending;
97static uint32_t critical_section_depth;
98static uint32_t force_isr_mask;
101static nrf_802154_sl_timer_t *alarm_head;
102static uint64_t alarm_target_lpticks;
103static uint64_t sync_fire_lpticks;
107 HW_TASK_STATE_SETTING_UP,
109 HW_TASK_STATE_UPDATING,
110 HW_TASK_STATE_CLEANING,
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;
117static bool timer_initialized;
118static uint64_t timer_time_upper;
119static uint32_t timer_last_low;
121static inline uint32_t
124 uint32_t primask = __get_PRIMASK();
133irq_unlock_local(uint32_t primask)
136 __set_PRIMASK(primask);
139static inline nrf_802154_sl_timer_t *
140timer_next_get(nrf_802154_sl_timer_t *
timer)
142 return (nrf_802154_sl_timer_t *)(uintptr_t)
timer->priv.placeholder[0];
146timer_next_set(nrf_802154_sl_timer_t *
timer, nrf_802154_sl_timer_t *next)
148 timer->priv.placeholder[0] = (uint64_t)(uintptr_t)next;
152timer_is_active(nrf_802154_sl_timer_t *
timer)
154 return timer->priv.placeholder[1] != 0U;
158timer_active_set(nrf_802154_sl_timer_t *
timer,
bool active)
160 timer->priv.placeholder[1] = active ? 1U : 0U;
163__attribute__((weak))
void
164nrf_802154_sl_timestamper_synchronized(
void)
169hw_task_state_set_locked(
enum hw_task_state expected,
enum hw_task_state new_state)
171 if(hw_task_state != expected) {
175 hw_task_state = new_state;
179static inline nrf_rtc_event_t
180compare_event(uint8_t cc_channel)
182 return NRF_RTC_CHANNEL_EVENT_ADDR(cc_channel);
185static inline uint32_t
186rtc_event_address_get(uint8_t cc_channel)
188 return nrf_rtc_event_address_get(LP_RTC, compare_event(cc_channel));
192rtc_event_check_cc(uint8_t cc_channel)
194 return nrf_rtc_event_check(LP_RTC, compare_event(cc_channel));
198rtc_event_clear_cc(uint8_t cc_channel)
200 nrf_rtc_event_clear(LP_RTC, compare_event(cc_channel));
203static inline uint32_t
204rtc_int_mask_get(uint8_t cc_channel)
206 return NRF_RTC_CHANNEL_INT_MASK(cc_channel);
210rtc_compare_int_enable(uint8_t cc_channel)
212 nrf_rtc_int_enable(LP_RTC, rtc_int_mask_get(cc_channel));
216rtc_compare_int_disable(uint8_t cc_channel)
218 nrf_rtc_int_disable(LP_RTC, rtc_int_mask_get(cc_channel));
221static inline uint64_t
222timer_time_get_locked(
void)
224 uint32_t low = nrf_rtc_counter_get(LP_RTC);
226 if(low < timer_last_low) {
227 timer_time_upper += COUNTER_WRAP;
230 timer_last_low = low;
233 return timer_time_upper + low;
239 uint32_t primask = irq_lock_local();
240 uint64_t now = timer_time_get_locked();
241 irq_unlock_local(primask);
246target_is_too_distant(uint64_t now, uint64_t target)
248 return (target > now) && ((target - now) > COUNTER_HALF_SPAN);
258timer_compare_set_locked(uint8_t cc_channel, uint64_t target,
bool exact)
260 uint64_t now = timer_time_get_locked();
263 if(target_is_too_distant(now, target)) {
272 force_isr_mask |= (1UL << cc_channel);
273 rtc_event_clear_cc(cc_channel);
277 min_target = now + MIN_TICKS_FROM_NOW;
278 if(target < min_target) {
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));
295 if(!exact && timer_time_get_locked() >= target) {
296 force_isr_mask |= (1UL << cc_channel);
303hw_task_triggered_check_locked(uint64_t now)
305 return rtc_event_check_cc(HW_TASK_CC) || now >= hw_task_fire_lpticks;
309hw_task_ppi_bind_locked(uint32_t ppi_channel)
311 if(ppi_channel == NRF_802154_SL_HW_TASK_PPI_INVALID) {
315 nrfx_gppi_event_endpoint_setup((uint8_t)ppi_channel,
316 rtc_event_address_get(HW_TASK_CC));
320hw_task_ppi_unbind_locked(uint32_t ppi_channel)
322 if(ppi_channel == NRF_802154_SL_HW_TASK_PPI_INVALID) {
326 nrfx_gppi_event_endpoint_clear((uint8_t)ppi_channel,
327 rtc_event_address_get(HW_TASK_CC));
338cc_enable_locked(uint8_t cc_channel, uint32_t force_mask)
340 if(critical_section_depth != 0U) {
344 rtc_compare_int_enable(cc_channel);
345 if((force_isr_mask & force_mask) != 0U) {
346 NVIC_SetPendingIRQ(LP_RTC_IRQn);
351alarm_reschedule_locked(
void)
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);
362 alarm_pending =
true;
363 alarm_target_lpticks = alarm_head->trigger_time;
365 (void)timer_compare_set_locked(ALARM_CC, alarm_target_lpticks,
false);
367 cc_enable_locked(ALARM_CC, FORCE_MASK_ALARM);
371sync_schedule_locked(uint64_t fire_lpticks)
374 sync_fire_lpticks = fire_lpticks;
376 (void)timer_compare_set_locked(SYNC_CC, sync_fire_lpticks,
false);
378 cc_enable_locked(SYNC_CC, FORCE_MASK_SYNC);
381static nrf_802154_sl_timer_ret_t
382timer_remove_locked(nrf_802154_sl_timer_t *
timer)
384 nrf_802154_sl_timer_t *prev = NULL;
385 nrf_802154_sl_timer_t *curr = alarm_head;
387 while(curr != NULL) {
389 nrf_802154_sl_timer_t *next = timer_next_get(curr);
394 timer_next_set(prev, next);
397 timer_next_set(curr, NULL);
398 timer_active_set(curr,
false);
399 alarm_reschedule_locked();
400 return NRF_802154_SL_TIMER_RET_SUCCESS;
404 curr = timer_next_get(curr);
407 return NRF_802154_SL_TIMER_RET_INACTIVE;
414 nrf_802154_sl_timer_t *
timer;
415 uint32_t primask = irq_lock_local();
416 uint64_t now = timer_time_get_locked();
421 alarm_pending =
false;
422 alarm_target_lpticks = 0;
423 irq_unlock_local(primask);
427 if(
timer->trigger_time > now) {
428 alarm_reschedule_locked();
429 irq_unlock_local(primask);
433 alarm_head = timer_next_get(
timer);
434 timer_next_set(
timer, NULL);
435 timer_active_set(
timer,
false);
436 alarm_reschedule_locked();
438 irq_unlock_local(primask);
440 if((
timer->action_type & NRF_802154_SL_TIMER_ACTION_TYPE_CALLBACK) &&
441 timer->action.callback.callback != NULL) {
448sync_process(uint64_t now)
450 if(sync_pending && now >= sync_fire_lpticks) {
451 sync_pending =
false;
452 nrf_802154_sl_timestamper_synchronized();
457LP_RTC_IRQHandler(
void)
459 uint32_t primask = irq_lock_local();
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();
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);
478 rtc_event_clear_cc(ALARM_CC);
481 rtc_event_clear_cc(SYNC_CC);
491 force_isr_mask &= ~((alarm_forced ? FORCE_MASK_ALARM : 0U) |
492 (sync_forced ? FORCE_MASK_SYNC : 0U));
494 irq_unlock_local(primask);
496 if(alarm_forced || alarm_event) {
500 if(sync_forced || sync_event) {
501 sync_process(timer_time_get());
506nrf_802154_platform_sl_lp_timer_init(
void)
510 primask = irq_lock_local();
512 alarm_pending =
false;
513 sync_pending =
false;
514 critical_section_depth = 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;
524 if(!timer_initialized) {
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);
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);
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);
542 NVIC_SetPriority(LP_RTC_IRQn, LP_RTC_IRQ_PRIORITY);
543 NVIC_ClearPendingIRQ(LP_RTC_IRQn);
544 NVIC_EnableIRQ(LP_RTC_IRQn);
546 timer_initialized =
true;
549 irq_unlock_local(primask);
553nrf_802154_platform_sl_lp_timer_deinit(
void)
555 uint32_t primask = irq_lock_local();
557 alarm_pending =
false;
558 sync_pending =
false;
559 critical_section_depth = 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;
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);
577 irq_unlock_local(primask);
581nrf_802154_platform_sl_lptimer_current_lpticks_get(
void)
583 return timer_time_get();
587nrf_802154_platform_sl_lptimer_us_to_lpticks_convert(uint64_t us,
bool round_up)
589 uint64_t numerator = us * US_DEN;
592 numerator += US_NUM - 1U;
595 return numerator / US_NUM;
599nrf_802154_platform_sl_lptimer_lpticks_to_us_convert(uint64_t lpticks)
601 return (lpticks * US_NUM) / US_DEN;
613nrf_802154_platform_sl_lptimer_schedule_at(uint64_t fire_lpticks)
615 uint32_t primask = irq_lock_local();
617 alarm_pending =
true;
618 alarm_target_lpticks = fire_lpticks;
619 (void)timer_compare_set_locked(ALARM_CC, fire_lpticks,
false);
621 cc_enable_locked(ALARM_CC, FORCE_MASK_ALARM);
623 irq_unlock_local(primask);
627nrf_802154_platform_sl_lptimer_disable(
void)
629 uint32_t primask = irq_lock_local();
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);
637 irq_unlock_local(primask);
641nrf_802154_platform_sl_lptimer_critical_section_enter(
void)
643 uint32_t primask = irq_lock_local();
645 critical_section_depth++;
647 if(critical_section_depth == 1U) {
648 rtc_compare_int_disable(ALARM_CC);
649 rtc_compare_int_disable(SYNC_CC);
652 irq_unlock_local(primask);
656nrf_802154_platform_sl_lptimer_critical_section_exit(
void)
658 uint32_t primask = irq_lock_local();
660 if(critical_section_depth != 0U) {
661 critical_section_depth--;
664 if(critical_section_depth == 0U) {
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);
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);
680 irq_unlock_local(primask);
683nrf_802154_sl_lptimer_platform_result_t
684nrf_802154_platform_sl_lptimer_hw_task_prepare(uint64_t fire_lpticks,
685 uint32_t ppi_channel)
687 uint32_t primask = irq_lock_local();
688 uint64_t now = timer_time_get_locked();
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;
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);
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;
707 rc = timer_compare_set_locked(HW_TASK_CC, fire_lpticks,
true);
709 hw_task_state = HW_TASK_STATE_IDLE;
710 irq_unlock_local(primask);
711 return NRF_802154_SL_LPTIMER_PLATFORM_TOO_LATE;
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;
720 hw_task_ppi_bind_locked(ppi_channel);
721 hw_task_ppi_channel = ppi_channel;
722 hw_task_state = HW_TASK_STATE_READY;
724 irq_unlock_local(primask);
726 return NRF_802154_SL_LPTIMER_PLATFORM_SUCCESS;
729nrf_802154_sl_lptimer_platform_result_t
730nrf_802154_platform_sl_lptimer_hw_task_cleanup(
void)
732 uint32_t primask = irq_lock_local();
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;
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;
744 irq_unlock_local(primask);
746 return NRF_802154_SL_LPTIMER_PLATFORM_SUCCESS;
749nrf_802154_sl_lptimer_platform_result_t
750nrf_802154_platform_sl_lptimer_hw_task_update_ppi(uint32_t ppi_channel)
753 uint32_t primask = irq_lock_local();
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;
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;
766 irq_unlock_local(primask);
768 return too_late ? NRF_802154_SL_LPTIMER_PLATFORM_TOO_LATE :
769 NRF_802154_SL_LPTIMER_PLATFORM_SUCCESS;
773nrf_802154_platform_sl_lptimer_sync_schedule_now(
void)
775 uint32_t primask = irq_lock_local();
776 uint64_t now = timer_time_get_locked();
778 sync_schedule_locked(now + SYNC_MARGIN_TICKS);
780 irq_unlock_local(primask);
784nrf_802154_platform_sl_lptimer_sync_schedule_at(uint64_t fire_lpticks)
786 uint32_t primask = irq_lock_local();
788 sync_schedule_locked(fire_lpticks);
790 irq_unlock_local(primask);
794nrf_802154_platform_sl_lptimer_sync_abort(
void)
796 uint32_t primask = irq_lock_local();
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);
803 irq_unlock_local(primask);
807nrf_802154_platform_sl_lptimer_sync_event_get(
void)
809 return rtc_event_address_get(SYNC_CC);
813nrf_802154_platform_sl_lptimer_sync_lpticks_get(
void)
815 return sync_fire_lpticks;
819nrf_802154_platform_sl_lptimer_granularity_get(
void)
822 return (uint32_t)((US_NUM + US_DEN - 1U) / US_DEN);
828nrf_802154_sl_timer_module_init(
void)
831 nrf_802154_platform_sl_lp_timer_init();
835nrf_802154_sl_timer_module_uninit(
void)
841nrf_802154_sl_timer_current_time_get(
void)
843 return timer_time_get();
847nrf_802154_sl_timer_init(nrf_802154_sl_timer_t *p_timer)
849 timer_next_set(p_timer, NULL);
850 timer_active_set(p_timer,
false);
854nrf_802154_sl_timer_deinit(nrf_802154_sl_timer_t *p_timer)
856 (void)nrf_802154_sl_timer_remove(p_timer);
859nrf_802154_sl_timer_ret_t
860nrf_802154_sl_timer_add(nrf_802154_sl_timer_t *p_timer)
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();
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;
872 if(timer_is_active(p_timer)) {
873 (void)timer_remove_locked(p_timer);
880 while(curr != NULL && curr->trigger_time <= p_timer->trigger_time) {
882 curr = timer_next_get(curr);
885 timer_next_set(p_timer, curr);
886 timer_active_set(p_timer,
true);
889 alarm_head = p_timer;
890 alarm_reschedule_locked();
892 timer_next_set(prev, p_timer);
895 irq_unlock_local(primask);
897 return NRF_802154_SL_TIMER_RET_SUCCESS;
900nrf_802154_sl_timer_ret_t
901nrf_802154_sl_timer_remove(nrf_802154_sl_timer_t *p_timer)
903 nrf_802154_sl_timer_ret_t ret;
904 uint32_t primask = irq_lock_local();
906 ret = timer_remove_locked(p_timer);
908 irq_unlock_local(primask);
913nrf_802154_sl_timer_ret_t
914nrf_802154_sl_timer_update_ppi(nrf_802154_sl_timer_t *p_timer, uint32_t ppi_chn)
918 return NRF_802154_SL_TIMER_RET_SUCCESS;
922nrf_802154_timer_coord_init(
void)
927nrf_802154_timer_coord_uninit(
void)
932nrf_802154_timer_coord_start(
void)
937nrf_802154_timer_coord_stop(
void)
942nrf_802154_timer_coord_timestamp_prepare(
const nrf_802154_sl_event_handle_t *p_event)
948nrf_802154_timer_coord_timestamp_get(uint64_t *p_timestamp)