From 0942bae6ef5b2cdaefe97483dd8f04a797b2a6a7 Mon Sep 17 00:00:00 2001 From: raiden00pl Date: Sun, 20 Sep 2026 16:18:49 +0200 Subject: [PATCH] arm/nrf54l: add GRTC and tickless scheduling arm/nrf54l: add GRTC and tickless scheduling Assisted-by: Codex:GPT-6 Signed-off-by: raiden00pl --- Documentation/platforms/arm/nrf54l/index.rst | 16 +- arch/arm/src/nrf54l/CMakeLists.txt | 6 + arch/arm/src/nrf54l/Kconfig | 39 ++ arch/arm/src/nrf54l/Make.defs | 8 + arch/arm/src/nrf54l/hardware/nrf54l_grtc.h | 181 ++++++ arch/arm/src/nrf54l/nrf54l_clockconfig.c | 26 + arch/arm/src/nrf54l/nrf54l_grtc.c | 618 +++++++++++++++++++ arch/arm/src/nrf54l/nrf54l_grtc.h | 140 +++++ arch/arm/src/nrf54l/nrf54l_idle.c | 9 +- arch/arm/src/nrf54l/nrf54l_tickless_grtc.c | 282 +++++++++ 10 files changed, 1321 insertions(+), 4 deletions(-) create mode 100644 arch/arm/src/nrf54l/hardware/nrf54l_grtc.h create mode 100644 arch/arm/src/nrf54l/nrf54l_grtc.c create mode 100644 arch/arm/src/nrf54l/nrf54l_grtc.h create mode 100644 arch/arm/src/nrf54l/nrf54l_tickless_grtc.c diff --git a/Documentation/platforms/arm/nrf54l/index.rst b/Documentation/platforms/arm/nrf54l/index.rst index d4fc86c73fe..1be3d0a9ee3 100644 --- a/Documentation/platforms/arm/nrf54l/index.rst +++ b/Documentation/platforms/arm/nrf54l/index.rst @@ -4,7 +4,7 @@ Nordic nRF54L The nRF54L series from Nordic Semiconductor is based on an ARM Cortex-M33 application core. NuttX supports nRF54L15 and nRF54LM20A/B as standalone -secure images, with a 128 MHz CPU clock and SysTick scheduling. +secure images, with a 128 MHz CPU clock and SysTick or GRTC scheduling. Peripheral Support ================== @@ -16,7 +16,7 @@ Peripheral Support Notes ========== ======= ===================================== GPIO Yes GPIOTE No -GRTC No +GRTC Yes Counter and tickless scheduling PWM No QDEC No RADIO No @@ -35,6 +35,15 @@ GPIO Pins can be configured and operated using ``nrf54l_gpio_*`` functions. +GRTC +---- + +``nrf54l_grtc_init(0)`` provides access to the 52-bit, 1 MHz system counter +and twelve compare channels. ``CONFIG_NRF54L_SYSTIMER_GRTC`` reserves the +instance and compare channel zero for tickless scheduling. The GRTC is timed +by LFCLK, so the LFCLK source selected with ``CONFIG_NRF54L_USE_LFCLK`` +determines the long-term accuracy of the system time. + TIMER ----- @@ -59,6 +68,7 @@ being transmitted or DMA error recovery is in progress. Power Management ================ -With SysTick selected, the CPU remains awake so its clock keeps running. +The idle loop uses ``WFI`` when GRTC provides tickless scheduling. With +SysTick selected, the CPU remains awake so its clock keeps running. ``CONFIG_PM`` initializes the NuttX power management framework. System OFF is not supported. diff --git a/arch/arm/src/nrf54l/CMakeLists.txt b/arch/arm/src/nrf54l/CMakeLists.txt index 3d466aa4741..484ffbcecbe 100644 --- a/arch/arm/src/nrf54l/CMakeLists.txt +++ b/arch/arm/src/nrf54l/CMakeLists.txt @@ -33,6 +33,12 @@ set(SRCS if(CONFIG_NRF54L_SYSTIMER_SYSTICK) list(APPEND SRCS nrf54l_systick.c) endif() +if(CONFIG_NRF54L_GRTC) + list(APPEND SRCS nrf54l_grtc.c) +endif() +if(CONFIG_NRF54L_SYSTIMER_GRTC) + list(APPEND SRCS nrf54l_tickless_grtc.c) +endif() if(CONFIG_NRF54L_UART) list(APPEND SRCS nrf54l_serial.c) endif() diff --git a/arch/arm/src/nrf54l/Kconfig b/arch/arm/src/nrf54l/Kconfig index 886fc9921f5..b2dec2687eb 100644 --- a/arch/arm/src/nrf54l/Kconfig +++ b/arch/arm/src/nrf54l/Kconfig @@ -135,8 +135,37 @@ config NRF54L_TIMER6 select NRF54L_TIMER default n +config NRF54L_GRTC + bool "GRTC" + default n + endmenu # nRF54L Peripheral Selection +config NRF54L_USE_LFCLK + bool "Enable LFCLK" + default n + ---help--- + Enable low-frequency clock. + +if NRF54L_USE_LFCLK + +choice + prompt "LFCLK source" + default NRF54L_LFCLK_XTAL + +config NRF54L_LFCLK_XTAL + bool "External 32.768Khz crystal" + +config NRF54L_LFCLK_RC + bool "Internal RC oscillator" + +config NRF54L_LFCLK_SYNTH + bool "Synthesized from HFCLK" + +endchoice # LFCLK source + +endif # NRF54L_USE_LFCLK + menu "System Timer" choice @@ -149,6 +178,16 @@ config NRF54L_SYSTIMER_SYSTICK select TIMER_ARCH select ARMV8M_SYSTICK +config NRF54L_SYSTIMER_GRTC + bool "GRTC" + select NRF54L_GRTC + select ARCH_HAVE_TICKLESS + select SCHED_TICKLESS + select SCHED_TICKLESS_ALARM + select NRF54L_USE_LFCLK + ---help--- + Use GRTC timer in tickless mode. + endchoice # System Timer Source endmenu # System Timer diff --git a/arch/arm/src/nrf54l/Make.defs b/arch/arm/src/nrf54l/Make.defs index 48ff25988a7..3b45c3e33bd 100644 --- a/arch/arm/src/nrf54l/Make.defs +++ b/arch/arm/src/nrf54l/Make.defs @@ -30,6 +30,14 @@ ifeq ($(CONFIG_NRF54L_SYSTIMER_SYSTICK),y) CHIP_CSRCS += nrf54l_systick.c endif +ifeq ($(CONFIG_NRF54L_GRTC),y) +CHIP_CSRCS += nrf54l_grtc.c +endif + +ifeq ($(CONFIG_NRF54L_SYSTIMER_GRTC),y) +CHIP_CSRCS += nrf54l_tickless_grtc.c +endif + ifeq ($(CONFIG_NRF54L_UART),y) CHIP_CSRCS += nrf54l_serial.c endif diff --git a/arch/arm/src/nrf54l/hardware/nrf54l_grtc.h b/arch/arm/src/nrf54l/hardware/nrf54l_grtc.h new file mode 100644 index 00000000000..ddf5014d33f --- /dev/null +++ b/arch/arm/src/nrf54l/hardware/nrf54l_grtc.h @@ -0,0 +1,181 @@ +/**************************************************************************** + * arch/arm/src/nrf54l/hardware/nrf54l_grtc.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_GRTC_H +#define __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_GRTC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include "hardware/nrf54l_memorymap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register offsets *********************************************************/ + +#define NRF54L_GRTC_TASKS_CAPTURE_OFFSET(n) (0x0000 + 4 * (n)) /* Capture counter */ +#define NRF54L_GRTC_TASKS_START_OFFSET 0x0060 /* Start counter */ +#define NRF54L_GRTC_TASKS_STOP_OFFSET 0x0064 /* Stop counter */ +#define NRF54L_GRTC_TASKS_CLEAR_OFFSET 0x0068 /* Clear counter */ +#define NRF54L_GRTC_TASKS_PWMSTART_OFFSET 0x006c /* Start PWM */ +#define NRF54L_GRTC_TASKS_PWMSTOP_OFFSET 0x0070 /* Stop PWM */ +#define NRF54L_GRTC_SUBSCRIBE_CAPTURE_OFFSET(n) (0x0080 + 4 * (n)) /* Capture subscription */ +#define NRF54L_GRTC_EVENTS_COMPARE_OFFSET(n) (0x0100 + 4 * (n)) /* Compare match */ +#define NRF54L_GRTC_EVENTS_RTCOMPARESYNC_OFFSET 0x0164 /* LF timer synchronized */ +#define NRF54L_GRTC_EVENTS_PWMPERIODEND_OFFSET 0x016c /* PWM period ended */ +#define NRF54L_GRTC_EVENTS_PWMREADY_OFFSET 0x0174 /* PWM ready */ +#define NRF54L_GRTC_EVENTS_CLKOUTREADY_OFFSET 0x0178 /* Clock output ready */ +#define NRF54L_GRTC_PUBLISH_COMPARE_OFFSET(n) (0x0180 + 4 * (n)) /* Compare publication */ +#define NRF54L_GRTC_PUBLISH_PWMREADY_OFFSET 0x01f4 /* PWM ready publication */ +#define NRF54L_GRTC_PUBLISH_CLKOUTREADY_OFFSET 0x01f8 /* Clock ready publication */ +#define NRF54L_GRTC_SHORTS_OFFSET 0x0200 /* Local shortcuts */ +#define NRF54L_GRTC_INTEN_OFFSET(n) (0x0300 + 16 * (n)) /* Interrupt enable */ +#define NRF54L_GRTC_INTENSET_OFFSET(n) (0x0304 + 16 * (n)) /* Enable interrupt */ +#define NRF54L_GRTC_INTENCLR_OFFSET(n) (0x0308 + 16 * (n)) /* Disable interrupt */ +#define NRF54L_GRTC_INTPEND_OFFSET(n) (0x030c + 16 * (n)) /* Pending interrupts */ +#define NRF54L_GRTC_EVTEN_OFFSET 0x0400 /* Event routing */ +#define NRF54L_GRTC_EVTENSET_OFFSET 0x0404 /* Enable routing */ +#define NRF54L_GRTC_EVTENCLR_OFFSET 0x0408 /* Disable routing */ +#define NRF54L_GRTC_MODE_OFFSET 0x0510 /* Counter mode */ +#define NRF54L_GRTC_CCL_OFFSET(n) (0x0520 + 16 * (n)) /* Compare low word */ +#define NRF54L_GRTC_CCH_OFFSET(n) (0x0524 + 16 * (n)) /* Compare high word */ +#define NRF54L_GRTC_CCADD_OFFSET(n) (0x0528 + 16 * (n)) /* Add to compare */ +#define NRF54L_GRTC_CCEN_OFFSET(n) (0x052c + 16 * (n)) /* Compare enable */ +#define NRF54L_GRTC_TIMEOUT_OFFSET 0x06a4 /* SYSCOUNTER sleep timeout */ +#define NRF54L_GRTC_INTERVAL_OFFSET 0x06a8 /* CC0 increment */ +#define NRF54L_GRTC_WAKETIME_OFFSET 0x06ac /* Wakeup time */ +#define NRF54L_GRTC_STATUS_LFTIMER_OFFSET 0x06b0 /* LF timer status */ +#define NRF54L_GRTC_STATUS_PWM_OFFSET 0x06b4 /* PWM status */ +#define NRF54L_GRTC_STATUS_CLKOUT_OFFSET 0x06b8 /* Clock output status */ +#define NRF54L_GRTC_PWMCONFIG_OFFSET 0x0710 /* PWM configuration */ +#define NRF54L_GRTC_CLKOUT_OFFSET 0x0714 /* Clock output configuration */ +#define NRF54L_GRTC_CLKCFG_OFFSET 0x0718 /* Clock configuration */ +#define NRF54L_GRTC_SYSCOUNTERL_OFFSET(n) (0x0720 + 16 * (n)) /* Counter low word */ +#define NRF54L_GRTC_SYSCOUNTERH_OFFSET(n) (0x0724 + 16 * (n)) /* Counter high word */ +#define NRF54L_GRTC_SYSCOUNTER_ACTIVE_OFFSET(n) (0x0728 + 16 * (n)) /* Keep counter active */ + +/* Register bit definitions *************************************************/ + +/* Tasks and events */ + +#define GRTC_TASKS_CAPTURE (1 << 0) +#define GRTC_TASKS_START (1 << 0) +#define GRTC_TASKS_STOP (1 << 0) +#define GRTC_TASKS_CLEAR (1 << 0) +#define GRTC_TASKS_PWMSTART (1 << 0) +#define GRTC_TASKS_PWMSTOP (1 << 0) +#define GRTC_EVENTS_COMPARE (1 << 0) +#define GRTC_EVENTS_RTCOMPARESYNC (1 << 0) +#define GRTC_EVENTS_PWMPERIODEND (1 << 0) +#define GRTC_EVENTS_PWMREADY (1 << 0) +#define GRTC_EVENTS_CLKOUTREADY (1 << 0) + +/* SUBSCRIBE and PUBLISH registers */ + +#define GRTC_SUBSCRIBE_CHIDX_SHIFT (0) +#define GRTC_SUBSCRIBE_CHIDX_MASK (0xff << GRTC_SUBSCRIBE_CHIDX_SHIFT) +#define GRTC_SUBSCRIBE_EN (1 << 31) +#define GRTC_PUBLISH_CHIDX_SHIFT (0) +#define GRTC_PUBLISH_CHIDX_MASK (0xff << GRTC_PUBLISH_CHIDX_SHIFT) +#define GRTC_PUBLISH_EN (1 << 31) + +/* SHORTS register */ + +#define GRTC_SHORTS_RTCOMPARE_CLEAR (1 << 0) + +/* INTEN, INTENSET, INTENCLR and INTPEND registers */ + +#define GRTC_INT_COMPARE(n) (1 << (n)) +#define GRTC_INT_RTCOMPARESYNC (1 << 25) +#define GRTC_INT_PWMPERIODEND (1 << 27) +#define GRTC_INT_PWMREADY (1 << 29) +#define GRTC_INT_CLKOUTREADY (1 << 30) + +/* EVTEN, EVTENSET and EVTENCLR registers */ + +#define GRTC_EVTEN_PWMPERIODEND (1 << 27) + +/* MODE register */ + +#define GRTC_MODE_AUTOEN (1 << 0) +#define GRTC_MODE_SYSCOUNTEREN (1 << 1) + +/* CC registers */ + +#define GRTC_CCL_MASK (0xffffffff) +#define GRTC_CCH_MASK (0x000fffff) +#define GRTC_CCADD_VALUE_MASK (0x7fffffff) +#define GRTC_CCADD_REFERENCE_CC (1 << 31) +#define GRTC_CCEN_ACTIVE (1 << 0) +#if defined(CONFIG_ARCH_CHIP_NRF54LM20A) || defined(CONFIG_ARCH_CHIP_NRF54LM20B) +# define GRTC_CCEN_PASTCC (1 << 1) +#endif + +/* TIMEOUT, INTERVAL and WAKETIME registers */ + +#define GRTC_TIMEOUT_MASK (0xffff) +#define GRTC_INTERVAL_MASK (0xffff) +#define GRTC_WAKETIME_MASK (0xff) + +/* STATUS registers */ + +#define GRTC_STATUS_LFTIMER_READY (1 << 0) +#define GRTC_STATUS_PWM_READY (1 << 0) +#define GRTC_STATUS_CLKOUT_READY (1 << 0) + +/* PWMCONFIG register */ + +#define GRTC_PWMCONFIG_PERIOD_MASK (0xff) + +/* CLKOUT register */ + +#define GRTC_CLKOUT_CLK32K (1 << 0) +#define GRTC_CLKOUT_CLKFAST (1 << 1) + +/* CLKCFG register */ + +#define GRTC_CLKCFG_CLKFASTDIV_SHIFT (0) +#define GRTC_CLKCFG_CLKFASTDIV_MASK (0xff << GRTC_CLKCFG_CLKFASTDIV_SHIFT) +#define GRTC_CLKCFG_CLKFASTDIV(n) ((n) << GRTC_CLKCFG_CLKFASTDIV_SHIFT) +#define GRTC_CLKCFG_CLKSEL_SHIFT (16) +#define GRTC_CLKCFG_CLKSEL_MASK (3 << GRTC_CLKCFG_CLKSEL_SHIFT) +#define GRTC_CLKCFG_CLKSEL_LFXO (0 << GRTC_CLKCFG_CLKSEL_SHIFT) +#define GRTC_CLKCFG_CLKSEL_LFCLK (1 << GRTC_CLKCFG_CLKSEL_SHIFT) +#define GRTC_CLKCFG_CLKSEL_LFLPRC (2 << GRTC_CLKCFG_CLKSEL_SHIFT) + +/* SYSCOUNTER registers */ + +#define GRTC_SYSCOUNTERL_MASK (0xffffffff) +#define GRTC_SYSCOUNTERH_MASK (0x000fffff) +#if defined(CONFIG_ARCH_CHIP_NRF54LM20A) || defined(CONFIG_ARCH_CHIP_NRF54LM20B) +# define GRTC_SYSCOUNTERH_LOADED (1 << 29) +#endif +#define GRTC_SYSCOUNTERH_BUSY (1 << 30) +#define GRTC_SYSCOUNTERH_OVERFLOW (1 << 31) +#define GRTC_SYSCOUNTER_ACTIVE (1 << 0) +#define GRTC_COUNTER_MAX (0x000fffffffffffffull) + +#endif /* __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_GRTC_H */ diff --git a/arch/arm/src/nrf54l/nrf54l_clockconfig.c b/arch/arm/src/nrf54l/nrf54l_clockconfig.c index 0bb851268fc..ca4ea707c17 100644 --- a/arch/arm/src/nrf54l/nrf54l_clockconfig.c +++ b/arch/arm/src/nrf54l/nrf54l_clockconfig.c @@ -156,6 +156,32 @@ void nrf54l_clockconfig(void) PANIC(); } +#ifdef CONFIG_NRF54L_USE_LFCLK + /* Initialize LFCLK */ + +#if defined(CONFIG_NRF54L_LFCLK_XTAL) + putreg32(CLOCK_LFCLK_SRC_SRC_LFXO, NRF54L_CLOCK_LFCLK_SRC); +#elif defined(CONFIG_NRF54L_LFCLK_SYNTH) + putreg32(CLOCK_LFCLK_SRC_SRC_LFSYNT, NRF54L_CLOCK_LFCLK_SRC); +#else + putreg32(CLOCK_LFCLK_SRC_SRC_LFRC, NRF54L_CLOCK_LFCLK_SRC); +#endif + + /* Trigger LFCLK start */ + + putreg32(0x0, NRF54L_CLOCK_EVENTS_LFCLKSTARTED); + putreg32(0x1, NRF54L_CLOCK_TASKS_LFCLKSTART); + + while (!getreg32(NRF54L_CLOCK_EVENTS_LFCLKSTARTED)) + { + /* Wait for LFCLK to be running */ + } + +#if defined(CONFIG_NRF54L_LFCLK_RC) + /* TODO: calibrate LFCLK RC oscillator */ +#endif +#endif + #ifndef CONFIG_ARCH_CHIP_NRF54L15 for (i = 0; i < 10000000 && getreg32(NRF54L_KMU_STATUS) == 1; i++) { diff --git a/arch/arm/src/nrf54l/nrf54l_grtc.c b/arch/arm/src/nrf54l/nrf54l_grtc.c new file mode 100644 index 00000000000..d34972239f1 --- /dev/null +++ b/arch/arm/src/nrf54l/nrf54l_grtc.c @@ -0,0 +1,618 @@ +/**************************************************************************** + * arch/arm/src/nrf54l/nrf54l_grtc.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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 "arm_internal.h" +#include "hardware/nrf54l_grtc.h" + +#include "nrf54l_grtc.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct nrf54l_grtc_priv_s +{ + struct nrf54l_grtc_ops_s *ops; + uint32_t base; + uint32_t irq; + uint8_t chan; + bool inuse; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* GRTC registers access ****************************************************/ + +static uint32_t nrf54l_grtc_getreg(struct nrf54l_grtc_dev_s *dev, + uint32_t offset); +static void nrf54l_grtc_putreg(struct nrf54l_grtc_dev_s *dev, + uint32_t offset, + uint32_t value); + +/* GRTC helpers *************************************************************/ + +static uint32_t nrf54l_grtc_irq2reg(struct nrf54l_grtc_dev_s *dev, + uint8_t s); +static void nrf54l_grtc_waitready(struct nrf54l_grtc_dev_s *dev); + +/* GRTC operations **********************************************************/ + +static int nrf54l_grtc_start(struct nrf54l_grtc_dev_s *dev); +static int nrf54l_grtc_stop(struct nrf54l_grtc_dev_s *dev); +static int nrf54l_grtc_clear(struct nrf54l_grtc_dev_s *dev); +static int nrf54l_grtc_getcounter(struct nrf54l_grtc_dev_s *dev, + uint64_t *cc); +static int nrf54l_grtc_setcc(struct nrf54l_grtc_dev_s *dev, uint8_t i, + uint64_t cc); +static int nrf54l_grtc_getcc(struct nrf54l_grtc_dev_s *dev, uint8_t i, + uint64_t *cc); +static int nrf54l_grtc_disablecc(struct nrf54l_grtc_dev_s *dev, uint8_t i); +static int nrf54l_grtc_setisr(struct nrf54l_grtc_dev_s *dev, xcpt_t handler, + void * arg); +static int nrf54l_grtc_enableint(struct nrf54l_grtc_dev_s *dev, uint8_t s); +static int nrf54l_grtc_disableint(struct nrf54l_grtc_dev_s *dev, uint8_t s); +static int nrf54l_grtc_checkint(struct nrf54l_grtc_dev_s *dev, uint8_t s); +static int nrf54l_grtc_ackint(struct nrf54l_grtc_dev_s *dev, uint8_t s); +static uint32_t nrf54l_grtc_getbase(struct nrf54l_grtc_dev_s *dev); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* NRF54L GRTC ops */ + +struct nrf54l_grtc_ops_s nrf54l_grtc_ops = +{ + .start = nrf54l_grtc_start, + .stop = nrf54l_grtc_stop, + .clear = nrf54l_grtc_clear, + .getcounter = nrf54l_grtc_getcounter, + .setcc = nrf54l_grtc_setcc, + .getcc = nrf54l_grtc_getcc, + .disablecc = nrf54l_grtc_disablecc, + .setisr = nrf54l_grtc_setisr, + .enableint = nrf54l_grtc_enableint, + .disableint = nrf54l_grtc_disableint, + .checkint = nrf54l_grtc_checkint, + .ackint = nrf54l_grtc_ackint, + .getbase = nrf54l_grtc_getbase, +}; + +/* GRTC */ + +struct nrf54l_grtc_priv_s g_nrf54l_grtc_priv = +{ + .ops = &nrf54l_grtc_ops, + .base = NRF54L_GRTC_BASE, + .irq = NRF54L_IRQ_GRTC_0, + .chan = 12, + .inuse = false, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nrf54l_grtc_getreg + * + * Description: + * Get a 32-bit register value by offset + * + ****************************************************************************/ + +static uint32_t nrf54l_grtc_getreg(struct nrf54l_grtc_dev_s *dev, + uint32_t offset) +{ + DEBUGASSERT(dev); + + return getreg32(((struct nrf54l_grtc_priv_s *)dev)->base + offset); +} + +/**************************************************************************** + * Name: nrf54l_grtc_putreg + * + * Description: + * Put a 32-bit register value by offset + * + ****************************************************************************/ + +static void nrf54l_grtc_putreg(struct nrf54l_grtc_dev_s *dev, + uint32_t offset, + uint32_t value) +{ + DEBUGASSERT(dev); + + putreg32(value, ((struct nrf54l_grtc_priv_s *)dev)->base + offset); +} + +/**************************************************************************** + * Name: nrf54l_grtc_irq2reg + * + * Description: + * Get the value of the interrupt register corresponding to the given + * interrupt source + * + ****************************************************************************/ + +static uint32_t nrf54l_grtc_irq2reg(struct nrf54l_grtc_dev_s *dev, + uint8_t s) +{ + struct nrf54l_grtc_priv_s *grtc = (struct nrf54l_grtc_priv_s *)dev; + uint32_t regval = 0; + + if (s < grtc->chan) + { + regval = GRTC_INT_COMPARE(s); + } + else + { + rtcerr("ERROR: unsupported IRQ source %d\n", s); + } + + return regval; +} + +/**************************************************************************** + * Name: nrf54l_grtc_waitready + * + * Description: + * Wait until the low-frequency timer has completed the previous task + * + ****************************************************************************/ + +static void nrf54l_grtc_waitready(struct nrf54l_grtc_dev_s *dev) +{ + while ((nrf54l_grtc_getreg(dev, NRF54L_GRTC_STATUS_LFTIMER_OFFSET) & + GRTC_STATUS_LFTIMER_READY) == 0) + { + } +} + +/**************************************************************************** + * Name: nrf54l_grtc_start + ****************************************************************************/ + +static int nrf54l_grtc_start(struct nrf54l_grtc_dev_s *dev) +{ + uint64_t counter; + + DEBUGASSERT(dev); + + nrf54l_grtc_waitready(dev); + nrf54l_grtc_putreg(dev, NRF54L_GRTC_TASKS_START_OFFSET, GRTC_TASKS_START); + nrf54l_grtc_putreg(dev, NRF54L_GRTC_MODE_OFFSET, GRTC_MODE_SYSCOUNTEREN); + nrf54l_grtc_putreg(dev, NRF54L_GRTC_SYSCOUNTER_ACTIVE_OFFSET(0), + GRTC_SYSCOUNTER_ACTIVE); + + /* Reading the low word initiates synchronization of the snapshot */ + + nrf54l_grtc_getcounter(dev, &counter); + + return OK; +} + +/**************************************************************************** + * Name: nrf54l_grtc_stop + ****************************************************************************/ + +static int nrf54l_grtc_stop(struct nrf54l_grtc_dev_s *dev) +{ + DEBUGASSERT(dev); + + nrf54l_grtc_putreg(dev, NRF54L_GRTC_SYSCOUNTER_ACTIVE_OFFSET(0), 0); + nrf54l_grtc_putreg(dev, NRF54L_GRTC_MODE_OFFSET, 0); + nrf54l_grtc_waitready(dev); + nrf54l_grtc_putreg(dev, NRF54L_GRTC_TASKS_STOP_OFFSET, GRTC_TASKS_STOP); + + return OK; +} + +/**************************************************************************** + * Name: nrf54l_grtc_clear + ****************************************************************************/ + +static int nrf54l_grtc_clear(struct nrf54l_grtc_dev_s *dev) +{ + DEBUGASSERT(dev); + + nrf54l_grtc_waitready(dev); + nrf54l_grtc_putreg(dev, NRF54L_GRTC_TASKS_CLEAR_OFFSET, GRTC_TASKS_CLEAR); + + return OK; +} + +/**************************************************************************** + * Name: nrf54l_grtc_getcounter + ****************************************************************************/ + +static int nrf54l_grtc_getcounter(struct nrf54l_grtc_dev_s *dev, + uint64_t *ctr) +{ + irqstate_t flags; + uint32_t low; + uint32_t high; + + DEBUGASSERT(dev); + DEBUGASSERT(ctr); + + /* The low word read snapshots the high word. Retry when synchronization + * is busy or the low word rolled over between the reads. + */ + + flags = enter_critical_section(); + + do + { + low = nrf54l_grtc_getreg(dev, NRF54L_GRTC_SYSCOUNTERL_OFFSET(0)); + high = nrf54l_grtc_getreg(dev, NRF54L_GRTC_SYSCOUNTERH_OFFSET(0)); + } + while ((high & (GRTC_SYSCOUNTERH_BUSY | GRTC_SYSCOUNTERH_OVERFLOW)) != 0); + + leave_critical_section(flags); + + *ctr = ((uint64_t)(high & GRTC_SYSCOUNTERH_MASK) << 32) | low; + + return OK; +} + +/**************************************************************************** + * Name: nrf54l_grtc_setcc + ****************************************************************************/ + +static int nrf54l_grtc_setcc(struct nrf54l_grtc_dev_s *dev, uint8_t i, + uint64_t cc) +{ + struct nrf54l_grtc_priv_s *grtc = NULL; + irqstate_t flags; + int ret = OK; + + DEBUGASSERT(dev); + + grtc = (struct nrf54l_grtc_priv_s *)dev; + + /* Is the channel supported? */ + + if (i >= grtc->chan || cc > GRTC_COUNTER_MAX) + { + rtcerr("ERROR: unsupported GRTC channel %d\n", i); + ret = -EINVAL; + goto errout; + } + + /* The channel must be disabled while the two words are written */ + + flags = enter_critical_section(); + + nrf54l_grtc_putreg(dev, NRF54L_GRTC_CCEN_OFFSET(i), 0); + nrf54l_grtc_putreg(dev, NRF54L_GRTC_CCL_OFFSET(i), (uint32_t)cc); + nrf54l_grtc_putreg(dev, NRF54L_GRTC_CCH_OFFSET(i), (uint32_t)(cc >> 32)); + nrf54l_grtc_putreg(dev, NRF54L_GRTC_CCEN_OFFSET(i), GRTC_CCEN_ACTIVE); + + leave_critical_section(flags); + +errout: + return ret; +} + +/**************************************************************************** + * Name: nrf54l_grtc_getcc + ****************************************************************************/ + +static int nrf54l_grtc_getcc(struct nrf54l_grtc_dev_s *dev, uint8_t i, + uint64_t *cc) +{ + struct nrf54l_grtc_priv_s *grtc = NULL; + uint32_t high; + int ret = OK; + + DEBUGASSERT(dev); + DEBUGASSERT(cc); + + grtc = (struct nrf54l_grtc_priv_s *)dev; + + /* Is the channel supported? */ + + if (i >= grtc->chan) + { + rtcerr("ERROR: unsupported GRTC channel %d\n", i); + ret = -EINVAL; + goto errout; + } + + high = nrf54l_grtc_getreg(dev, NRF54L_GRTC_CCH_OFFSET(i)); + *cc = ((uint64_t)(high & GRTC_CCH_MASK) << 32) | + nrf54l_grtc_getreg(dev, NRF54L_GRTC_CCL_OFFSET(i)); + +errout: + return ret; +} + +/**************************************************************************** + * Name: nrf54l_grtc_disablecc + ****************************************************************************/ + +static int nrf54l_grtc_disablecc(struct nrf54l_grtc_dev_s *dev, uint8_t i) +{ + struct nrf54l_grtc_priv_s *grtc = NULL; + int ret = OK; + + DEBUGASSERT(dev); + + grtc = (struct nrf54l_grtc_priv_s *)dev; + + /* Is the channel supported? */ + + if (i >= grtc->chan) + { + rtcerr("ERROR: unsupported GRTC channel %d\n", i); + ret = -EINVAL; + goto errout; + } + + nrf54l_grtc_putreg(dev, NRF54L_GRTC_CCEN_OFFSET(i), 0); + +errout: + return ret; +} + +/**************************************************************************** + * Name: nrf54l_grtc_setisr + ****************************************************************************/ + +static int nrf54l_grtc_setisr(struct nrf54l_grtc_dev_s *dev, xcpt_t handler, + void *arg) +{ + struct nrf54l_grtc_priv_s *grtc = NULL; + int ret = OK; + + DEBUGASSERT(dev); + + grtc = (struct nrf54l_grtc_priv_s *)dev; + + /* Disable interrupt when callback is removed */ + + if (!handler) + { + up_disable_irq(grtc->irq); + irq_detach(grtc->irq); + ret = OK; + goto errout; + } + + /* Otherwise set callback and enable interrupt */ + + irq_attach(grtc->irq, handler, arg); + up_enable_irq(grtc->irq); + +errout: + return ret; +} + +/**************************************************************************** + * Name: nrf54l_grtc_enableint + ****************************************************************************/ + +static int nrf54l_grtc_enableint(struct nrf54l_grtc_dev_s *dev, uint8_t s) +{ + uint32_t regval = 0; + int ret = OK; + + DEBUGASSERT(dev); + + /* Get register value for given interrupt source */ + + regval = nrf54l_grtc_irq2reg(dev, s); + if (regval == 0) + { + ret = -EINVAL; + goto errout; + } + + nrf54l_grtc_putreg(dev, NRF54L_GRTC_INTENSET_OFFSET(0), regval); + +errout: + return ret; +} + +/**************************************************************************** + * Name: nrf54l_grtc_disableint + ****************************************************************************/ + +static int nrf54l_grtc_disableint(struct nrf54l_grtc_dev_s *dev, uint8_t s) +{ + uint32_t regval = 0; + int ret = OK; + + DEBUGASSERT(dev); + + /* Get register value for given interrupt source */ + + regval = nrf54l_grtc_irq2reg(dev, s); + if (regval == 0) + { + ret = -EINVAL; + goto errout; + } + + nrf54l_grtc_putreg(dev, NRF54L_GRTC_INTENCLR_OFFSET(0), regval); + +errout: + return ret; +} + +/**************************************************************************** + * Name: nrf54l_grtc_checkint + ****************************************************************************/ + +static int nrf54l_grtc_checkint(struct nrf54l_grtc_dev_s *dev, uint8_t s) +{ + struct nrf54l_grtc_priv_s *grtc = (struct nrf54l_grtc_priv_s *)dev; + int ret = 0; + + DEBUGASSERT(dev); + + if (s >= grtc->chan) + { + rtcerr("ERROR: unsupported IRQ source %d\n", s); + ret = -EINVAL; + goto errout; + } + + ret = nrf54l_grtc_getreg(dev, NRF54L_GRTC_EVENTS_COMPARE_OFFSET(s)); + +errout: + return ret; +} + +/**************************************************************************** + * Name: nrf54l_grtc_ackint + ****************************************************************************/ + +static int nrf54l_grtc_ackint(struct nrf54l_grtc_dev_s *dev, uint8_t s) +{ + struct nrf54l_grtc_priv_s *grtc = (struct nrf54l_grtc_priv_s *)dev; + int ret = 0; + + DEBUGASSERT(dev); + + if (s >= grtc->chan) + { + rtcerr("ERROR: unsupported IRQ source %d\n", s); + ret = -EINVAL; + goto errout; + } + + nrf54l_grtc_putreg(dev, NRF54L_GRTC_EVENTS_COMPARE_OFFSET(s), 0); + +errout: + return ret; +} + +/**************************************************************************** + * Name: nrf54l_grtc_getbase + ****************************************************************************/ + +static uint32_t nrf54l_grtc_getbase(struct nrf54l_grtc_dev_s *dev) +{ + struct nrf54l_grtc_priv_s *grtc = (struct nrf54l_grtc_priv_s *)dev; + + DEBUGASSERT(dev); + + return grtc->base; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nrf54l_grtc_init + * + * Description: + * Initialize GRTC device + * + ****************************************************************************/ + +struct nrf54l_grtc_dev_s *nrf54l_grtc_init(int grtc) +{ + struct nrf54l_grtc_priv_s *priv = NULL; + uint32_t regval; + + /* Get GRTC instance */ + + switch (grtc) + { + case 0: + { + priv = &g_nrf54l_grtc_priv; + break; + } + + default: + { + rtcerr("ERROR: unsupported GRTC %d\n", grtc); + goto errout; + } + } + + if (priv->inuse != false) + { + /* GRTC already in use */ + + priv = NULL; + goto errout; + } + + priv->inuse = true; + + /* Clock the LF timer from LFCLK. This register is not affected by a + * system reset so it is always configured here. + */ + + regval = getreg32(priv->base + NRF54L_GRTC_CLKCFG_OFFSET); + regval &= ~GRTC_CLKCFG_CLKSEL_MASK; + regval |= GRTC_CLKCFG_CLKSEL_LFCLK; + putreg32(regval, priv->base + NRF54L_GRTC_CLKCFG_OFFSET); + +errout: + return (struct nrf54l_grtc_dev_s *)priv; +} + +/**************************************************************************** + * Name: nrf54l_grtc_deinit + * + * Description: + * Deinit GRTC device + * + ****************************************************************************/ + +int nrf54l_grtc_deinit(struct nrf54l_grtc_dev_s *dev) +{ + struct nrf54l_grtc_priv_s *grtc = NULL; + + DEBUGASSERT(dev); + + grtc = (struct nrf54l_grtc_priv_s *)dev; + + grtc->inuse = false; + + return OK; +} diff --git a/arch/arm/src/nrf54l/nrf54l_grtc.h b/arch/arm/src/nrf54l/nrf54l_grtc.h new file mode 100644 index 00000000000..3d34b187fc7 --- /dev/null +++ b/arch/arm/src/nrf54l/nrf54l_grtc.h @@ -0,0 +1,140 @@ +/**************************************************************************** + * arch/arm/src/nrf54l/nrf54l_grtc.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_NRF54L_NRF54L_GRTC_H +#define __ARCH_ARM_SRC_NRF54L_NRF54L_GRTC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Helpers ******************************************************************/ + +#define NRF54L_GRTC_START(d) ((d)->ops->start(d)) +#define NRF54L_GRTC_STOP(d) ((d)->ops->stop(d)) +#define NRF54L_GRTC_CLEAR(d) ((d)->ops->clear(d)) +#define NRF54L_GRTC_GETCOUNTER(d, c) ((d)->ops->getcounter(d, c)) +#define NRF54L_GRTC_SETCC(d, i, cc) ((d)->ops->setcc(d, i, cc)) +#define NRF54L_GRTC_GETCC(d, i, cc) ((d)->ops->getcc(d, i, cc)) +#define NRF54L_GRTC_DISABLECC(d, i) ((d)->ops->disablecc(d, i)) +#define NRF54L_GRTC_SETISR(d, hnd, arg) ((d)->ops->setisr(d, hnd, arg)) +#define NRF54L_GRTC_ENABLEINT(d, s) ((d)->ops->enableint(d, s)) +#define NRF54L_GRTC_DISABLEINT(d, s) ((d)->ops->disableint(d, s)) +#define NRF54L_GRTC_CHECKINT(d, s) ((d)->ops->checkint(d, s)) +#define NRF54L_GRTC_ACKINT(d, s) ((d)->ops->ackint(d, s)) +#define NRF54L_GRTC_GETBASE(d) ((d)->ops->getbase(d)) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* GRTC CC index */ + +enum nrf54l_grtc_cc_e +{ + NRF54L_GRTC_CC0 = 0, + NRF54L_GRTC_CC1 = 1, + NRF54L_GRTC_CC2 = 2, + NRF54L_GRTC_CC3 = 3, + NRF54L_GRTC_CC4 = 4, + NRF54L_GRTC_CC5 = 5, + NRF54L_GRTC_CC6 = 6, + NRF54L_GRTC_CC7 = 7, + NRF54L_GRTC_CC8 = 8, + NRF54L_GRTC_CC9 = 9, + NRF54L_GRTC_CC10 = 10, + NRF54L_GRTC_CC11 = 11, +}; + +/* GRTC Interrupts/Events */ + +enum nrf54l_grtc_evt_e +{ + NRF54L_GRTC_EVT_COMPARE0 = 0, + NRF54L_GRTC_EVT_COMPARE1 = 1, + NRF54L_GRTC_EVT_COMPARE2 = 2, + NRF54L_GRTC_EVT_COMPARE3 = 3, + NRF54L_GRTC_EVT_COMPARE4 = 4, + NRF54L_GRTC_EVT_COMPARE5 = 5, + NRF54L_GRTC_EVT_COMPARE6 = 6, + NRF54L_GRTC_EVT_COMPARE7 = 7, + NRF54L_GRTC_EVT_COMPARE8 = 8, + NRF54L_GRTC_EVT_COMPARE9 = 9, + NRF54L_GRTC_EVT_COMPARE10 = 10, + NRF54L_GRTC_EVT_COMPARE11 = 11, +}; + +/* NRF54L GRTC device */ + +struct nrf54l_grtc_dev_s +{ + struct nrf54l_grtc_ops_s *ops; +}; + +/* NRF54L GRTC ops */ + +struct nrf54l_grtc_ops_s +{ + /* GRTC tasks */ + + int (*start)(struct nrf54l_grtc_dev_s *dev); + int (*stop)(struct nrf54l_grtc_dev_s *dev); + int (*clear)(struct nrf54l_grtc_dev_s *dev); + + /* GRTC operations */ + + int (*getcounter)(struct nrf54l_grtc_dev_s *dev, uint64_t *cc); + int (*setcc)(struct nrf54l_grtc_dev_s *dev, uint8_t i, uint64_t cc); + int (*getcc)(struct nrf54l_grtc_dev_s *dev, uint8_t i, uint64_t *cc); + int (*disablecc)(struct nrf54l_grtc_dev_s *dev, uint8_t i); + + /* GRTC interrupts */ + + int (*setisr)(struct nrf54l_grtc_dev_s *dev, xcpt_t handler, void *arg); + int (*enableint)(struct nrf54l_grtc_dev_s *dev, uint8_t source); + int (*disableint)(struct nrf54l_grtc_dev_s *dev, uint8_t source); + int (*checkint)(struct nrf54l_grtc_dev_s *dev, uint8_t source); + int (*ackint)(struct nrf54l_grtc_dev_s *dev, uint8_t source); + + /* Utility */ + + uint32_t (*getbase)(struct nrf54l_grtc_dev_s *dev); +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +struct nrf54l_grtc_dev_s *nrf54l_grtc_init(int grtc); +int nrf54l_grtc_deinit(struct nrf54l_grtc_dev_s *dev); + +#endif /* __ARCH_ARM_SRC_NRF54L_NRF54L_GRTC_H */ diff --git a/arch/arm/src/nrf54l/nrf54l_idle.c b/arch/arm/src/nrf54l/nrf54l_idle.c index c4557fabb5c..322e2d70d45 100644 --- a/arch/arm/src/nrf54l/nrf54l_idle.c +++ b/arch/arm/src/nrf54l/nrf54l_idle.c @@ -83,9 +83,16 @@ void up_idle(void) nxsched_process_timer(); #else - /* The SysTick's clock will only tick when the CPU is + /* Sleep until an interrupt occurs to save power + * + * The SysTick's clock will only tick when the CPU is * running (not in WFE/WFI) or when the system is in debug interface mode. */ +#ifdef CONFIG_NRF54L_SYSTIMER_GRTC + BEGIN_IDLE(); + asm("WFI"); + END_IDLE(); +#endif #endif } diff --git a/arch/arm/src/nrf54l/nrf54l_tickless_grtc.c b/arch/arm/src/nrf54l/nrf54l_tickless_grtc.c new file mode 100644 index 00000000000..806c7c153ac --- /dev/null +++ b/arch/arm/src/nrf54l/nrf54l_tickless_grtc.c @@ -0,0 +1,282 @@ +/**************************************************************************** + * arch/arm/src/nrf54l/nrf54l_tickless_grtc.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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/nrf54l_grtc.h" +#include "nrf54l_grtc.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Check configuration */ + +#ifdef CONFIG_TIMER_ARCH +# error CONFIG_TIMER_ARCH must be not set +#endif + +/* The SYSCOUNTER runs at 1MHz so the counter value is in microseconds. + * The compare value must be ahead of the counter for the event to fire, + * so a small margin covers the register writes. + */ + +#define NRF54L_GRTC_ALARM_MARGIN (10) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct nrf54l_tickless_dev_s +{ + struct nrf54l_grtc_dev_s *grtc; /* nrf54l GRTC driver */ + bool alarm_set; /* is the alarm set? */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int grtc_handler(int irq, void *context, void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +struct nrf54l_tickless_dev_s g_tickless_dev; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static inline void grtc_counter_to_ts(uint64_t counter, struct timespec *now) +{ + now->tv_sec = counter / USEC_PER_SEC; + now->tv_nsec = (counter % USEC_PER_SEC) * NSEC_PER_USEC; +} + +static void grtc_prepare_alarm(uint64_t target) +{ + uint64_t counter; + + /* Set CC to the desired value. If the alarm is already due, the CC is + * moved ahead of the counter so that the COMPARE event is guaranteed + * to fire. We repeat this until the CC is ahead of the counter or + * the event has already fired (as the counter may change in between). + */ + + for (; ; ) + { + NRF54L_GRTC_GETCOUNTER(g_tickless_dev.grtc, &counter); + if (target <= counter + NRF54L_GRTC_ALARM_MARGIN) + { + target = counter + NRF54L_GRTC_ALARM_MARGIN; + } + + NRF54L_GRTC_SETCC(g_tickless_dev.grtc, NRF54L_GRTC_CC0, target); + + NRF54L_GRTC_GETCOUNTER(g_tickless_dev.grtc, &counter); + if (target > counter || + NRF54L_GRTC_CHECKINT(g_tickless_dev.grtc, + NRF54L_GRTC_EVT_COMPARE0)) + { + break; + } + } + + /* Enable interrupt */ + + NRF54L_GRTC_ENABLEINT(g_tickless_dev.grtc, NRF54L_GRTC_EVT_COMPARE0); +} + +/**************************************************************************** + * Name: grtc_cancel_ack + ****************************************************************************/ + +static void grtc_cancel_ack(void) +{ + irqstate_t flags; + + flags = enter_critical_section(); + + NRF54L_GRTC_DISABLEINT(g_tickless_dev.grtc, NRF54L_GRTC_EVT_COMPARE0); + NRF54L_GRTC_DISABLECC(g_tickless_dev.grtc, NRF54L_GRTC_CC0); + NRF54L_GRTC_ACKINT(g_tickless_dev.grtc, NRF54L_GRTC_EVT_COMPARE0); + g_tickless_dev.alarm_set = false; + + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: grtc_handler + ****************************************************************************/ + +static int grtc_handler(int irq, void *context, void *arg) +{ + irqstate_t flags; + + flags = enter_critical_section(); + + /* if the compare event fired */ + + if (NRF54L_GRTC_CHECKINT(g_tickless_dev.grtc, NRF54L_GRTC_EVT_COMPARE0)) + { + /* cancel the alarm and ack the event */ + + grtc_cancel_ack(); + + /* let scheduler now of alarm firing */ + + nxsched_process_timer(); + } + + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_alarm_cancel + ****************************************************************************/ + +int up_alarm_cancel(struct timespec *ts) +{ + uint64_t counter; + irqstate_t flags; + + flags = enter_critical_section(); + + NRF54L_GRTC_DISABLEINT(g_tickless_dev.grtc, NRF54L_GRTC_EVT_COMPARE0); + NRF54L_GRTC_GETCOUNTER(g_tickless_dev.grtc, &counter); + + NRF54L_GRTC_DISABLECC(g_tickless_dev.grtc, NRF54L_GRTC_CC0); + NRF54L_GRTC_ACKINT(g_tickless_dev.grtc, NRF54L_GRTC_EVT_COMPARE0); + g_tickless_dev.alarm_set = false; + + leave_critical_section(flags); + + grtc_counter_to_ts(counter, ts); + + return OK; +} + +/**************************************************************************** + * Name: up_alarm_start + ****************************************************************************/ + +int up_alarm_start(const struct timespec *ts) +{ + uint64_t target; + irqstate_t flags; + + flags = enter_critical_section(); + + /* remember the alarm time */ + + g_tickless_dev.alarm_set = true; + + /* Round up so the alarm never fires before the requested time. Clamp + * to the counter range, the alarm is reevaluated before it can expire. + */ + + if (ts->tv_sec >= (GRTC_COUNTER_MAX / USEC_PER_SEC)) + { + target = GRTC_COUNTER_MAX; + } + else + { + target = (uint64_t)ts->tv_sec * USEC_PER_SEC + + (ts->tv_nsec + NSEC_PER_USEC - 1) / NSEC_PER_USEC; + } + + grtc_prepare_alarm(target); + + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Name: up_timer_gettime + ****************************************************************************/ + +int up_timer_gettime(struct timespec *ts) +{ + uint64_t counter; + irqstate_t flags; + + flags = enter_critical_section(); + + NRF54L_GRTC_GETCOUNTER(g_tickless_dev.grtc, &counter); + grtc_counter_to_ts(counter, ts); + + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Name: up_timer_initialize + ****************************************************************************/ + +void up_timer_initialize(void) +{ + memset(&g_tickless_dev, 0, sizeof(struct nrf54l_tickless_dev_s)); + + g_tickless_dev.grtc = nrf54l_grtc_init(0); + + /* Ensure we have support for the GRTC instance */ + + ASSERT(g_tickless_dev.grtc); + + /* Configure ISR */ + + NRF54L_GRTC_SETISR(g_tickless_dev.grtc, grtc_handler, NULL); + + /* Start counting */ + + NRF54L_GRTC_CLEAR(g_tickless_dev.grtc); + NRF54L_GRTC_ACKINT(g_tickless_dev.grtc, NRF54L_GRTC_EVT_COMPARE0); + NRF54L_GRTC_START(g_tickless_dev.grtc); + + /* kick off alarm scheduling */ + + nxsched_process_timer(); +}