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

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


The following commit(s) were added to refs/heads/master by this push:
     new f0e4f61add8 arch/arm/rp23xx: Add RTC using the POWMAN always-on timer.
f0e4f61add8 is described below

commit f0e4f61add82b9389e41e3ba39b55d8ca0d10dff
Author: Marco Casaroli <[email protected]>
AuthorDate: Sat Jul 25 01:41:08 2026 +0200

    arch/arm/rp23xx: Add RTC using the POWMAN always-on timer.
    
    The RP2350 has no dedicated RTC block (the RP2040 one was removed).  Use the
    POWMAN always-on timer instead: a 64-bit millisecond counter clocked from 
the
    low-power oscillator, so it keeps running across warm resets.
    
    Enabling CONFIG_RP23XX_RTC selects CONFIG_RTC and builds the driver, which
    implements the simple up_rtc_initialize()/up_rtc_time()/up_rtc_settime()
    interface backing the system clock (seconds resolution).  
up_rtc_initialize()
    sources the timer from the low-power oscillator at a 1 kHz tick and starts 
it,
    preserving the value when the timer is already running.  POWMAN register 
writes
    carry the required 0x5afe password.
    
    Document the RTC on the rp23xx platform page: an "RTC" section explaining 
how
    the POWMAN always-on timer is used (LPOSC clocking, the 0x5afe password and 
the
    four 16-bit time registers, stopping the counter to reload it), with 
reference
    links to the RP2350 datasheet Power chapter and the pico-sdk 
hardware_powman /
    pico_aon_timer implementation the driver mirrors.  The same references are
    noted in the driver header.
    
    Assisted-by: Claude Opus 4.8 (1M context) <[email protected]>
    Signed-off-by: Marco Casaroli <[email protected]>
---
 Documentation/platforms/arm/rp23xx/index.rst |  39 +++++
 arch/arm/src/rp23xx/CMakeLists.txt           |   4 +
 arch/arm/src/rp23xx/Kconfig                  |  10 ++
 arch/arm/src/rp23xx/Make.defs                |   4 +
 arch/arm/src/rp23xx/rp23xx_rtc.c             | 231 +++++++++++++++++++++++++++
 5 files changed, 288 insertions(+)

diff --git a/Documentation/platforms/arm/rp23xx/index.rst 
b/Documentation/platforms/arm/rp23xx/index.rst
index e34b82af760..2b673aba40e 100644
--- a/Documentation/platforms/arm/rp23xx/index.rst
+++ b/Documentation/platforms/arm/rp23xx/index.rst
@@ -45,6 +45,7 @@ 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
+RTC              Working       POWMAN always-on timer, backs the system clock
 ==============   ============  =====
 
 Installation
@@ -218,6 +219,44 @@ 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.
 
+RTC
+===
+
+The RP2350 has no dedicated RTC block -- the one on the RP2040 was removed --
+so NuttX drives the real-time clock from the POWMAN *always-on timer*
+instead. This is a 64-bit millisecond counter that can be clocked from the
+low-power oscillator (LPOSC, nominally 32.768 kHz), so it keeps counting
+across warm resets and through the low-power states managed by POWMAN.
+
+Enable it with `RP23XX_RTC` (which selects `RTC`). The driver
+(`rp23xx_rtc.c`) implements the lightweight `up_rtc_initialize()`,
+`up_rtc_time()` and `up_rtc_settime()` interface that backs the NuttX system
+clock at one-second resolution. `up_rtc_initialize()` sources the timer from
+the low-power oscillator at a 1 kHz tick and starts it, but preserves the
+current value if the bootrom (or a previous boot) already left it running, so
+the wall-clock time survives a warm reset.
+
+Two POWMAN details the driver has to honour:
+
+* Every POWMAN register write must carry the `0x5afe` password in its top 16
+  bits or it is silently ignored, so only the low 16 bits of each register
+  hold data. The 64-bit time is therefore written and read through four
+  16-bit registers, and bit operations use the atomic `SET`/`CLR` register
+  aliases.
+* The counter must be stopped (`TIMER.RUN` cleared) while a new value is
+  loaded and then restarted, otherwise the write is not latched cleanly.
+
+The implementation follows the POWMAN "Always-on Timer" description in the
+Power chapter of the `RP2350 datasheet
+<https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf>`_ and the
+reference `pico-sdk hardware_powman
+<https://github.com/raspberrypi/pico-sdk/tree/master/src/rp2_common/hardware_powman>`_
+library (`powman_timer_start_lposc()`, `powman_timer_set_ms()` and
+`powman_timer_get_ms()`), which the pico-sdk in turn exposes through its
+`pico_aon_timer
+<https://github.com/raspberrypi/pico-sdk/tree/master/src/rp2_common/pico_aon_timer>`_
+wrapper.
+
 Programming
 ============
 
diff --git a/arch/arm/src/rp23xx/CMakeLists.txt 
b/arch/arm/src/rp23xx/CMakeLists.txt
index 814a1007859..6bd1eeaf358 100644
--- a/arch/arm/src/rp23xx/CMakeLists.txt
+++ b/arch/arm/src/rp23xx/CMakeLists.txt
@@ -109,4 +109,8 @@ if(CONFIG_RP23XX_PSRAM)
   list(APPEND SRCS rp23xx_psram.c rp23xx_heaps.c)
 endif()
 
+if(CONFIG_RP23XX_RTC)
+  list(APPEND SRCS rp23xx_rtc.c)
+endif()
+
 target_sources(arch PRIVATE ${SRCS})
diff --git a/arch/arm/src/rp23xx/Kconfig b/arch/arm/src/rp23xx/Kconfig
index 0713e4f8721..7fd81b6f909 100644
--- a/arch/arm/src/rp23xx/Kconfig
+++ b/arch/arm/src/rp23xx/Kconfig
@@ -868,6 +868,16 @@ config RP23XX_RNG
                /dev/random (and /dev/urandom when CONFIG_DEV_URANDOM selects 
the
                architecture source) via the TRNG entropy register (EHR).
 
+config RP23XX_RTC
+       bool "Enable RTC (POWMAN always-on timer)"
+       default n
+       select RTC
+       ---help---
+               Use the RP2350 POWMAN always-on timer as the real-time clock 
backing
+               the system clock.  The RP2350 has no dedicated RTC block; the 
POWMAN
+               timer is a 64-bit millisecond counter clocked from the low-power
+               oscillator, so it keeps running across warm resets.
+
 config RP23XX_ADC
        bool "Enable ADC Support"
        default n
diff --git a/arch/arm/src/rp23xx/Make.defs b/arch/arm/src/rp23xx/Make.defs
index 040f23ff868..75366113e42 100644
--- a/arch/arm/src/rp23xx/Make.defs
+++ b/arch/arm/src/rp23xx/Make.defs
@@ -113,3 +113,7 @@ ifeq ($(CONFIG_RP23XX_PSRAM),y)
 CHIP_CSRCS += rp23xx_psram.c
 CHIP_CSRCS += rp23xx_heaps.c
 endif
+
+ifeq ($(CONFIG_RP23XX_RTC),y)
+CHIP_CSRCS += rp23xx_rtc.c
+endif
diff --git a/arch/arm/src/rp23xx/rp23xx_rtc.c b/arch/arm/src/rp23xx/rp23xx_rtc.c
new file mode 100644
index 00000000000..be78ad3f64d
--- /dev/null
+++ b/arch/arm/src/rp23xx/rp23xx_rtc.c
@@ -0,0 +1,231 @@
+/****************************************************************************
+ * arch/arm/src/rp23xx/rp23xx_rtc.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
+ *
+ * The RP2350 has no dedicated RTC block (the RP2040 one was removed).  This
+ * driver uses the POWMAN always-on timer instead: a 64-bit millisecond
+ * counter, clocked from the low-power oscillator so it keeps running across
+ * warm resets.  It backs the NuttX system clock through the simple up_rtc_*
+ * interface (seconds resolution).
+ *
+ * Reference: the POWMAN "Always-on Timer" in the Power chapter of the RP2350
+ * datasheet (https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf)
+ * and the pico-sdk hardware_powman library
+ * (https://github.com/raspberrypi/pico-sdk, src/rp2_common/hardware_powman),
+ * whose powman_timer_start_lposc()/set_ms()/get_ms() this mirrors.
+ *
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+#include <errno.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+
+#include "arm_internal.h"
+#include "hardware/rp23xx_powman.h"
+
+#ifdef CONFIG_RTC
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Every POWMAN write must carry this password in the top 16 bits, otherwise
+ * it is ignored (and flagged in BADPASSWD).  Only the low 16 bits are data,
+ * which is why the 64-bit time is split across four registers.
+ */
+
+#define POWMAN_PASSWORD   0x5afe0000
+
+/* Atomic set/clear aliases of a bus register (RP2350 memory map). */
+
+#define POWMAN_SET_ALIAS  0x2000
+#define POWMAN_CLR_ALIAS  0x3000
+
+/* Nominal low-power oscillator frequency. */
+
+#define LPOSC_FREQ_HZ     32768
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/* Variable determines the state of the RTC module; set once the counter is
+ * running so the rest of the system knows the RTC is available.
+ */
+
+volatile bool g_rtc_enabled = false;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static inline void powman_write(uint32_t reg, uint32_t value)
+{
+  putreg32(POWMAN_PASSWORD | (value & 0xffff), reg);
+}
+
+static inline void powman_setbits(uint32_t reg, uint32_t bits)
+{
+  putreg32(POWMAN_PASSWORD | bits, reg + POWMAN_SET_ALIAS);
+}
+
+static inline void powman_clrbits(uint32_t reg, uint32_t bits)
+{
+  putreg32(POWMAN_PASSWORD | bits, reg + POWMAN_CLR_ALIAS);
+}
+
+/* Read the 64-bit millisecond counter, guarding against a carry from the low
+ * word to the high word between the two reads.
+ */
+
+static uint64_t powman_get_ms(void)
+{
+  uint32_t hi = getreg32(RP23XX_POWMAN_READ_TIME_UPPER);
+  uint32_t lo;
+
+  for (; ; )
+    {
+      uint32_t next;
+
+      lo   = getreg32(RP23XX_POWMAN_READ_TIME_LOWER);
+      next = getreg32(RP23XX_POWMAN_READ_TIME_UPPER);
+      if (hi == next)
+        {
+          break;
+        }
+
+      hi = next;
+    }
+
+  return ((uint64_t)hi << 32) | lo;
+}
+
+/* The counter must be stopped while its value is (re)loaded. */
+
+static void powman_set_ms(uint64_t ms)
+{
+  bool running =
+    (getreg32(RP23XX_POWMAN_TIMER) & RP23XX_POWMAN_TIMER_RUN) != 0;
+
+  if (running)
+    {
+      powman_clrbits(RP23XX_POWMAN_TIMER, RP23XX_POWMAN_TIMER_RUN);
+    }
+
+  powman_write(RP23XX_POWMAN_SET_TIME_15TO0,  (uint32_t)(ms));
+  powman_write(RP23XX_POWMAN_SET_TIME_31TO16, (uint32_t)(ms >> 16));
+  powman_write(RP23XX_POWMAN_SET_TIME_47TO32, (uint32_t)(ms >> 32));
+  powman_write(RP23XX_POWMAN_SET_TIME_63TO48, (uint32_t)(ms >> 48));
+
+  if (running)
+    {
+      powman_setbits(RP23XX_POWMAN_TIMER, RP23XX_POWMAN_TIMER_RUN);
+    }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_rtc_initialize
+ *
+ * Description:
+ *   Initialize the POWMAN always-on timer.  If it is not already running
+ *   (e.g. left running by the bootrom), source it from the low-power
+ *   oscillator at a 1 kHz tick and start it.  Preserves the current value
+ *   when it is already running.
+ *
+ ****************************************************************************/
+
+int up_rtc_initialize(void)
+{
+  if ((getreg32(RP23XX_POWMAN_TIMER) & RP23XX_POWMAN_TIMER_RUN) == 0)
+    {
+      uint32_t khz_int  = LPOSC_FREQ_HZ / 1000;
+      uint32_t khz_frac = (LPOSC_FREQ_HZ % 1000) * 65536 / 1000;
+      int timeout       = 1000000;
+
+      powman_write(RP23XX_POWMAN_LPOSC_FREQ_KHZ_INT, khz_int);
+      powman_write(RP23XX_POWMAN_LPOSC_FREQ_KHZ_FRAC, khz_frac);
+      powman_setbits(RP23XX_POWMAN_TIMER, RP23XX_POWMAN_TIMER_USE_LPOSC);
+      powman_setbits(RP23XX_POWMAN_TIMER, RP23XX_POWMAN_TIMER_RUN);
+
+      /* Wait for the timer to accept the low-power oscillator source, but do
+       * not spin forever if it never does.
+       */
+
+      while ((getreg32(RP23XX_POWMAN_TIMER) &
+              RP23XX_POWMAN_TIMER_USING_LPOSC) == 0 && --timeout > 0)
+        {
+        }
+    }
+
+  g_rtc_enabled = true;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: up_rtc_time
+ *
+ * Description:
+ *   Get the current time in seconds.
+ *
+ ****************************************************************************/
+
+time_t up_rtc_time(void)
+{
+  return (time_t)(powman_get_ms() / 1000);
+}
+
+/****************************************************************************
+ * Name: up_rtc_settime
+ *
+ * Description:
+ *   Set the RTC to the provided time value.
+ *
+ ****************************************************************************/
+
+int up_rtc_settime(FAR const struct timespec *tp)
+{
+  irqstate_t flags;
+  uint64_t ms;
+
+  ms = (uint64_t)tp->tv_sec * 1000 + tp->tv_nsec / 1000000;
+
+  flags = enter_critical_section();
+  powman_set_ms(ms);
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+#endif /* CONFIG_RTC */

Reply via email to