From bd18ca5c7ac2dc8dd6fb5ccb9f430e966d13fc2a Mon Sep 17 00:00:00 2001 From: Artur Hadasz Date: Fri, 8 Sep 2023 15:53:59 +0200 Subject: [PATCH] modules: nrfxlib: nrf_802154: RTC0 used for HW Task Instead of using an RTC1 channel for hardware tasks triggering used by 802.15.4, an RTC0 channel is used. This frees an RTC1 CC channel, allowing it to be used by Zephyr for other purposes. Signed-off-by: Artur Hadasz (cherry picked from commit 9b18cb3ecd36b9075e46563baffd2146dec40eb5) --- .../nrf_802154_platform_sl_lptimer_zephyr.c | 178 ++++++++++++++---- 1 file changed, 145 insertions(+), 33 deletions(-) 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; }