diff --git a/arch/arm/src/nrf53/Kconfig b/arch/arm/src/nrf53/Kconfig index b4b8e07c7a..4cf7b88105 100644 --- a/arch/arm/src/nrf53/Kconfig +++ b/arch/arm/src/nrf53/Kconfig @@ -248,8 +248,29 @@ config NRF53_SYSTIMER_SYSTICK you choose this option, WFE/WFI will not be used in idle loop. +config NRF53_SYSTIMER_RTC + bool "RTC" + select NRF53_RTC + select SCHED_TICKLESS + select SCHED_TICKLESS_ALARM + select NRF53_USE_LFCLK + ---help--- + Use RTC timer in tickless mode. + endchoice +if NRF53_SYSTIMER_RTC + +config NRF53_SYSTIMER_RTC_INSTANCE + int "RTC timer instance" + default 0 if !NRF53_SOFTDEVICE_CONTROLLER + default 1 if NRF53_SOFTDEVICE_CONTROLLER + range 0 1 + ---help--- + Which RTC instance to use to drive the system timer + +endif + endmenu # System Timer menu "PWM configuration" diff --git a/arch/arm/src/nrf53/Make.defs b/arch/arm/src/nrf53/Make.defs index 81f78f16e8..77b2916452 100644 --- a/arch/arm/src/nrf53/Make.defs +++ b/arch/arm/src/nrf53/Make.defs @@ -22,7 +22,12 @@ include armv8-m/Make.defs ifeq ($(CONFIG_NRF53_SYSTIMER_SYSTICK),y) CHIP_CSRCS += nrf53_systick.c +else +ifeq ($(CONFIG_NRF53_SYSTIMER_RTC),y) +CHIP_CSRCS += nrf53_tickless_rtc.c endif +endif + CHIP_CSRCS += nrf53_start.c nrf53_clockconfig.c nrf53_irq.c nrf53_utils.c CHIP_CSRCS += nrf53_allocateheap.c nrf53_lowputc.c nrf53_gpio.c CHIP_CSRCS += nrf53_uid.c diff --git a/arch/arm/src/nrf53/nrf53_tickless_rtc.c b/arch/arm/src/nrf53/nrf53_tickless_rtc.c new file mode 100644 index 0000000000..418fee3288 --- /dev/null +++ b/arch/arm/src/nrf53/nrf53_tickless_rtc.c @@ -0,0 +1,342 @@ +/**************************************************************************** + * arch/arm/src/nrf53/nrf53_tickless_rtc.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "arm_internal.h" +#include "hardware/nrf53_rtc.h" +#include "nrf53_rtc.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Check configuration */ + +#ifdef CONFIG_TIMER_ARCH +# error CONFIG_TIMER_ARCH must be not set +#endif + +/* Check corresponding RTC support */ + +#if (CONFIG_NRF53_SYSTIMER_RTC_INSTANCE == 0) && !defined(CONFIG_NRF53_RTC0) +# error "Support for RTC0 is not enabled" +#elif (CONFIG_NRF53_SYSTIMER_RTC_INSTANCE == 1) && !defined(CONFIG_NRF53_RTC1) +# error "Support for RTC1 is not enabled" +#endif + +#define NRF53_RTC_PERIOD (512) +#define NRF53_RTC_MAX (0x00ffffff) +#define NRF53_RTC_MAX_TIME (NRF53_RTC_MAX * 31) + +/* Convert uS to timer count for f = 32768Hz, using more precision + * when possible: + * (1 / 32768) s ~ 30.51 uS ~ 31 uS + * 512 * (1 / 32768) s = 0.015625 s = 15625 uS + * So, instead of always dividing by 31, if t * 512 < (2**32 - 1), we can do: + * (t * 512) / 15625 ~ t / 30.51 + */ + +#define USEC_TO_COUNTER(t) (t > 0x7fffff ? (t / 31) : ((t * 512) / 15625)) + +/* To convert from counter to uS we split the counter into one second worth + * of counts (32768) and a fractional part we can safely multiply first + * by (USEC_PER_SEC/8) and still be within 32 bit value + */ + +#define COUNTER_TO_USEC(c) ((c / 32768) * USEC_PER_SEC) + \ + (((c % 32768) * (USEC_PER_SEC / 8)) / (32768 / 8)) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct nrf53_tickless_dev_s +{ + struct nrf53_rtc_dev_s *rtc; /* nrf53 RTC driver */ + uint32_t periods; /* how many times the timer overflowed */ + bool alarm_set; /* is the alarm set? */ + struct timespec alarm; /* absolute time of alarm */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int rtc_handler(int irq, void *context, void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +struct nrf53_tickless_dev_s g_tickless_dev; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static inline void rtc_counter_to_ts(uint32_t counter, struct timespec *now) +{ + uint32_t usec; + + usec = COUNTER_TO_USEC(counter); + now->tv_sec = usec / USEC_PER_SEC; + now->tv_nsec = (usec % USEC_PER_SEC) * NSEC_PER_USEC; + + now->tv_sec += g_tickless_dev.periods * NRF53_RTC_PERIOD; +} + +static void rtc_prepare_alarm(void) +{ + struct timespec now; + struct timespec delta; + uint32_t usec; + uint32_t counter; + uint32_t target_counter; + const uint32_t rtc_base = NRF53_RTC_GETBASE(g_tickless_dev.rtc); + + /* Get current absolute time */ + + counter = NRF53_RTC_GETCOUNTER_REG(rtc_base); + rtc_counter_to_ts(counter, &now); + + /* Obtain relative time to alarm */ + + clock_timespec_subtract(&g_tickless_dev.alarm, &now, &delta); + usec = delta.tv_sec * USEC_PER_SEC + delta.tv_nsec / NSEC_PER_USEC; + + /* Check if the alarm is to expire within one RTC period, if so we can set + * the CC. + */ + + if (usec < NRF53_RTC_PERIOD * USEC_PER_SEC) + { + /* Obtain absolute number of microseconds of alarm within current + * RTC period. + */ + + usec = (g_tickless_dev.alarm.tv_sec % NRF53_RTC_PERIOD) * + USEC_PER_SEC + g_tickless_dev.alarm.tv_nsec / NSEC_PER_USEC; + + /* Compute counter value for that point in time */ + + target_counter = USEC_TO_COUNTER(usec); + + /* Enable interrupt. First set CC to distant value to ensure + * no match will be generated. Doing things this way we now that + * once we write a CC value it should be ready to match. + */ + + NRF53_RTC_SETCC_REG(rtc_base, 0, counter - 1); + NRF53_RTC_ENABLEINT(g_tickless_dev.rtc, NRF53_RTC_EVT_COMPARE0); + + /* Set CC to desired value */ + + NRF53_RTC_SETCC_REG(rtc_base, 0, target_counter); + + /* Ensure counter fires: from nrf53832_PS_v1.4 (p. 245) we know that + * "If the COUNTER is N, writing N+2 to a CC register is + * guaranteed to trigger a COMPARE event at N+2.", so anything + * less than that is not guaranteed and it may not ever match. + * + * To ensure this, we check if CC < N + 2, and if so we set CC = N+2. + * We repeat this until this is satisfied (as the counter may change + * in between calculations). + */ + + while (NRF53_RTC_GETCC_REG(rtc_base, 0) < + NRF53_RTC_GETCOUNTER_REG(rtc_base) + 2) + { + NRF53_RTC_SETCC_REG(rtc_base, 0, + NRF53_RTC_GETCOUNTER_REG(rtc_base) + 2); + } + } +} + +/**************************************************************************** + * Name: rtc_handler + ****************************************************************************/ + +static int rtc_handler(int irq, void *context, void *arg) +{ + irqstate_t flags; + + flags = enter_critical_section(); + + /* if the timer wrapped-around */ + + if (NRF53_RTC_CHECKINT(g_tickless_dev.rtc, NRF53_RTC_EVT_OVRFLW)) + { + /* ack interrupt */ + + NRF53_RTC_ACKINT(g_tickless_dev.rtc, NRF53_RTC_EVT_OVRFLW); + + /* count one more period */ + + g_tickless_dev.periods++; + + /* check if the currently set alarm is to fire in this new period */ + + if (g_tickless_dev.alarm_set) + { + rtc_prepare_alarm(); + } + } + + /* if the compare event fired */ + + if (NRF53_RTC_CHECKINT(g_tickless_dev.rtc, NRF53_RTC_EVT_COMPARE0)) + { + struct timespec now; + + /* cancel alarm and get current time */ + + up_alarm_cancel(&now); + + /* let scheduler now of alarm firing */ + + nxsched_alarm_expiration(&now); + } + + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_alarm_cancel + ****************************************************************************/ + +int up_alarm_cancel(struct timespec *ts) +{ + uint32_t counter; + irqstate_t flags; + + flags = enter_critical_section(); + + NRF53_RTC_DISABLEINT(g_tickless_dev.rtc, NRF53_RTC_EVT_COMPARE0); + NRF53_RTC_GETCOUNTER(g_tickless_dev.rtc, &counter); + rtc_counter_to_ts(counter, ts); + + NRF53_RTC_ACKINT(g_tickless_dev.rtc, NRF53_RTC_EVT_COMPARE0); + g_tickless_dev.alarm_set = false; + + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Name: up_alarm_start + ****************************************************************************/ + +int up_alarm_start(const struct timespec *ts) +{ + irqstate_t flags; + flags = enter_critical_section(); + + /* remember the alarm time */ + + g_tickless_dev.alarm_set = true; + g_tickless_dev.alarm = *ts; + + rtc_prepare_alarm(); + + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Name: up_timer_gettime + ****************************************************************************/ + +int up_timer_gettime(struct timespec *ts) +{ + uint32_t counter; + irqstate_t flags; + + flags = enter_critical_section(); + + NRF53_RTC_GETCOUNTER(g_tickless_dev.rtc, &counter); + rtc_counter_to_ts(counter, ts); + + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Name: up_timer_initialize + ****************************************************************************/ + +void up_timer_initialize(void) +{ + struct timespec ts; + + memset(&g_tickless_dev, 0, sizeof(struct nrf53_tickless_dev_s)); + + g_tickless_dev.rtc = nrf53_rtc_init(CONFIG_NRF53_SYSTIMER_RTC_INSTANCE); + + /* Ensure we have support for the selected RTC instance */ + + ASSERT(g_tickless_dev.rtc); + + /* Configure prescaler */ + + NRF53_RTC_SETPRE(g_tickless_dev.rtc, 0); + + /* Configure ISR */ + + NRF53_RTC_SETISR(g_tickless_dev.rtc, rtc_handler, NULL); + + /* Enable overflow interrupt */ + + NRF53_RTC_ENABLEINT(g_tickless_dev.rtc, NRF53_RTC_EVT_OVRFLW); + + /* Start counting */ + + NRF53_RTC_CLEAR(g_tickless_dev.rtc); + NRF53_RTC_ACKINT(g_tickless_dev.rtc, NRF53_RTC_EVT_OVRFLW); + NRF53_RTC_ACKINT(g_tickless_dev.rtc, NRF53_RTC_EVT_COMPARE0); + NRF53_RTC_START(g_tickless_dev.rtc); + + /* kick off alarm scheduling */ + + ts.tv_sec = ts.tv_nsec = 0; + nxsched_alarm_expiration(&ts); +}