diff --git a/drivers/timer/nrf_rtc_timer.c b/drivers/timer/nrf_rtc_timer.c index 1946451e31f..cd6a6aeab39 100644 --- a/drivers/timer/nrf_rtc_timer.c +++ b/drivers/timer/nrf_rtc_timer.c @@ -24,10 +24,62 @@ 1000000000UL) / 30517578125UL)) & 0x00FFFFFF) extern int64_t _sys_clock_tick_count; +extern int32_t _sys_idle_elapsed_ticks; +static uint32_t rtc_clock_tick_count; #ifdef CONFIG_TICKLESS_IDLE -extern int32_t _sys_idle_elapsed_ticks; +static uint8_t volatile isr_req; +static uint8_t isr_ack; +#endif /* CONFIG_TICKLESS_IDLE */ +static uint32_t rtc_compare_set(uint32_t rtc_ticks) +{ + uint32_t prev, cc, elapsed_ticks; + uint8_t retry = 10; + + prev = NRF_RTC1->COUNTER; + + do { + /* Assert if retries failed to set compare in the future */ + __ASSERT_NO_MSG(retry); + retry--; + + /* update with elapsed ticks from h/w */ + elapsed_ticks = (prev - rtc_clock_tick_count) & 0x00FFFFFF; + + /* setup next RTC compare event by ticks */ + cc = (rtc_clock_tick_count + elapsed_ticks + rtc_ticks) & + 0x00FFFFFF; + + NRF_RTC1->CC[0] = cc; + + prev = NRF_RTC1->COUNTER; + } while (((cc - prev) & 0x00FFFFFF) < 3); + +#ifdef CONFIG_TICKLESS_IDLE + /* If system clock ticks have elapsed, pend RTC IRQ which will + * call announce + */ + if (elapsed_ticks >= rtc_ticks) { + uint8_t req; + + /* pending the interrupt does not trigger the RTC event, hence + * use a request/ack mechanism to let the ISR know that the + * interrupt was requested + */ + req = isr_req + 1; + if (req != isr_ack) { + isr_req = req; + } + + _NvicIrqPend(NRF5_IRQ_RTC1_IRQn); + } +#endif /* CONFIG_TICKLESS_IDLE */ + + return elapsed_ticks; +} + +#ifdef CONFIG_TICKLESS_IDLE void _timer_idle_enter(int32_t ticks) { /* restrict ticks to max supported by RTC */ @@ -35,51 +87,59 @@ void _timer_idle_enter(int32_t ticks) ticks = 0x00FFFFFF / RTC_TICKS; } - /* setup next RTC compare event by ticks amount */ - NRF_RTC1->CC[0] = ((_sys_clock_tick_count + ticks) * RTC_TICKS) & - 0x00FFFFFF; - - /* TODO: check if CC is set to stale value */ + /* Postpone RTC compare event by requested system clock ticks */ + rtc_compare_set(ticks * RTC_TICKS); } void _timer_idle_exit(void) { - uint32_t elapsed_ticks; - - /* update with elapsed ticks from h/w */ - elapsed_ticks = ((NRF_RTC1->COUNTER - - (_sys_clock_tick_count * RTC_TICKS)) / - RTC_TICKS) & 0x00FFFFFF; - - /* setup next RTC compare event by 1 tick */ - NRF_RTC1->CC[0] = ((_sys_clock_tick_count + elapsed_ticks + 1) * - RTC_TICKS) & 0x00FFFFFF; - - /* TODO: check if CC is set to stale value */ + /* Advance RTC compare event to next system clock tick */ + rtc_compare_set(RTC_TICKS); } #endif /* CONFIG_TICKLESS_IDLE */ static void rtc1_nrf5_isr(void *arg) { +#ifdef CONFIG_TICKLESS_IDLE + uint8_t req; + + ARG_UNUSED(arg); + + req = isr_req; + /* iterate here since pending the interrupt can be done from higher + * priority, and thus queuing multiple triggers + */ + while (NRF_RTC1->EVENTS_COMPARE[0] || (req != isr_ack)) { + uint32_t elapsed_ticks; + + NRF_RTC1->EVENTS_COMPARE[0] = 0; + + if (req != isr_ack) { + isr_ack = req; + req = isr_req; + + elapsed_ticks = (NRF_RTC1->COUNTER - + rtc_clock_tick_count) + & 0x00FFFFFF; + } else { + elapsed_ticks = rtc_compare_set(RTC_TICKS); + } +#else ARG_UNUSED(arg); if (NRF_RTC1->EVENTS_COMPARE[0]) { + uint32_t elapsed_ticks; + NRF_RTC1->EVENTS_COMPARE[0] = 0; -#ifdef CONFIG_TICKLESS_IDLE - /* update with elapsed ticks from h/w */ - _sys_idle_elapsed_ticks = ((NRF_RTC1->COUNTER - - (_sys_clock_tick_count * - RTC_TICKS)) / RTC_TICKS) & - 0x00FFFFFF; + elapsed_ticks = rtc_compare_set(RTC_TICKS); #endif - /* setup next RTC compare event */ - NRF_RTC1->CC[0] = ((_sys_clock_tick_count + - _sys_idle_elapsed_ticks + 1) * RTC_TICKS) - & 0x00FFFFFF; + rtc_clock_tick_count += elapsed_ticks; + rtc_clock_tick_count &= 0x00FFFFFF; - /* TODO: check if CC is set to stale value */ + /* update with elapsed ticks from the hardware */ + _sys_idle_elapsed_ticks = elapsed_ticks / RTC_TICKS; _sys_clock_tick_announce(); }