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