nrf53: Add RTC pretick

Add RTC pretick option that triggers HW activity one tick before and
RTC event that leads to the interrupt. Option is active only on nrf53
network core.

Signed-off-by: Krzysztof Chruściński <krzysztof.chruscinski@nordicsemi.no>
This commit is contained in:
Krzysztof Chruściński 2023-06-23 09:06:51 +02:00 committed by Carles Cufí
commit 31eaffdf05
3 changed files with 201 additions and 1 deletions

View file

@ -25,6 +25,11 @@
#define RTC_LABEL rtc1
#define RTC_CH_COUNT RTC1_CC_NUM
#define RTC_PRETICK (IS_ENABLED(CONFIG_SOC_NRF53_RTC_PRETICK) && \
IS_ENABLED(CONFIG_SOC_NRF5340_CPUNET))
BUILD_ASSERT(!RTC_PRETICK || !(RTC_PRETICK && (CONFIG_NRF_RTC_TIMER_USER_CHAN_COUNT > 0)),
"Cannot use user channels when RTC pretick is used");
BUILD_ASSERT(CHAN_COUNT <= RTC_CH_COUNT, "Not enough compare channels");
/* Ensure that counter driver for RTC1 is not enabled. */
BUILD_ASSERT(DT_NODE_HAS_STATUS(DT_NODELABEL(RTC_LABEL), disabled),
@ -44,6 +49,9 @@ BUILD_ASSERT(DT_NODE_HAS_STATUS(DT_NODELABEL(RTC_LABEL), disabled),
#define ANCHOR_RANGE_END (7 * COUNTER_SPAN / 8)
#define TARGET_TIME_INVALID (UINT64_MAX)
extern void rtc_pretick_rtc1_cc0_set_hook(uint32_t val);
extern void rtc_pretick_rtc1_isr_hook(void);
static volatile uint32_t overflow_cnt;
static volatile uint64_t anchor;
static uint64_t last_count;
@ -260,7 +268,7 @@ static int set_alarm(int32_t chan, uint32_t req_cc, bool exact)
* This never happens when the written value is N+3. Use 3 cycles as
* the nearest possible scheduling then.
*/
enum { MIN_CYCLES_FROM_NOW = 3 };
enum { MIN_CYCLES_FROM_NOW = RTC_PRETICK ? 4 : 3 };
uint32_t cc_val = req_cc;
uint32_t cc_inc = MIN_CYCLES_FROM_NOW;
@ -277,6 +285,9 @@ static int set_alarm(int32_t chan, uint32_t req_cc, bool exact)
for (;;) {
uint32_t now;
if (RTC_PRETICK) {
rtc_pretick_rtc1_cc0_set_hook(cc_val);
}
set_comparator(chan, cc_val);
/* Enable event routing after the required CC value was set.
* Even though the above operation may get repeated (see below),
@ -559,6 +570,10 @@ void rtc_nrf_isr(const void *arg)
{
ARG_UNUSED(arg);
if (RTC_PRETICK) {
rtc_pretick_rtc1_isr_hook();
}
if (nrf_rtc_int_enable_check(RTC, NRF_RTC_INT_OVERFLOW_MASK) &&
nrf_rtc_event_check(RTC, NRF_RTC_EVENT_OVERFLOW)) {
nrf_rtc_event_clear(RTC, NRF_RTC_EVENT_OVERFLOW);

View file

@ -11,12 +11,14 @@ config SOC_NRF5340_CPUAPP
select ARMV8_M_DSP
select HAS_POWEROFF
select SOC_COMPATIBLE_NRF5340_CPUAPP
imply SOC_NRF53_RTC_PRETICK
config SOC_NRF5340_CPUNET
bool
select ARM_ON_EXIT_CPU_IDLE
select SOC_COMPATIBLE_NRF5340_CPUNET
imply SOC_NRF53_ANOMALY_160_WORKAROUND_NEEDED
imply SOC_NRF53_RTC_PRETICK
choice
prompt "nRF53x MCU Selection"
@ -49,6 +51,25 @@ config SOC_NRF53_ANOMALY_160_WORKAROUND
depends on SYS_CLOCK_EXISTS
select ARM_ON_ENTER_CPU_IDLE_HOOK
config SOC_NRF53_RTC_PRETICK
bool
depends on !NRF_802154_RADIO_DRIVER
select NRFX_DPPI
if SOC_NRF53_RTC_PRETICK
config SOC_NRF53_RTC_PRETICK_IPC_CH_FROM_NET
int "IPC 0 channel for RTC pretick"
range 0 15
default 10
config SOC_NRF53_RTC_PRETICK_IPC_CH_TO_NET
int "IPC 1 channel for RTC pretick"
range 0 15
default 11
endif
if SOC_NRF5340_CPUAPP
config SOC_DCDC_NRF53X_APP

View file

@ -18,6 +18,8 @@
#include <zephyr/logging/log.h>
#include <nrf_erratas.h>
#include <hal/nrf_power.h>
#include <hal/nrf_ipc.h>
#include <helpers/nrfx_gppi.h>
#if defined(CONFIG_SOC_NRF5340_CPUAPP)
#include <zephyr/drivers/gpio.h>
#include <zephyr/devicetree.h>
@ -31,6 +33,8 @@
#if defined(CONFIG_PM_S2RAM)
#include <hal/nrf_vmc.h>
#endif
#include <hal/nrf_wdt.h>
#include <hal/nrf_rtc.h>
#include <soc_secure.h>
#include <cmsis_core.h>
@ -38,6 +42,9 @@
#define PIN_XL1 0
#define PIN_XL2 1
#define RTC1_PRETICK_CC_CHAN 1
#define RTC1_PRETICK_OVERFLOW_CHAN 2
#if defined(CONFIG_SOC_NRF_GPIO_FORWARDER_FOR_NRF5340)
#define GPIOS_PSEL_BY_IDX(node_id, prop, idx) \
NRF_DT_GPIOS_TO_PSEL_BY_IDX(node_id, prop, idx),
@ -137,11 +144,164 @@ bool z_arm_on_enter_cpu_idle(void)
suppress_message = true;
}
#endif
#if defined(CONFIG_SOC_NRF53_RTC_PRETICK) && defined(CONFIG_SOC_NRF5340_CPUNET)
if (ok_to_sleep) {
NRF_IPC->PUBLISH_RECEIVE[CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_TO_NET] |=
IPC_PUBLISH_RECEIVE_EN_Msk;
if (!nrf_rtc_event_check(NRF_RTC0, NRF_RTC_CHANNEL_EVENT_ADDR(3)) &&
!nrf_rtc_event_check(NRF_RTC1, NRF_RTC_CHANNEL_EVENT_ADDR(RTC1_PRETICK_CC_CHAN)) &&
!nrf_rtc_event_check(NRF_RTC1, NRF_RTC_CHANNEL_EVENT_ADDR(RTC1_PRETICK_OVERFLOW_CHAN))) {
NRF_WDT->TASKS_STOP = 1;
/* Check if any event did not occur after we checked for
* stopping condition. If yes, we might have stopped WDT
* when it should be running. Restart it.
*/
if (nrf_rtc_event_check(NRF_RTC0, NRF_RTC_CHANNEL_EVENT_ADDR(3)) ||
nrf_rtc_event_check(NRF_RTC1, NRF_RTC_CHANNEL_EVENT_ADDR(RTC1_PRETICK_CC_CHAN)) ||
nrf_rtc_event_check(NRF_RTC1, NRF_RTC_CHANNEL_EVENT_ADDR(RTC1_PRETICK_OVERFLOW_CHAN))) {
NRF_WDT->TASKS_START = 1;
}
}
}
#endif
return ok_to_sleep;
}
#endif /* CONFIG_SOC_NRF53_ANOMALY_160_WORKAROUND */
#if CONFIG_SOC_NRF53_RTC_PRETICK
#ifdef CONFIG_SOC_NRF5340_CPUAPP
/* RTC pretick - application core part. */
static int rtc_pretick_cpuapp_init(void)
{
uint8_t ch;
nrfx_err_t err;
nrf_ipc_event_t ipc_event =
nrf_ipc_receive_event_get(CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_FROM_NET);
nrf_ipc_task_t ipc_task =
nrf_ipc_send_task_get(CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_TO_NET);
uint32_t task_ipc = nrf_ipc_task_address_get(NRF_IPC, ipc_task);
uint32_t evt_ipc = nrf_ipc_event_address_get(NRF_IPC, ipc_event);
err = nrfx_gppi_channel_alloc(&ch);
if (err != NRFX_SUCCESS) {
return -ENOMEM;
}
nrf_ipc_receive_config_set(NRF_IPC, CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_FROM_NET,
BIT(CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_FROM_NET));
nrf_ipc_send_config_set(NRF_IPC, CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_TO_NET,
BIT(CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_TO_NET));
nrfx_gppi_task_endpoint_setup(ch, task_ipc);
nrfx_gppi_event_endpoint_setup(ch, evt_ipc);
nrfx_gppi_channels_enable(BIT(ch));
return 0;
}
#else /* CONFIG_SOC_NRF5340_CPUNET */
static void rtc_pretick_rtc_isr_hook(void)
{
NRF_IPC->PUBLISH_RECEIVE[CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_TO_NET] &=
~IPC_PUBLISH_RECEIVE_EN_Msk;
}
void rtc_pretick_rtc0_isr_hook(void)
{
rtc_pretick_rtc_isr_hook();
}
void rtc_pretick_rtc1_cc0_set_hook(uint32_t val)
{
nrf_rtc_cc_set(NRF_RTC1, RTC1_PRETICK_CC_CHAN, val - 1);
}
void rtc_pretick_rtc1_isr_hook(void)
{
rtc_pretick_rtc_isr_hook();
if (nrf_rtc_event_check(NRF_RTC1, NRF_RTC_EVENT_OVERFLOW)) {
if (IS_ENABLED(CONFIG_SOC_NRF53_RTC_PRETICK)) {
nrf_rtc_event_clear(NRF_RTC1,
NRF_RTC_CHANNEL_EVENT_ADDR(RTC1_PRETICK_OVERFLOW_CHAN));
}
}
if (nrf_rtc_event_check(NRF_RTC1, NRF_RTC_EVENT_COMPARE_0)) {
if (IS_ENABLED(CONFIG_SOC_NRF53_RTC_PRETICK)) {
nrf_rtc_event_clear(NRF_RTC1, NRF_RTC_CHANNEL_EVENT_ADDR(RTC1_PRETICK_CC_CHAN));
}
}
}
static int rtc_pretick_cpunet_init(void)
{
uint8_t ppi_ch;
nrf_ipc_task_t ipc_task =
nrf_ipc_send_task_get(CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_FROM_NET);
nrf_ipc_event_t ipc_event =
nrf_ipc_receive_event_get(CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_TO_NET);
uint32_t task_ipc = nrf_ipc_task_address_get(NRF_IPC, ipc_task);
uint32_t evt_ipc = nrf_ipc_event_address_get(NRF_IPC, ipc_event);
uint32_t task_wdt = nrf_wdt_task_address_get(NRF_WDT, NRF_WDT_TASK_START);
uint32_t evt_mpsl_cc = nrf_rtc_event_address_get(NRF_RTC0, NRF_RTC_EVENT_COMPARE_3);
uint32_t evt_cc = nrf_rtc_event_address_get(NRF_RTC1,
NRF_RTC_CHANNEL_EVENT_ADDR(RTC1_PRETICK_CC_CHAN));
uint32_t evt_overflow = nrf_rtc_event_address_get(NRF_RTC1,
NRF_RTC_CHANNEL_EVENT_ADDR(RTC1_PRETICK_OVERFLOW_CHAN));
/* Configure Watchdog to allow stopping. */
nrf_wdt_behaviour_set(NRF_WDT, WDT_CONFIG_STOPEN_Msk | BIT(4));
*((volatile uint32_t *)0x41203120) = 0x14;
/* Configure IPC */
nrf_ipc_receive_config_set(NRF_IPC, CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_TO_NET,
BIT(CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_TO_NET));
nrf_ipc_send_config_set(NRF_IPC, CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_FROM_NET,
BIT(CONFIG_SOC_NRF53_RTC_PRETICK_IPC_CH_FROM_NET));
/* Allocate PPI channel for RTC Compare event publishers that starts WDT. */
nrfx_err_t err = nrfx_gppi_channel_alloc(&ppi_ch);
if (err != NRFX_SUCCESS) {
return -ENOMEM;
}
/* Setup a PPI connection between RTC "pretick" events and IPC task. */
if (IS_ENABLED(CONFIG_BT_LL_SOFTDEVICE)) {
nrfx_gppi_event_endpoint_setup(ppi_ch, evt_mpsl_cc);
}
nrfx_gppi_event_endpoint_setup(ppi_ch, evt_cc);
nrfx_gppi_event_endpoint_setup(ppi_ch, evt_overflow);
nrfx_gppi_task_endpoint_setup(ppi_ch, task_ipc);
nrfx_gppi_event_endpoint_setup(ppi_ch, evt_ipc);
nrfx_gppi_task_endpoint_setup(ppi_ch, task_wdt);
nrfx_gppi_channels_enable(BIT(ppi_ch));
nrf_rtc_event_enable(NRF_RTC1, NRF_RTC_CHANNEL_INT_MASK(RTC1_PRETICK_CC_CHAN));
nrf_rtc_event_enable(NRF_RTC1, NRF_RTC_CHANNEL_INT_MASK(RTC1_PRETICK_OVERFLOW_CHAN));
nrf_rtc_event_clear(NRF_RTC1, NRF_RTC_CHANNEL_EVENT_ADDR(RTC1_PRETICK_CC_CHAN));
nrf_rtc_event_clear(NRF_RTC1, NRF_RTC_CHANNEL_EVENT_ADDR(RTC1_PRETICK_OVERFLOW_CHAN));
/* Set event 1 tick before overflow. */
nrf_rtc_cc_set(NRF_RTC1, RTC1_PRETICK_OVERFLOW_CHAN, 0x00FFFFFF);
return 0;
}
#endif /* CONFIG_SOC_NRF5340_CPUNET */
static int rtc_pretick_init(void)
{
ARG_UNUSED(unused);
#ifdef CONFIG_SOC_NRF5340_CPUAPP
return rtc_pretick_cpuapp_init();
#else
return rtc_pretick_cpunet_init();
#endif
}
#endif /* CONFIG_SOC_NRF53_RTC_PRETICK */
static int nordicsemi_nrf53_init(void)
{
#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(CONFIG_NRF_ENABLE_CACHE)
@ -242,3 +402,7 @@ void arch_busy_wait(uint32_t time_us)
}
SYS_INIT(nordicsemi_nrf53_init, PRE_KERNEL_1, 0);
#ifdef CONFIG_SOC_NRF53_RTC_PRETICK
SYS_INIT(rtc_pretick_init, POST_KERNEL, 0);
#endif