From aeaf718fc9db4c1713c1a2ff7bb0f2bc367600cc Mon Sep 17 00:00:00 2001 From: Marco Casaroli Date: Sat, 25 Jul 2026 02:24:12 +0200 Subject: [PATCH 1/2] rp23xx: Add /dev/timer driver on the TIMER0/TIMER1 blocks. The RP2350 has two system timer blocks (TIMER0, TIMER1), each a free-running 64-bit counter incremented once per microsecond by the TICKS block (set up in rp23xx_clock.c). They are independent of the ARM SysTick that drives the OS tick, so they are free for application use, but the port had no driver for them. Add rp23xx_timer.c, a NuttX timer lower-half that binds a block to a /dev/timerN device. It uses ALARM0 of the block, which matches the low 32 bits of the microsecond counter, to implement single-shot and periodic timeouts with 1 us resolution and a maximum interval of 2^32 - 1 us (~71.5 minutes). Periodic reloads are scheduled relative to the previous expiry to avoid drift, but never behind the counter (an alarm set in the past would not match until the 32-bit counter wraps). Enable with CONFIG_RP23XX_TIMER (which selects CONFIG_TIMER), then turn on each block independently: CONFIG_RP23XX_TIMER0 registers /dev/timer0 and CONFIG_RP23XX_TIMER1 registers /dev/timer1. A block claimed by the tickless oneshot (CONFIG_RP23XX_SYSTIMER_TICKLESS) is excluded from these choices in Kconfig, so the two features can be enabled together without colliding on the same block or its alarm IRQ. Tested on Pimoroni Pico Plus 2 (RP2350B) hardware with examples/timer and a CMSIS-DAP probe. The device registers as /dev/timer0; the ALARM0 interrupt fires at the programmed period (verified over SWD at the configured 1 s interval, not a busy loop), and the full path -- alarm match, the driver ISR, the timer notification and delivery of the SIGNO to user space -- reaches the example's signal handler and increments its counter. Assisted-by: Claude Opus 4.8 (1M context) Signed-off-by: Marco Casaroli --- Documentation/platforms/arm/rp23xx/index.rst | 24 ++ arch/arm/src/rp23xx/CMakeLists.txt | 4 + arch/arm/src/rp23xx/Kconfig | 40 ++ arch/arm/src/rp23xx/Make.defs | 4 + arch/arm/src/rp23xx/rp23xx_timer.c | 395 ++++++++++++++++++ arch/arm/src/rp23xx/rp23xx_timer.h | 74 ++++ .../rp23xx/common/src/rp23xx_common_bringup.c | 25 ++ 7 files changed, 566 insertions(+) create mode 100644 arch/arm/src/rp23xx/rp23xx_timer.c create mode 100644 arch/arm/src/rp23xx/rp23xx_timer.h diff --git a/Documentation/platforms/arm/rp23xx/index.rst b/Documentation/platforms/arm/rp23xx/index.rst index e788ace692c43..b8422a9e6885d 100644 --- a/Documentation/platforms/arm/rp23xx/index.rst +++ b/Documentation/platforms/arm/rp23xx/index.rst @@ -43,6 +43,7 @@ SRAM Boot Working Requires external SWD debugger PSRAM Working Three modes of heap allocation described below TRNG Working Hardware RNG at /dev/random and /dev/urandom Flash MTD Working Unused flash tail as an MTD device, answers BIOC_XIPBASE +Timer Working /dev/timerN on TIMER0/TIMER1, 1 us resolution ============== ============ ===== Installation @@ -171,6 +172,29 @@ external PSRAM is configured as a separate user heap called `psram` and can be used through the global variable `g_psramheap` after including `rp23xx_heaps.h` +Timer +===== + +The RP2350 has two system timer blocks, TIMER0 and TIMER1, each a +free-running 64-bit counter incremented once per microsecond. They are +independent of the ARM SysTick that drives the OS tick, so they are +available for application timers. + +Enable the driver with `RP23XX_TIMER` (which selects `TIMER`), then turn on +each block you want: `RP23XX_TIMER0` registers `/dev/timer0` (TIMER0) and +`RP23XX_TIMER1` registers `/dev/timer1` (TIMER1). Each device implements the +standard NuttX timer lower-half: single-shot or periodic timeouts with 1 us +resolution and a maximum interval of 2^32 - 1 us (about 71.5 minutes), driven +by ALARM0 of the block. + +A block claimed by the tickless-OS oneshot +(`RP23XX_SYSTIMER_TICKLESS`) is removed from the choices above, so a +`/dev/timer` device and the tickless OS time source never collide on the same +block -- you can run both at once (e.g. tickless on TIMER0, `/dev/timer1` on +TIMER1). With tickless disabled, both `/dev/timer0` and +`/dev/timer1` are available. The `examples/timer` application can exercise a +device; point `CONFIG_EXAMPLES_TIMER_DEVNAME` at the block you enabled. + Programming ============ diff --git a/arch/arm/src/rp23xx/CMakeLists.txt b/arch/arm/src/rp23xx/CMakeLists.txt index 0d21226da9173..5c52dca2aa08b 100644 --- a/arch/arm/src/rp23xx/CMakeLists.txt +++ b/arch/arm/src/rp23xx/CMakeLists.txt @@ -56,6 +56,10 @@ if(CONFIG_RP23XX_PWM) list(APPEND SRCS rp23xx_pwm.c) endif() +if(CONFIG_RP23XX_TIMER) + list(APPEND SRCS rp23xx_timer.c) +endif() + if(CONFIG_RP23XX_I2C) list(APPEND SRCS rp23xx_i2c.c) endif() diff --git a/arch/arm/src/rp23xx/Kconfig b/arch/arm/src/rp23xx/Kconfig index 0a4f29e33a025..e3f2f499a0578 100644 --- a/arch/arm/src/rp23xx/Kconfig +++ b/arch/arm/src/rp23xx/Kconfig @@ -736,6 +736,46 @@ config RP23XX_SPISD_SPI_CH endif # SPISD Configuration +##################################################################### +# Timer Configuration +##################################################################### + +menuconfig RP23XX_TIMER + bool "System timer (/dev/timer) support" + default n + select TIMER + ---help--- + Build the /dev/timerN driver on top of the RP2350 system timer + blocks (TIMER0 and TIMER1). Each block is a free-running 64-bit + microsecond counter, independent of the ARM SysTick that drives + the OS tick, so it is available for application timers. The + driver uses ALARM0 of a block for a single-shot or periodic + timeout with 1 us resolution. + + Enable one or both blocks below. A block claimed by the tickless + oneshot (CONFIG_RP23XX_SYSTIMER_TICKLESS) is removed from the list + so the two features never collide on the same block. + +if RP23XX_TIMER + +config RP23XX_TIMER0 + bool "Register TIMER0 as /dev/timer0" + default y + depends on !RP23XX_SYSTIMER_TICKLESS_TIMER0 + ---help--- + Bind the TIMER0 block to /dev/timer0. Unavailable when the + tickless oneshot is configured to use TIMER0. + +config RP23XX_TIMER1 + bool "Register TIMER1 as /dev/timer1" + default n + depends on !RP23XX_SYSTIMER_TICKLESS_TIMER1 + ---help--- + Bind the TIMER1 block to /dev/timer1. Unavailable when the + tickless oneshot is configured to use TIMER1. + +endif # RP23XX_TIMER + ##################################################################### # ADC Configuration ##################################################################### diff --git a/arch/arm/src/rp23xx/Make.defs b/arch/arm/src/rp23xx/Make.defs index 71f3475b6c925..fb41bb5c6a8d3 100644 --- a/arch/arm/src/rp23xx/Make.defs +++ b/arch/arm/src/rp23xx/Make.defs @@ -60,6 +60,10 @@ ifeq ($(CONFIG_RP23XX_PWM),y) CHIP_CSRCS += rp23xx_pwm.c endif +ifeq ($(CONFIG_RP23XX_TIMER),y) +CHIP_CSRCS += rp23xx_timer.c +endif + ifeq ($(CONFIG_RP23XX_I2C),y) CHIP_CSRCS += rp23xx_i2c.c endif diff --git a/arch/arm/src/rp23xx/rp23xx_timer.c b/arch/arm/src/rp23xx/rp23xx_timer.c new file mode 100644 index 0000000000000..91e4e66ccf42d --- /dev/null +++ b/arch/arm/src/rp23xx/rp23xx_timer.c @@ -0,0 +1,395 @@ +/**************************************************************************** + * arch/arm/src/rp23xx/rp23xx_timer.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 + * + * A /dev/timerN driver on top of an RP2350 system timer block (TIMER0 or + * TIMER1). Each block is a free-running 64-bit counter incremented once per + * microsecond (the 1 MHz tick is set up by the TICKS block in + * rp23xx_clock.c) and independent of the ARM SysTick that drives the OS + * tick, so it is free for application timers. This driver uses ALARM0 of + * the selected block, which matches against the low 32 bits of the counter, + * giving a single-shot or periodic timeout with 1 us resolution and a + * maximum interval of 2^32 - 1 us (~71.5 minutes). + * + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "chip.h" +#include "arm_internal.h" +#include "rp23xx_timer.h" +#include "hardware/rp23xx_timer.h" +#include "hardware/rp23xx_memorymap.h" + +#ifdef CONFIG_RP23XX_TIMER + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* This driver uses ALARM0 of the block; alarm 0 is bit 0 in the ARMED, + * INTE, INTF, INTR and INTS registers. + */ + +#define TIMER_ALARM0_BIT (1 << 0) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct rp23xx_timer_lowerhalf_s +{ + const struct timer_ops_s *ops; /* Lower-half operations (must be 1st) */ + uint32_t base; /* TIMER block base address */ + int irq; /* ALARM0 interrupt number */ + tccb_t callback; /* Current user interrupt callback */ + void *arg; /* Argument passed to upper half cb */ + uint32_t timeout; /* The current timeout value (us) */ + bool started; /* True if the alarm is armed */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int rp23xx_timer_start(FAR struct timer_lowerhalf_s *lower); +static int rp23xx_timer_stop(FAR struct timer_lowerhalf_s *lower); +static int rp23xx_timer_getstatus(FAR struct timer_lowerhalf_s *lower, + FAR struct timer_status_s *status); +static int rp23xx_timer_settimeout(FAR struct timer_lowerhalf_s *lower, + uint32_t timeout); +static void rp23xx_timer_setcallback(FAR struct timer_lowerhalf_s *lower, + tccb_t callback, FAR void *arg); +static int rp23xx_timer_maxtimeout(FAR struct timer_lowerhalf_s *lower, + FAR uint32_t *maxtimeout); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct timer_ops_s g_rp23xx_timer_ops = +{ + .start = rp23xx_timer_start, + .stop = rp23xx_timer_stop, + .getstatus = rp23xx_timer_getstatus, + .settimeout = rp23xx_timer_settimeout, + .setcallback = rp23xx_timer_setcallback, + .ioctl = NULL, + .maxtimeout = rp23xx_timer_maxtimeout, +}; + +static struct rp23xx_timer_lowerhalf_s g_rp23xx_timer_lowerhalf[2] = +{ + { + .ops = &g_rp23xx_timer_ops, + }, + { + .ops = &g_rp23xx_timer_ops, + }, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: rp23xx_timer_interrupt + * + * Description: + * The ALARM0 interrupt handler. Acknowledges the alarm, invokes the + * upper-half callback and, for a periodic timer, re-arms the alarm for the + * next expiry. + * + ****************************************************************************/ + +static int rp23xx_timer_interrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct rp23xx_timer_lowerhalf_s *priv = arg; + uint32_t next; + uint32_t now; + uint32_t target; + + /* Acknowledge the alarm (write-1-clear of the latched interrupt). The + * alarm auto-disarms in hardware when it fires. + */ + + putreg32(TIMER_ALARM0_BIT, priv->base + RP23XX_TIMER_INTR_OFFSET); + + if (priv->callback != NULL) + { + next = priv->timeout; + if (priv->callback(&next, priv->arg) && next > 0) + { + /* Periodic: schedule the next expiry relative to the previous + * target so the period does not drift with interrupt latency, but + * never behind the counter -- an alarm set in the past would not + * match until the 32-bit microsecond counter wraps ~71 minutes + * later. + */ + + priv->timeout = next; + now = getreg32(priv->base + RP23XX_TIMER_TIMERAWL_OFFSET); + target = getreg32(priv->base + RP23XX_TIMER_ALARM0_OFFSET) + next; + + if ((int32_t)(target - now) <= 0) + { + target = now + next; + } + + putreg32(target, priv->base + RP23XX_TIMER_ALARM0_OFFSET); + } + else + { + rp23xx_timer_stop((FAR struct timer_lowerhalf_s *)priv); + } + } + else + { + rp23xx_timer_stop((FAR struct timer_lowerhalf_s *)priv); + } + + return OK; +} + +/**************************************************************************** + * Name: rp23xx_timer_start + ****************************************************************************/ + +static int rp23xx_timer_start(FAR struct timer_lowerhalf_s *lower) +{ + FAR struct rp23xx_timer_lowerhalf_s *priv = + (FAR struct rp23xx_timer_lowerhalf_s *)lower; + irqstate_t flags; + uint32_t now; + + if (priv->timeout == 0) + { + return -EINVAL; + } + + flags = enter_critical_section(); + + now = getreg32(priv->base + RP23XX_TIMER_TIMERAWL_OFFSET); + putreg32(now + priv->timeout, priv->base + RP23XX_TIMER_ALARM0_OFFSET); + modifyreg32(priv->base + RP23XX_TIMER_INTE_OFFSET, 0, TIMER_ALARM0_BIT); + priv->started = true; + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: rp23xx_timer_stop + ****************************************************************************/ + +static int rp23xx_timer_stop(FAR struct timer_lowerhalf_s *lower) +{ + FAR struct rp23xx_timer_lowerhalf_s *priv = + (FAR struct rp23xx_timer_lowerhalf_s *)lower; + irqstate_t flags; + + flags = enter_critical_section(); + + /* Disable the interrupt, force-disarm the alarm (writing a 1 to the ARMED + * bit disarms it) and clear any latched status. + */ + + modifyreg32(priv->base + RP23XX_TIMER_INTE_OFFSET, TIMER_ALARM0_BIT, 0); + putreg32(TIMER_ALARM0_BIT, priv->base + RP23XX_TIMER_ARMED_OFFSET); + putreg32(TIMER_ALARM0_BIT, priv->base + RP23XX_TIMER_INTR_OFFSET); + priv->started = false; + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: rp23xx_timer_getstatus + ****************************************************************************/ + +static int rp23xx_timer_getstatus(FAR struct timer_lowerhalf_s *lower, + FAR struct timer_status_s *status) +{ + FAR struct rp23xx_timer_lowerhalf_s *priv = + (FAR struct rp23xx_timer_lowerhalf_s *)lower; + irqstate_t flags; + + flags = enter_critical_section(); + + status->flags = 0; + if (priv->started) + { + status->flags |= TCFLAGS_ACTIVE; + } + + if (priv->callback != NULL) + { + status->flags |= TCFLAGS_HANDLER; + } + + status->timeout = priv->timeout; + + if (priv->started) + { + uint32_t now = getreg32(priv->base + RP23XX_TIMER_TIMERAWL_OFFSET); + uint32_t target = getreg32(priv->base + RP23XX_TIMER_ALARM0_OFFSET); + + /* Unsigned subtraction wraps mod 2^32, matching the counter. */ + + status->timeleft = target - now; + } + else + { + status->timeleft = 0; + } + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: rp23xx_timer_settimeout + ****************************************************************************/ + +static int rp23xx_timer_settimeout(FAR struct timer_lowerhalf_s *lower, + uint32_t timeout) +{ + FAR struct rp23xx_timer_lowerhalf_s *priv = + (FAR struct rp23xx_timer_lowerhalf_s *)lower; + irqstate_t flags; + + if (timeout == 0) + { + return -EINVAL; + } + + flags = enter_critical_section(); + + priv->timeout = timeout; + if (priv->started) + { + uint32_t now = getreg32(priv->base + RP23XX_TIMER_TIMERAWL_OFFSET); + putreg32(now + timeout, priv->base + RP23XX_TIMER_ALARM0_OFFSET); + } + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: rp23xx_timer_setcallback + ****************************************************************************/ + +static void rp23xx_timer_setcallback(FAR struct timer_lowerhalf_s *lower, + tccb_t callback, FAR void *arg) +{ + FAR struct rp23xx_timer_lowerhalf_s *priv = + (FAR struct rp23xx_timer_lowerhalf_s *)lower; + irqstate_t flags; + + flags = enter_critical_section(); + priv->callback = callback; + priv->arg = arg; + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: rp23xx_timer_maxtimeout + ****************************************************************************/ + +static int rp23xx_timer_maxtimeout(FAR struct timer_lowerhalf_s *lower, + FAR uint32_t *maxtimeout) +{ + UNUSED(lower); + + /* ALARM0 matches the low 32 bits of the microsecond counter. */ + + *maxtimeout = UINT32_MAX; + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: rp23xx_timer_initialize + * + * Description: + * Bind TIMER0 or TIMER1 to a /dev/timerN device. See rp23xx_timer.h. + * + ****************************************************************************/ + +int rp23xx_timer_initialize(FAR const char *devpath, int instance) +{ + FAR struct rp23xx_timer_lowerhalf_s *priv; + FAR void *handle; + + if (instance != 0 && instance != 1) + { + return -EINVAL; + } + + priv = &g_rp23xx_timer_lowerhalf[instance]; + + priv->base = (instance == 0) ? RP23XX_TIMER0_BASE : RP23XX_TIMER1_BASE; + priv->irq = (instance == 0) ? + RP23XX_TIMER0_IRQ_0 : RP23XX_TIMER1_IRQ_0; + priv->callback = NULL; + priv->arg = NULL; + priv->timeout = 0; + priv->started = false; + + /* Start from a known-idle state: interrupt disabled, alarm disarmed, any + * latched status cleared. + */ + + modifyreg32(priv->base + RP23XX_TIMER_INTE_OFFSET, TIMER_ALARM0_BIT, 0); + putreg32(TIMER_ALARM0_BIT, priv->base + RP23XX_TIMER_ARMED_OFFSET); + putreg32(TIMER_ALARM0_BIT, priv->base + RP23XX_TIMER_INTR_OFFSET); + + irq_attach(priv->irq, rp23xx_timer_interrupt, priv); + up_enable_irq(priv->irq); + + handle = timer_register(devpath, (FAR struct timer_lowerhalf_s *)priv); + if (handle == NULL) + { + up_disable_irq(priv->irq); + irq_detach(priv->irq); + return -EEXIST; + } + + return OK; +} + +#endif /* CONFIG_RP23XX_TIMER */ diff --git a/arch/arm/src/rp23xx/rp23xx_timer.h b/arch/arm/src/rp23xx/rp23xx_timer.h new file mode 100644 index 0000000000000..9372593cbeb2c --- /dev/null +++ b/arch/arm/src/rp23xx/rp23xx_timer.h @@ -0,0 +1,74 @@ +/**************************************************************************** + * arch/arm/src/rp23xx/rp23xx_timer.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_RP23XX_RP23XX_TIMER_H +#define __ARCH_ARM_SRC_RP23XX_RP23XX_TIMER_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: rp23xx_timer_initialize + * + * Description: + * Bind one of the two RP2350 system timer blocks (TIMER0 or TIMER1) to a + * /dev/timerN device. Each block is a free-running 64-bit microsecond + * counter; this driver uses its ALARM0 to implement the NuttX timer + * lower-half (single-shot and periodic timeouts, 1 us resolution, up to + * 2^32 - 1 us ~= 71.5 minutes per interval). + * + * Input Parameters: + * devpath - The full path to the timer device, e.g. "/dev/timer0". + * instance - The timer block to use: 0 for TIMER0, 1 for TIMER1. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int rp23xx_timer_initialize(FAR const char *devpath, int instance); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_RP23XX_RP23XX_TIMER_H */ diff --git a/boards/arm/rp23xx/common/src/rp23xx_common_bringup.c b/boards/arm/rp23xx/common/src/rp23xx_common_bringup.c index e293897eef03c..33645f1649090 100644 --- a/boards/arm/rp23xx/common/src/rp23xx_common_bringup.c +++ b/boards/arm/rp23xx/common/src/rp23xx_common_bringup.c @@ -45,6 +45,10 @@ #include "rp23xx_adc.h" #endif +#ifdef CONFIG_RP23XX_TIMER +#include "rp23xx_timer.h" +#endif + #ifdef CONFIG_WATCHDOG # include "rp23xx_wdt.h" #endif @@ -492,6 +496,27 @@ int rp23xx_common_bringup(void) #endif /* defined(CONFIG_ADC) && defined(CONFIG_RP23XX_ADC) */ + /* Initialize the system timer blocks as /dev/timerN. Each enabled block + * is independent; a block claimed by the tickless oneshot is excluded in + * Kconfig, so the two features can be enabled together. + */ + +#ifdef CONFIG_RP23XX_TIMER0 + ret = rp23xx_timer_initialize("/dev/timer0", 0); + if (ret < 0) + { + syslog(LOG_ERR, "Failed to initialize /dev/timer0: %d\n", ret); + } +#endif + +#ifdef CONFIG_RP23XX_TIMER1 + ret = rp23xx_timer_initialize("/dev/timer1", 1); + if (ret < 0) + { + syslog(LOG_ERR, "Failed to initialize /dev/timer1: %d\n", ret); + } +#endif + /* Initialize board neo-pixel */ #if defined(CONFIG_RP23XX_BOARD_HAS_WS2812) && defined(CONFIG_WS2812) From e8516475a45e493b5ad9321039eafbf408571798 Mon Sep 17 00:00:00 2001 From: Marco Casaroli Date: Sat, 25 Jul 2026 02:50:26 +0200 Subject: [PATCH 2/2] rp23xx: Add tickless OS support using the RP2350 hardware TIMER. The port could only run with a periodic ARM SysTick tick. Add an alarm/ oneshot lower-half backed by an RP2350 system timer block so the scheduler can run tickless, waking the CPU only when a timer actually expires. rp23xx_oneshot.c implements the ONESHOT_COUNT lower-half (mirroring the RISC-V mtimer driver). A timer block is a free-running 64-bit microsecond counter (clocked by the TICKS block, independent of the SysTick), which serves directly as the monotonic time base returned by current(), so timekeeping is exact to 1 us. The one adaptation versus a full 64-bit compare timer is that the RP2350 ALARM registers match only the low 32 bits of the counter: max_delay() is therefore capped below 2^32 counts (~71.5 minutes) so the scheduler never asks for a longer interval, and any deadline that is already due -- or that the counter reaches while the alarm is being armed -- is raised immediately through the INTF force register instead of waiting a full 32-bit wrap for the compare to match again. Enabled with CONFIG_RP23XX_SYSTIMER_TICKLESS (mutually exclusive with RP23XX_SYSTIMER_SYSTICK), which selects ONESHOT, ONESHOT_COUNT and ALARM_ARCH; up_timer_initialize() then hands the oneshot to up_alarm_set_lowerhalf(). ARCH_CHIP_RP23XX now selects ARCH_HAVE_TICKLESS. The block is selectable with CONFIG_RP23XX_SYSTIMER_TICKLESS_TIMER0 (default) or _TIMER1; the chosen block is claimed exclusively by the scheduler and is excluded from the /dev/timer driver (CONFIG_RP23XX_TIMER) in Kconfig, so the tickless clock and a /dev/timer device can coexist on different blocks. Tested on Pimoroni Pico Plus 2 (RP2350B) hardware with CONFIG_SCHED_TICKLESS and CONFIG_SCHED_TICKLESS_ALARM: the image boots to nsh and keeps accurate time -- "uptime" advances at real-time rate (17 s over a measured 17.4 s) and "sleep 4" blocks for 4.25 s of wall time -- confirming the oneshot both drives the scheduler and provides a correct 1 MHz monotonic clock. Assisted-by: Claude Opus 4.8 (1M context) Signed-off-by: Marco Casaroli --- Documentation/platforms/arm/rp23xx/index.rst | 23 ++ arch/arm/Kconfig | 1 + arch/arm/src/rp23xx/CMakeLists.txt | 4 + arch/arm/src/rp23xx/Kconfig | 34 ++ arch/arm/src/rp23xx/Make.defs | 4 + arch/arm/src/rp23xx/rp23xx_oneshot.c | 331 +++++++++++++++++++ arch/arm/src/rp23xx/rp23xx_oneshot.h | 71 ++++ arch/arm/src/rp23xx/rp23xx_timerisr.c | 14 +- 8 files changed, 480 insertions(+), 2 deletions(-) create mode 100644 arch/arm/src/rp23xx/rp23xx_oneshot.c create mode 100644 arch/arm/src/rp23xx/rp23xx_oneshot.h diff --git a/Documentation/platforms/arm/rp23xx/index.rst b/Documentation/platforms/arm/rp23xx/index.rst index b8422a9e6885d..e34b82af760ea 100644 --- a/Documentation/platforms/arm/rp23xx/index.rst +++ b/Documentation/platforms/arm/rp23xx/index.rst @@ -44,6 +44,7 @@ PSRAM Working Three modes of heap allocation described below TRNG Working Hardware RNG at /dev/random and /dev/urandom Flash MTD Working Unused flash tail as an MTD device, answers BIOC_XIPBASE Timer Working /dev/timerN on TIMER0/TIMER1, 1 us resolution +Tickless Working Optional, RP2350 TIMER via the alarm/oneshot ============== ============ ===== Installation @@ -195,6 +196,28 @@ TIMER1). With tickless disabled, both `/dev/timer0` and `/dev/timer1` are available. The `examples/timer` application can exercise a device; point `CONFIG_EXAMPLES_TIMER_DEVNAME` at the block you enabled. +Tickless OS +=========== + +By default the OS tick is a periodic ARM SysTick interrupt. The RP2350 can +instead run tickless, driving the scheduler from a hardware alarm so the CPU +is only interrupted when a timer actually expires. + +Enable `RP23XX_SYSTIMER_TICKLESS` together with `SCHED_TICKLESS` and +`SCHED_TICKLESS_ALARM`. Choose the timer block with the "Tickless timer +block" option (`RP23XX_SYSTIMER_TICKLESS_TIMER0`, the default, or +`RP23XX_SYSTIMER_TICKLESS_TIMER1`). The system time is then taken from that +block -- a free-running 64-bit microsecond counter -- and its ALARM0 provides +the next-event interrupt through the alarm/oneshot lower-half +(`rp23xx_oneshot.c`). Because the 64-bit counter is the monotonic time base, +timekeeping is exact to 1 us, and a single alarm can schedule up to ~71 +minutes ahead, so long idle periods need no wake-ups. This is mutually +exclusive with `RP23XX_SYSTIMER_SYSTICK`. + +The block chosen here is claimed exclusively by the scheduler and is removed +from the `/dev/timer` driver's choices (see the Timer section), so the tickless +clock and a `/dev/timer` device can run at the same time on different blocks. + Programming ============ diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index b107f0ab91d2f..6b2ea0d088669 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -394,6 +394,7 @@ config ARCH_CHIP_RP23XX select ARM_HAVE_DSP select ARCH_HAVE_FPU select ARCH_HAVE_CUSTOM_TESTSET + select ARCH_HAVE_TICKLESS select ARCH_BOARD_COMMON ---help--- Raspberry Pi RP23XX architectures (ARM dual Cortex-M33 or RISC-V). diff --git a/arch/arm/src/rp23xx/CMakeLists.txt b/arch/arm/src/rp23xx/CMakeLists.txt index 5c52dca2aa08b..6036cb04ebf00 100644 --- a/arch/arm/src/rp23xx/CMakeLists.txt +++ b/arch/arm/src/rp23xx/CMakeLists.txt @@ -60,6 +60,10 @@ if(CONFIG_RP23XX_TIMER) list(APPEND SRCS rp23xx_timer.c) endif() +if(CONFIG_RP23XX_SYSTIMER_TICKLESS) + list(APPEND SRCS rp23xx_oneshot.c) +endif() + if(CONFIG_RP23XX_I2C) list(APPEND SRCS rp23xx_i2c.c) endif() diff --git a/arch/arm/src/rp23xx/Kconfig b/arch/arm/src/rp23xx/Kconfig index e3f2f499a0578..cb71ef9f529bf 100644 --- a/arch/arm/src/rp23xx/Kconfig +++ b/arch/arm/src/rp23xx/Kconfig @@ -23,6 +23,40 @@ config RP23XX_SYSTIMER_SYSTICK ---help--- Use ARM SysTick. +config RP23XX_SYSTIMER_TICKLESS + bool "Tickless OS using the RP2350 hardware TIMER" + default n + depends on !RP23XX_SYSTIMER_SYSTICK + select ONESHOT + select ONESHOT_COUNT + select ALARM_ARCH + ---help--- + Use an RP2350 system timer block (TIMER0 or TIMER1, chosen below) + as the OS time source and drive the scheduler through the + alarm/oneshot interface. Each block is a free-running 64-bit + microsecond counter, independent of the ARM SysTick. This is the + arch-side support required by CONFIG_SCHED_TICKLESS (with + CONFIG_SCHED_TICKLESS_ALARM) and gives long tickless idle intervals + -- up to ~71 minutes per alarm -- with 1 us resolution. + +choice + prompt "Tickless timer block" + default RP23XX_SYSTIMER_TICKLESS_TIMER0 + depends on RP23XX_SYSTIMER_TICKLESS + ---help--- + Which RP2350 system timer block drives the tickless OS clock. The + block used here is claimed exclusively by the scheduler; the other + block stays available for the /dev/timer driver (CONFIG_RP23XX_TIMER), + so both features can be enabled at once. + +config RP23XX_SYSTIMER_TICKLESS_TIMER0 + bool "TIMER0" + +config RP23XX_SYSTIMER_TICKLESS_TIMER1 + bool "TIMER1" + +endchoice + ##################################################################### # UART Configuration ##################################################################### diff --git a/arch/arm/src/rp23xx/Make.defs b/arch/arm/src/rp23xx/Make.defs index fb41bb5c6a8d3..8f59e6aac6d7b 100644 --- a/arch/arm/src/rp23xx/Make.defs +++ b/arch/arm/src/rp23xx/Make.defs @@ -64,6 +64,10 @@ ifeq ($(CONFIG_RP23XX_TIMER),y) CHIP_CSRCS += rp23xx_timer.c endif +ifeq ($(CONFIG_RP23XX_SYSTIMER_TICKLESS),y) +CHIP_CSRCS += rp23xx_oneshot.c +endif + ifeq ($(CONFIG_RP23XX_I2C),y) CHIP_CSRCS += rp23xx_i2c.c endif diff --git a/arch/arm/src/rp23xx/rp23xx_oneshot.c b/arch/arm/src/rp23xx/rp23xx_oneshot.c new file mode 100644 index 0000000000000..f052cc0dc4857 --- /dev/null +++ b/arch/arm/src/rp23xx/rp23xx_oneshot.c @@ -0,0 +1,331 @@ +/**************************************************************************** + * arch/arm/src/rp23xx/rp23xx_oneshot.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 + * + * A oneshot lower-half backed by an RP2350 system timer block (TIMER0 or + * TIMER1, selected by CONFIG_RP23XX_SYSTIMER_TICKLESS_TIMERn), used to drive + * the tickless scheduler via up_alarm_set_lowerhalf(). The block is a + * free-running 64-bit counter incremented once per microsecond (the 1 MHz + * tick is set up by the TICKS block in rp23xx_clock.c), and serves directly + * as the monotonic time base returned by current(). The other block is left + * free for the /dev/timer driver. + * + * The one wrinkle versus a full 64-bit compare timer (e.g. the RISC-V mtimer + * this mirrors) is that the RP2350 ALARM registers match only the low 32 + * bits of the counter. max_delay() is therefore capped below 2^32 counts so + * the scheduler never asks for a longer interval, and any deadline that is + * already due (or that the counter reaches while the alarm is being armed) + * is raised at once through the INTF force register rather than waiting a + * full 32-bit wrap (~71.5 minutes) for the compare to match again. + * + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include +#include + +#include "chip.h" +#include "arm_internal.h" +#include "rp23xx_oneshot.h" +#include "hardware/rp23xx_timer.h" +#include "hardware/rp23xx_memorymap.h" + +#ifdef CONFIG_RP23XX_SYSTIMER_TICKLESS + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* This driver uses ALARM0 of TIMER0; alarm 0 is bit 0 in the ARMED, INTE, + * INTF, INTR and INTS registers. + */ + +#define ONESHOT_ALARM0_BIT (1 << 0) + +/* The counter is clocked at 1 MHz (1 us per count). */ + +#define ONESHOT_FREQ_HZ 1000000 + +/* The ALARM compare is only 32 bits wide, so never schedule further ahead + * than this many counts. + */ + +#define ONESHOT_MAX_COUNTS ((clkcnt_t)UINT32_MAX) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Cast-compatible with struct oneshot_lowerhalf_s (embedded first). */ + +struct rp23xx_oneshot_lowerhalf_s +{ + struct oneshot_lowerhalf_s lower; /* Lower-half instance (must be first) */ + uint32_t base; /* TIMER block base address */ + int irq; /* ALARM0 interrupt number */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static clkcnt_t rp23xx_oneshot_max_delay(FAR struct oneshot_lowerhalf_s *l); +static clkcnt_t rp23xx_oneshot_current(FAR struct oneshot_lowerhalf_s *l); +static void rp23xx_oneshot_start_absolute(FAR struct oneshot_lowerhalf_s *l, + clkcnt_t expected); +static void rp23xx_oneshot_start(FAR struct oneshot_lowerhalf_s *l, + clkcnt_t delta); +static void rp23xx_oneshot_cancel(FAR struct oneshot_lowerhalf_s *l); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct oneshot_operations_s g_rp23xx_oneshot_ops = +{ + .current = rp23xx_oneshot_current, + .start = rp23xx_oneshot_start, + .start_absolute = rp23xx_oneshot_start_absolute, + .cancel = rp23xx_oneshot_cancel, + .max_delay = rp23xx_oneshot_max_delay, +}; + +static struct rp23xx_oneshot_lowerhalf_s g_rp23xx_oneshot = +{ + .lower.ops = &g_rp23xx_oneshot_ops, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: rp23xx_oneshot_count + * + * Description: + * Read the free-running 64-bit microsecond counter, guarding against a + * carry from the low word into the high word between the two reads. + * + ****************************************************************************/ + +static uint64_t +rp23xx_oneshot_count(FAR struct rp23xx_oneshot_lowerhalf_s *p) +{ + uint32_t hi = getreg32(p->base + RP23XX_TIMER_TIMERAWH_OFFSET); + uint32_t lo; + uint32_t next; + + for (; ; ) + { + lo = getreg32(p->base + RP23XX_TIMER_TIMERAWL_OFFSET); + next = getreg32(p->base + RP23XX_TIMER_TIMERAWH_OFFSET); + if (hi == next) + { + break; + } + + hi = next; + } + + return ((uint64_t)hi << 32) | lo; +} + +/**************************************************************************** + * Name: rp23xx_oneshot_setcompare + * + * Description: + * Arm ALARM0 for the absolute counter value "target". Must be called with + * interrupts disabled. If the target is already due -- or the counter + * reaches it while the alarm is being armed -- the interrupt is forced + * immediately through INTF instead of waiting a full 32-bit wrap for the + * compare to match. + * + ****************************************************************************/ + +static void +rp23xx_oneshot_setcompare(FAR struct rp23xx_oneshot_lowerhalf_s *priv, + uint64_t target) +{ + /* Clear any stale force left from a previous expiry. */ + + modifyreg32(priv->base + RP23XX_TIMER_INTF_OFFSET, ONESHOT_ALARM0_BIT, 0); + + if ((int64_t)(target - rp23xx_oneshot_count(priv)) <= 0) + { + /* Already due: raise the interrupt now. */ + + modifyreg32(priv->base + RP23XX_TIMER_INTF_OFFSET, 0, + ONESHOT_ALARM0_BIT); + } + else + { + /* Writing ALARM0 arms it against the low 32 bits of the counter. */ + + putreg32((uint32_t)target, priv->base + RP23XX_TIMER_ALARM0_OFFSET); + + /* Close the race: if the counter reached the target while we were + * arming, force so we do not wait a full wrap for the match. + */ + + if ((int64_t)(rp23xx_oneshot_count(priv) - target) >= 0) + { + modifyreg32(priv->base + RP23XX_TIMER_INTF_OFFSET, 0, + ONESHOT_ALARM0_BIT); + } + } +} + +/**************************************************************************** + * Name: rp23xx_oneshot_interrupt + ****************************************************************************/ + +static int rp23xx_oneshot_interrupt(int irq, FAR void *context, + FAR void *arg) +{ + FAR struct rp23xx_oneshot_lowerhalf_s *priv = arg; + + /* Disarm the alarm, drop any force and clear the latched match. */ + + putreg32(ONESHOT_ALARM0_BIT, priv->base + RP23XX_TIMER_ARMED_OFFSET); + modifyreg32(priv->base + RP23XX_TIMER_INTF_OFFSET, ONESHOT_ALARM0_BIT, 0); + putreg32(ONESHOT_ALARM0_BIT, priv->base + RP23XX_TIMER_INTR_OFFSET); + + oneshot_process_callback(&priv->lower); + return OK; +} + +/**************************************************************************** + * Name: rp23xx_oneshot_max_delay + ****************************************************************************/ + +static clkcnt_t rp23xx_oneshot_max_delay(FAR struct oneshot_lowerhalf_s *l) +{ + UNUSED(l); + return ONESHOT_MAX_COUNTS; +} + +/**************************************************************************** + * Name: rp23xx_oneshot_current + ****************************************************************************/ + +static clkcnt_t rp23xx_oneshot_current(FAR struct oneshot_lowerhalf_s *l) +{ + return rp23xx_oneshot_count((FAR struct rp23xx_oneshot_lowerhalf_s *)l); +} + +/**************************************************************************** + * Name: rp23xx_oneshot_start_absolute + ****************************************************************************/ + +static void rp23xx_oneshot_start_absolute(FAR struct oneshot_lowerhalf_s *l, + clkcnt_t expected) +{ + FAR struct rp23xx_oneshot_lowerhalf_s *priv = + (FAR struct rp23xx_oneshot_lowerhalf_s *)l; + irqstate_t flags = up_irq_save(); + + rp23xx_oneshot_setcompare(priv, expected); + up_irq_restore(flags); +} + +/**************************************************************************** + * Name: rp23xx_oneshot_start + ****************************************************************************/ + +static void rp23xx_oneshot_start(FAR struct oneshot_lowerhalf_s *l, + clkcnt_t delta) +{ + FAR struct rp23xx_oneshot_lowerhalf_s *priv = + (FAR struct rp23xx_oneshot_lowerhalf_s *)l; + irqstate_t flags = up_irq_save(); + + rp23xx_oneshot_setcompare(priv, rp23xx_oneshot_count(priv) + delta); + up_irq_restore(flags); +} + +/**************************************************************************** + * Name: rp23xx_oneshot_cancel + ****************************************************************************/ + +static void rp23xx_oneshot_cancel(FAR struct oneshot_lowerhalf_s *l) +{ + FAR struct rp23xx_oneshot_lowerhalf_s *priv = + (FAR struct rp23xx_oneshot_lowerhalf_s *)l; + irqstate_t flags = up_irq_save(); + + putreg32(ONESHOT_ALARM0_BIT, priv->base + RP23XX_TIMER_ARMED_OFFSET); + modifyreg32(priv->base + RP23XX_TIMER_INTF_OFFSET, ONESHOT_ALARM0_BIT, 0); + putreg32(ONESHOT_ALARM0_BIT, priv->base + RP23XX_TIMER_INTR_OFFSET); + up_irq_restore(flags); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: rp23xx_oneshot_initialize + * + * Description: + * Initialise the selected system timer block (TIMER0 or TIMER1) as a + * oneshot lower-half. See rp23xx_oneshot.h. + * + ****************************************************************************/ + +FAR struct oneshot_lowerhalf_s *rp23xx_oneshot_initialize(void) +{ + FAR struct rp23xx_oneshot_lowerhalf_s *priv = &g_rp23xx_oneshot; + +#ifdef CONFIG_RP23XX_SYSTIMER_TICKLESS_TIMER1 + priv->base = RP23XX_TIMER1_BASE; + priv->irq = RP23XX_TIMER1_IRQ_0; +#else + priv->base = RP23XX_TIMER0_BASE; + priv->irq = RP23XX_TIMER0_IRQ_0; +#endif + + oneshot_count_init(&priv->lower, ONESHOT_FREQ_HZ); + + /* Start from a known-idle state: alarm disarmed, no force, status clear, + * then enable the ALARM0 interrupt at the peripheral and the NVIC. + */ + + putreg32(ONESHOT_ALARM0_BIT, priv->base + RP23XX_TIMER_ARMED_OFFSET); + modifyreg32(priv->base + RP23XX_TIMER_INTF_OFFSET, ONESHOT_ALARM0_BIT, 0); + putreg32(ONESHOT_ALARM0_BIT, priv->base + RP23XX_TIMER_INTR_OFFSET); + modifyreg32(priv->base + RP23XX_TIMER_INTE_OFFSET, 0, ONESHOT_ALARM0_BIT); + + irq_attach(priv->irq, rp23xx_oneshot_interrupt, priv); + up_enable_irq(priv->irq); + + return &priv->lower; +} + +#endif /* CONFIG_RP23XX_SYSTIMER_TICKLESS */ diff --git a/arch/arm/src/rp23xx/rp23xx_oneshot.h b/arch/arm/src/rp23xx/rp23xx_oneshot.h new file mode 100644 index 0000000000000..eec8184a79c11 --- /dev/null +++ b/arch/arm/src/rp23xx/rp23xx_oneshot.h @@ -0,0 +1,71 @@ +/**************************************************************************** + * arch/arm/src/rp23xx/rp23xx_oneshot.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_RP23XX_RP23XX_ONESHOT_H +#define __ARCH_ARM_SRC_RP23XX_RP23XX_ONESHOT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: rp23xx_oneshot_initialize + * + * Description: + * Initialise the RP2350 system timer TIMER0 as a oneshot lower-half and + * return it, ready to be handed to up_alarm_set_lowerhalf() to drive the + * tickless scheduler. TIMER0 is a free-running 64-bit microsecond counter + * whose ALARM0 provides the compare event. + * + * Returned Value: + * The oneshot lower-half instance on success; NULL never happens (the + * instance is statically allocated). + * + ****************************************************************************/ + +FAR struct oneshot_lowerhalf_s *rp23xx_oneshot_initialize(void); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_RP23XX_RP23XX_ONESHOT_H */ diff --git a/arch/arm/src/rp23xx/rp23xx_timerisr.c b/arch/arm/src/rp23xx/rp23xx_timerisr.c index f0ef2171cae7c..f49778e5252ad 100644 --- a/arch/arm/src/rp23xx/rp23xx_timerisr.c +++ b/arch/arm/src/rp23xx/rp23xx_timerisr.c @@ -40,6 +40,11 @@ #include "chip.h" #include "systick.h" +#ifdef CONFIG_RP23XX_SYSTIMER_TICKLESS +# include +# include "rp23xx_oneshot.h" +#endif + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -76,7 +81,8 @@ * of the systems. * ****************************************************************************/ -#ifndef CONFIG_RP23XX_SYSTIMER_SYSTICK +#if !defined(CONFIG_RP23XX_SYSTIMER_SYSTICK) && \ + !defined(CONFIG_RP23XX_SYSTIMER_TICKLESS) static int rp23xx_timerisr(int irq, uint32_t *regs, void *arg) { /* Process timer interrupt */ @@ -110,7 +116,11 @@ void up_timer_initialize(void) regval |= (NVIC_SYSH_PRIORITY_DEFAULT << NVIC_SYSH_PRIORITY_PR15_SHIFT); putreg32(regval, NVIC_SYSH12_15_PRIORITY); -#ifdef CONFIG_RP23XX_SYSTIMER_SYSTICK +#if defined(CONFIG_RP23XX_SYSTIMER_TICKLESS) + /* Drive the tickless scheduler from the selected RP2350 hardware TIMER. */ + + up_alarm_set_lowerhalf(rp23xx_oneshot_initialize()); +#elif defined(CONFIG_RP23XX_SYSTIMER_SYSTICK) up_timer_set_lowerhalf(systick_initialize(true, SYSTICK_CLOCK, -1)); #else