diff --git a/modules/nrfxlib/nrf_802154/sl/platform/nrf_802154_platform_sl_lptimer_zephyr.c b/modules/nrfxlib/nrf_802154/sl/platform/nrf_802154_platform_sl_lptimer_zephyr.c index ffde377e6c24..a2a3fcf3682e 100644 --- a/modules/nrfxlib/nrf_802154/sl/platform/nrf_802154_platform_sl_lptimer_zephyr.c +++ b/modules/nrfxlib/nrf_802154/sl/platform/nrf_802154_platform_sl_lptimer_zephyr.c @@ -11,12 +11,28 @@ #include #include +#include +#include #include #include "platform/nrf_802154_clock.h" #include "nrf_802154_sl_utils.h" #define RTC_CHAN_INVALID (-1) +#if defined(CONFIG_SOC_NRF5340_CPUNET) + +BUILD_ASSERT(CONFIG_MPSL); + +#define HW_TASK_RTC NRF_RTC0 +#define HW_TASK_RTC_CHAN (3) + +#define RTC_COUNTER_BIT_WIDTH 24U +#define RTC_COUNTER_SPAN BIT(RTC_COUNTER_BIT_WIDTH) +#define RTC_COUNTER_MAX (RTC_COUNTER_SPAN - 1U) +#define RTC_COUNTER_HALF_SPAN (RTC_COUNTER_SPAN / 2U) + +#define RTC_MIN_CYCLES_FROM_NOW 3 +#endif struct timer_desc { z_nrf_rtc_timer_compare_handler_t handler; @@ -98,40 +114,152 @@ static void timer_start_at(struct timer_desc *timer, uint64_t target_time) nrf_802154_sl_mcu_critical_exit(state); } +#if defined(CONFIG_SOC_NRF5340_CPUNET) +static inline uint32_t rtc_counter_diff(uint32_t a, uint32_t b) +{ + return (a - b) & RTC_COUNTER_MAX; +} +#endif + static bool hw_task_state_set(enum hw_task_state_type expected_state, enum hw_task_state_type new_state) { return atomic_cas(&m_hw_task.state, expected_state, new_state); } -static void cc_bind_to_ppi(int32_t cc_num, uint32_t ppi_num) +static inline uint32_t hw_task_rtc_get_compare_evt_address(int32_t cc_num) +{ +#if defined(CONFIG_SOC_NRF5340_CPUNET) + return nrf_rtc_event_address_get(HW_TASK_RTC, nrf_rtc_compare_event_get(cc_num)); +#else + return z_nrf_rtc_timer_compare_evt_address_get(cc_num); +#endif +} + +static void hw_task_rtc_cc_bind_to_ppi(int32_t cc_num, uint32_t ppi_num) { - uint32_t event_address = z_nrf_rtc_timer_compare_evt_address_get(cc_num); + uint32_t event_address = hw_task_rtc_get_compare_evt_address(cc_num); nrfx_gppi_event_endpoint_setup(ppi_num, event_address); } -static void cc_unbind(int32_t cc_num, uint32_t ppi_num) +static void hw_task_rtc_cc_unbind(int32_t cc_num, uint32_t ppi_num) { if (ppi_num != NRF_802154_SL_HW_TASK_PPI_INVALID) { - uint32_t event_address = z_nrf_rtc_timer_compare_evt_address_get(cc_num); + uint32_t event_address = hw_task_rtc_get_compare_evt_address(cc_num); nrfx_gppi_event_endpoint_clear(ppi_num, event_address); } } -static bool cc_event_check(int32_t cc_num) +static bool hw_task_rtc_cc_event_check(int32_t cc_num) { - uint32_t event_address = z_nrf_rtc_timer_compare_evt_address_get(cc_num); + uint32_t event_address = hw_task_rtc_get_compare_evt_address(cc_num); return *(volatile uint32_t *)event_address; } +static void hw_task_rtc_timer_abort(void) +{ +#if defined(CONFIG_SOC_NRF5340_CPUNET) + /* No interrupt disabling/clearing is needed, as HW task does not use interrupts*/ + nrf_rtc_event_disable(HW_TASK_RTC, NRF_RTC_CHANNEL_INT_MASK(m_hw_task.chan)); + nrf_rtc_event_clear(HW_TASK_RTC, NRF_RTC_CHANNEL_EVENT_ADDR(m_hw_task.chan)); +#else + z_nrf_rtc_timer_abort(m_hw_task.chan); +#endif +} + +#if defined(CONFIG_SOC_NRF5340_CPUNET) +static uint32_t zephyr_rtc_ticks_to_hw_task_rtc_counter(uint64_t ticks) +{ + uint64_t zephyr_rtc_counter_now = 0; + uint32_t hw_task_rtc_counter_now = 0; + uint32_t hw_task_rtc_counter_now_2 = 0; + + /** + * The RTC0 counter is read twice - once before and once after reading RTC1. + * If the two reads are different, the procedure is repeated. + * This is to make sure that no RTC tick occurred between reading RTC0 and + * RTC1 and so the calculated difference between the timers is correct. + */ + do { + hw_task_rtc_counter_now = nrf_rtc_counter_get(HW_TASK_RTC); + zephyr_rtc_counter_now = z_nrf_rtc_timer_read(); + barrier_dmem_fence_full(); + hw_task_rtc_counter_now_2 = nrf_rtc_counter_get(HW_TASK_RTC); + + } while (hw_task_rtc_counter_now != hw_task_rtc_counter_now_2); + + /* This function can only be called in close proximity of ticks */ + assert(ticks >= zephyr_rtc_counter_now + || zephyr_rtc_counter_now - ticks < RTC_COUNTER_HALF_SPAN); + assert(ticks <= zephyr_rtc_counter_now + || ticks - zephyr_rtc_counter_now < RTC_COUNTER_HALF_SPAN); + + uint32_t diff = rtc_counter_diff(hw_task_rtc_counter_now, + (uint32_t) zephyr_rtc_counter_now % RTC_COUNTER_SPAN); + + return (uint32_t) ((ticks + diff) % RTC_COUNTER_SPAN); +} + +static int hw_task_rtc_counter_compare_set(uint32_t cc_value) +{ + uint32_t current_counter_value = nrf_rtc_counter_get(HW_TASK_RTC); + + /* As the HW tasks are setup only shortly before they are triggered, + * it is certain that the required fire time is not further away + * than one half of the RTC counter span. + * If it is it means that the trigger time is already in the past. + */ + if (rtc_counter_diff(cc_value, current_counter_value) > RTC_COUNTER_HALF_SPAN) { + return -EINVAL; + } + + nrf_rtc_event_disable(HW_TASK_RTC, NRF_RTC_CHANNEL_INT_MASK(m_hw_task.chan)); + nrf_rtc_event_clear(HW_TASK_RTC, NRF_RTC_CHANNEL_EVENT_ADDR(m_hw_task.chan)); + + nrf_rtc_cc_set(HW_TASK_RTC, m_hw_task.chan, cc_value); + + nrf_rtc_event_enable(HW_TASK_RTC, NRF_RTC_CHANNEL_INT_MASK(m_hw_task.chan)); + + current_counter_value = nrf_rtc_counter_get(HW_TASK_RTC); + + if (rtc_counter_diff(cc_value, current_counter_value + RTC_MIN_CYCLES_FROM_NOW) + > (RTC_COUNTER_HALF_SPAN - RTC_MIN_CYCLES_FROM_NOW)) { + nrf_rtc_event_disable(HW_TASK_RTC, NRF_RTC_CHANNEL_INT_MASK(m_hw_task.chan)); + nrf_rtc_event_clear(HW_TASK_RTC, NRF_RTC_CHANNEL_EVENT_ADDR(m_hw_task.chan)); + return -EINVAL; + } + + return 0; +} +#endif + +static int hw_task_rtc_timer_set(uint64_t fire_lpticks) +{ +#if defined(CONFIG_SOC_NRF5340_CPUNET) + uint32_t hw_task_rtc_counter_ticks = zephyr_rtc_ticks_to_hw_task_rtc_counter(fire_lpticks); + + return hw_task_rtc_counter_compare_set(hw_task_rtc_counter_ticks); +#else + return z_nrf_rtc_timer_exact_set(m_hw_task.chan, fire_lpticks, NULL, NULL); +#endif +} + void nrf_802154_platform_sl_lp_timer_init(void) { m_in_critical_section = false; m_hw_task.state = HW_TASK_STATE_IDLE; - m_hw_task.chan = RTC_CHAN_INVALID; +#if defined(CONFIG_SOC_NRF5340_CPUNET) + m_hw_task.chan = HW_TASK_RTC_CHAN; +#else + m_hw_task.chan = z_nrf_rtc_timer_chan_alloc(); + if (m_hw_task.chan < 0) { + assert(false); + return; + } +#endif m_hw_task.ppi = NRF_802154_SL_HW_TASK_PPI_INVALID; m_timer.handler = timer_handler; m_sync_timer.handler = sync_timer_handler; @@ -159,7 +287,9 @@ void nrf_802154_platform_sl_lp_timer_deinit(void) z_nrf_rtc_timer_chan_free(m_sync_timer.chan); if (m_hw_task.chan != RTC_CHAN_INVALID) { +#if !defined(CONFIG_SOC_NRF5340_CPUNET) z_nrf_rtc_timer_chan_free(m_hw_task.chan); +#endif m_hw_task.chan = RTC_CHAN_INVALID; } } @@ -270,25 +400,7 @@ nrf_802154_sl_lptimer_platform_result_t nrf_802154_platform_sl_lptimer_hw_task_p return NRF_802154_SL_LPTIMER_PLATFORM_NO_RESOURCES; } - /* Allocate the channel if not already done. */ - if (m_hw_task.chan == RTC_CHAN_INVALID) { - /* The channel allocation cannot take place during module - * initialization, because for nRF53 it would run - * out of resources - some other module temporarily needs - * an rtc channel to initialize. For this reason, this is where - * the allocation is made. - * Once a channel has been successfully allocated, it will be held - * until nrf_802154_platform_sl_lp_timer_deinit is called. - */ - m_hw_task.chan = z_nrf_rtc_timer_chan_alloc(); - if (m_hw_task.chan < 0) { - m_hw_task.chan = RTC_CHAN_INVALID; - hw_task_state_set(HW_TASK_STATE_SETTING_UP, HW_TASK_STATE_IDLE); - return NRF_802154_SL_LPTIMER_PLATFORM_NO_RESOURCES; - } - } - - if (z_nrf_rtc_timer_exact_set(m_hw_task.chan, fire_lpticks, NULL, NULL) != 0) { + if (hw_task_rtc_timer_set(fire_lpticks) != 0) { hw_task_state_set(HW_TASK_STATE_SETTING_UP, HW_TASK_STATE_IDLE); uint64_t now = z_nrf_rtc_timer_read(); @@ -299,7 +411,7 @@ nrf_802154_sl_lptimer_platform_result_t nrf_802154_platform_sl_lptimer_hw_task_p return NRF_802154_SL_LPTIMER_PLATFORM_TOO_LATE; } - evt_address = z_nrf_rtc_timer_compare_evt_address_get(m_hw_task.chan); + evt_address = hw_task_rtc_get_compare_evt_address(m_hw_task.chan); nrf_802154_sl_mcu_critical_enter(mcu_cs_state); @@ -307,9 +419,9 @@ nrf_802154_sl_lptimer_platform_result_t nrf_802154_platform_sl_lptimer_hw_task_p if ((z_nrf_rtc_timer_read() + 2) > fire_lpticks) { /* it is too late */ nrf_802154_sl_mcu_critical_exit(mcu_cs_state); - cc_unbind(m_hw_task.chan, ppi_channel); + hw_task_rtc_cc_unbind(m_hw_task.chan, ppi_channel); m_hw_task.ppi = NRF_802154_SL_HW_TASK_PPI_INVALID; - z_nrf_rtc_timer_abort(m_hw_task.chan); + hw_task_rtc_timer_abort(); hw_task_state_set(HW_TASK_STATE_SETTING_UP, HW_TASK_STATE_IDLE); return NRF_802154_SL_LPTIMER_PLATFORM_TOO_LATE; } @@ -330,9 +442,9 @@ nrf_802154_sl_lptimer_platform_result_t nrf_802154_platform_sl_lptimer_hw_task_c return NRF_802154_SL_LPTIMER_PLATFORM_WRONG_STATE; } - z_nrf_rtc_timer_abort(m_hw_task.chan); + hw_task_rtc_timer_abort(); - cc_unbind(m_hw_task.chan, m_hw_task.ppi); + hw_task_rtc_cc_unbind(m_hw_task.chan, m_hw_task.ppi); m_hw_task.ppi = NRF_802154_SL_HW_TASK_PPI_INVALID; hw_task_state_set(HW_TASK_STATE_CLEANING, HW_TASK_STATE_IDLE); @@ -353,10 +465,10 @@ nrf_802154_sl_lptimer_platform_result_t nrf_802154_platform_sl_lptimer_hw_task_u nrf_802154_sl_mcu_critical_enter(mcu_cs_state); - cc_bind_to_ppi(m_hw_task.chan, ppi_channel); + hw_task_rtc_cc_bind_to_ppi(m_hw_task.chan, ppi_channel); m_hw_task.ppi = ppi_channel; - cc_triggered = cc_event_check(m_hw_task.chan); + cc_triggered = hw_task_rtc_cc_event_check(m_hw_task.chan); if (z_nrf_rtc_timer_read() >= m_hw_task.fire_lpticks) { cc_triggered = true; }