mirror of
https://github.com/apache/nuttx.git
synced 2026-08-01 20:28:58 +00:00
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) <noreply@anthropic.com> Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
This commit is contained in:
parent
216edd74db
commit
f0e4f61add
5 changed files with 288 additions and 0 deletions
|
|
@ -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
|
||||
============
|
||||
|
||||
|
|
|
|||
|
|
@ -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})
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
231
arch/arm/src/rp23xx/rp23xx_rtc.c
Normal file
231
arch/arm/src/rp23xx/rp23xx_rtc.c
Normal file
|
|
@ -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 */
|
||||
Loading…
Add table
Add a link
Reference in a new issue