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

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

commit b00d7005b063023920efa36173ff3ebfa442b8e7
Author: Marco Casaroli <[email protected]>
AuthorDate: Sat Jul 25 02:24:12 2026 +0200

    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) <[email protected]>
    Signed-off-by: Marco Casaroli <[email protected]>
---
 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 ++++
 .../arm/rp23xx/common/src/rp23xx_common_bringup.c  |  25 ++
 7 files changed, 566 insertions(+)

diff --git a/Documentation/platforms/arm/rp23xx/index.rst 
b/Documentation/platforms/arm/rp23xx/index.rst
index e788ace692c..b8422a9e688 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 0d21226da91..5c52dca2aa0 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 0a4f29e33a0..e3f2f499a05 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 71f3475b6c9..fb41bb5c6a8 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 00000000000..91e4e66ccf4
--- /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 <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/timers/timer.h>
+
+#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 00000000000..9372593cbeb
--- /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 <nuttx/config.h>
+
+/****************************************************************************
+ * 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 e293897eef0..33645f16490 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)

Reply via email to