This is an automated email from the ASF dual-hosted git repository.

jerpelea pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 0942bae6ef5b2cdaefe97483dd8f04a797b2a6a7
Author: raiden00pl <[email protected]>
AuthorDate: Sun Sep 20 16:18:49 2026 +0200

    arm/nrf54l: add GRTC and tickless scheduling
    
    arm/nrf54l: add GRTC and tickless scheduling
    
    Assisted-by: Codex:GPT-6
    Signed-off-by: raiden00pl <[email protected]>
---
 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(-)

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 <nuttx/config.h>
+#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 <nuttx/config.h>
+
+#include <assert.h>
+#include <nuttx/debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+
+#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 <nuttx/config.h>
+
+#include <nuttx/irq.h>
+
+#include <stdint.h>
+
+/****************************************************************************
+ * 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 <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <nuttx/debug.h>
+
+#include <nuttx/arch.h>
+
+#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();
+}

Reply via email to