arm/nrf54l: add GRTC and tickless scheduling

arm/nrf54l: add GRTC and tickless scheduling

Assisted-by: Codex:GPT-6
Signed-off-by: raiden00pl <raiden00@railab.me>
This commit is contained in:
raiden00pl 2026-09-20 16:18:49 +02:00 • committed by Alin Jerpelea
parent 04d2c9013c
commit 0942bae6ef
10 changed files with 1321 additions and 4 deletions

View file

@ -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.

View file

@ -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()

View file

@ -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

View file

@ -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

View file

@ -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 */

View file

@ -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++)
{

View file

@ -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;
}

View file

@ -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 */

View file

@ -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
}

View file

@ -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();
}