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
commit 275ab8e83f1be8abbf74fdb1fa49c162ab721d5c Author: Jorge Guzman <[email protected]> AuthorDate: Sun Oct 25 02:42:20 2026 -0300 arch/arm/gd32w51x: add the deep-sleep power management With CONFIG_PM, the PM_STANDBY state of the IDLE domain is the PMU deep-sleep mode: the core and bus clocks stop, SRAM and registers keep their contents, the LDO runs in low power and low-driver mode. - idle: custom up_idle() that runs the PM state machine; PM_STANDBY enters deep-sleep and goes back to PM_NORMAL on wake-up. - pm: arm_pminitialize() keeps the debugger connection in deep-sleep (DBG_CTL0 DSLP_HOLD), so that a sleeping board can still be programmed. gd32w51x_pmstop() waits for the console, sleeps, sets the PLL up again and corrects the system time with clock_resynchronize(). WFI only wakes up on an interrupt that could be taken, and the critical section raises BASEPRI: the wait masks the interrupts with PRIMASK instead. - rtc: the wake-up timer (RTC clock / 16, EXTI line 24) ends the sleep after CONFIG_GD32W51X_PM_WAKEUP_MS, so that the timers keep running; up_rtc_getdatetime_with_subseconds() gives the RTC time with its sub-second counter, for an accurate correction. - serial: received characters are reported as PM activity. - boards/gd-xd-w515-eval: new pm configuration, NSH with the power management (activity governor), its procfs and the RTC. Tested on the GD-xD-W515-EVAL: idle for 33 s, the board spent 22 s in deep-sleep, NSH answered within 1 s, and after a minute mostly asleep the system time still matched the host within the 1 s resolution of "date". Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Jorge Guzman <[email protected]> --- .../arm/gd32w51x/boards/gd-xd-w515-eval/index.rst | 25 +++ arch/arm/Kconfig | 2 + arch/arm/src/gd32w51x/CMakeLists.txt | 5 + arch/arm/src/gd32w51x/Kconfig | 22 +++ arch/arm/src/gd32w51x/Make.defs | 6 +- arch/arm/src/gd32w51x/gd32w51x_idle.c | 131 ++++++++++++++++ arch/arm/src/gd32w51x/gd32w51x_pm.c | 173 +++++++++++++++++++++ arch/arm/src/gd32w51x/gd32w51x_pm.h | 85 ++++++++++ arch/arm/src/gd32w51x/gd32w51x_rtc.c | 163 ++++++++++++++++++- arch/arm/src/gd32w51x/gd32w51x_serial.c | 7 + .../gd32w51x/gd-xd-w515-eval/configs/pm/defconfig | 54 +++++++ 11 files changed, 671 insertions(+), 2 deletions(-) diff --git a/Documentation/platforms/arm/gd32w51x/boards/gd-xd-w515-eval/index.rst b/Documentation/platforms/arm/gd32w51x/boards/gd-xd-w515-eval/index.rst index 338fd3bd06e..82d48b06b2f 100644 --- a/Documentation/platforms/arm/gd32w51x/boards/gd-xd-w515-eval/index.rst +++ b/Documentation/platforms/arm/gd32w51x/boards/gd-xd-w515-eval/index.rst @@ -262,3 +262,28 @@ reboot: nsh> echo hello > /flash/hello.txt nsh> cat /flash/hello.txt hello + +pm +-- + +NSH with the power management (``CONFIG_PM``, activity governor) and the +RTC. After a few seconds without activity the governor selects +``PM_STANDBY``, which is the deep-sleep mode of the GD32W51x: the core and +bus clocks stop, the SRAM and the registers keep their contents. The chip +wakes up on any EXTI interrupt and on the RTC wake-up timer every +``CONFIG_GD32W51X_PM_WAKEUP_MS`` (1 s), sets the PLL up again and corrects +the system time with the time spent asleep, read from the RTC with its +sub-second counter. Timers may run late by up to that period while the +board sleeps, and a character received while the board sleeps is lost: +press Enter until NSH answers, then the console activity keeps the board +awake. The debugger connection is kept in deep-sleep, so a sleeping board +can still be programmed. + +:: + + nsh> cat /proc/pm/state0 + DOMAIN0 WAKE SLEEP TOTAL + normal 20s 17% 1s 0% 21s 18% + idle 15s 13% 0s 0% 15s 13% + standby 76s 67% 0s 0% 76s 67% + sleep 0s 0% 0s 0% 0s 0% diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index a121045fb85..9a7c40cbe6b 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -171,6 +171,8 @@ config ARCH_CHIP_GD32W51X select ARCH_HAVE_PROGMEM select ARCH_HAVE_PWM_MULTICHAN select ARCH_HAVE_RESET + select ARCH_HAVE_RTC_SUBSECONDS if RTC + select ARCH_IDLE_CUSTOM select ARM_HAVE_DSP select ARM_HAVE_MPU_UNIFIED select ARMV8M_HAVE_STACKCHECK diff --git a/arch/arm/src/gd32w51x/CMakeLists.txt b/arch/arm/src/gd32w51x/CMakeLists.txt index 7383450c949..d7026c7fdcb 100644 --- a/arch/arm/src/gd32w51x/CMakeLists.txt +++ b/arch/arm/src/gd32w51x/CMakeLists.txt @@ -28,8 +28,13 @@ set(SRCS gd32w51x_lowputc.c gd32w51x_serial.c gd32w51x_tzinit.c + gd32w51x_idle.c gd32w51x_gpio.c) +if(CONFIG_PM) + list(APPEND SRCS gd32w51x_pm.c) +endif() + if(CONFIG_RTC) list(APPEND SRCS gd32w51x_rtc.c) endif() diff --git a/arch/arm/src/gd32w51x/Kconfig b/arch/arm/src/gd32w51x/Kconfig index 7f11e7707fc..7c0a457728c 100644 --- a/arch/arm/src/gd32w51x/Kconfig +++ b/arch/arm/src/gd32w51x/Kconfig @@ -373,3 +373,25 @@ config GD32W51X_PROGMEM_SIZE endmenu # Program memory Configuration +menu "Power Management Configuration" + depends on PM + +config GD32W51X_PM_WAKEUP_MS + int "Wake-up period in standby (ms)" + default 1000 + range 0 32000 + depends on RTC + ---help--- + In the PM_STANDBY state the chip is in deep-sleep, where the + system timer stops. Besides any EXTI interrupt, the RTC wake-up + timer ends the deep-sleep after this period, so that the timers + still run, late by at most this period. 0 disables it. + +config GD32W51X_PM_SERIAL_ACTIVITY + int "PM serial activity" + default 10 + ---help--- + PM activity reported to the power management logic on every + received character. + +endmenu # Power Management Configuration diff --git a/arch/arm/src/gd32w51x/Make.defs b/arch/arm/src/gd32w51x/Make.defs index 10e97866801..d3b1f208eaa 100644 --- a/arch/arm/src/gd32w51x/Make.defs +++ b/arch/arm/src/gd32w51x/Make.defs @@ -24,7 +24,11 @@ include armv8-m/Make.defs CHIP_CSRCS = gd32w51x_start.c gd32w51x_clockconfig.c gd32w51x_irq.c CHIP_CSRCS += gd32w51x_timerisr.c gd32w51x_lowputc.c gd32w51x_serial.c -CHIP_CSRCS += gd32w51x_tzinit.c +CHIP_CSRCS += gd32w51x_tzinit.c gd32w51x_idle.c + +ifeq ($(CONFIG_PM),y) +CHIP_CSRCS += gd32w51x_pm.c +endif ifeq ($(CONFIG_RTC),y) CHIP_CSRCS += gd32w51x_rtc.c diff --git a/arch/arm/src/gd32w51x/gd32w51x_idle.c b/arch/arm/src/gd32w51x/gd32w51x_idle.c new file mode 100644 index 00000000000..83ef924267c --- /dev/null +++ b/arch/arm/src/gd32w51x/gd32w51x_idle.c @@ -0,0 +1,131 @@ +/**************************************************************************** + * arch/arm/src/gd32w51x/gd32w51x_idle.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 + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <debug.h> + +#include <nuttx/arch.h> +#include <nuttx/irq.h> +#include <nuttx/power/pm.h> + +#include "arm_internal.h" +#include "gd32w51x_pm.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_idlepm + * + * Description: + * Perform the IDLE state power management: the PM_STANDBY state is the + * deep-sleep mode of the GD32W51x. + * + ****************************************************************************/ + +#ifdef CONFIG_PM +static void up_idlepm(void) +{ + static enum pm_state_e oldstate = PM_NORMAL; + enum pm_state_e newstate; + irqstate_t flags; + int ret; + + /* Decide which power saving level can be obtained */ + + newstate = pm_checkstate(PM_IDLE_DOMAIN); + + if (newstate == oldstate) + { + return; + } + + flags = enter_critical_section(); + + /* Force the global state change */ + + ret = pm_changestate(PM_IDLE_DOMAIN, newstate); + if (ret < 0) + { + /* The new state change failed, revert to the preceding state */ + + pm_changestate(PM_IDLE_DOMAIN, oldstate); + } + else + { + oldstate = newstate; + + if (newstate == PM_STANDBY) + { + /* Sleep until an interrupt, then tell the drivers that the + * system is running again. + */ + + gd32w51x_pmstop(); + + if (pm_changestate(PM_IDLE_DOMAIN, PM_NORMAL) >= 0) + { + oldstate = PM_NORMAL; + } + } + } + + leave_critical_section(flags); +} +#else +# define up_idlepm() +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_idle + * + * Description: + * up_idle() is the logic that will be executed when there is no other + * ready-to-run task. This is processor idle time and will continue until + * some interrupt occurs to cause a context switch from the idle task. + * + ****************************************************************************/ + +void up_idle(void) +{ +#if defined(CONFIG_SUPPRESS_INTERRUPTS) || defined(CONFIG_SUPPRESS_TIMER_INTS) + /* If the system is idle and there are no timer interrupts, then process + * "fake" timer interrupts. Hopefully, something will wake up. + */ + + nxsched_process_timer(); +#else + /* Perform IDLE mode power management */ + + up_idlepm(); +#endif +} diff --git a/arch/arm/src/gd32w51x/gd32w51x_pm.c b/arch/arm/src/gd32w51x/gd32w51x_pm.c new file mode 100644 index 00000000000..71067913a3d --- /dev/null +++ b/arch/arm/src/gd32w51x/gd32w51x_pm.c @@ -0,0 +1,173 @@ +/**************************************************************************** + * arch/arm/src/gd32w51x/gd32w51x_pm.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 + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> + +#include <nuttx/arch.h> +#include <nuttx/clock.h> +#include <nuttx/power/pm.h> + +#include "arm_internal.h" +#include "nvic.h" +#include "chip.h" +#include "gd32w51x_clockconfig.h" +#include "gd32w51x_pm.h" +#include "hardware/gd32w51x_uart.h" + +#ifdef CONFIG_PM + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* PMU_CTL0: LDO and low-driver modes during deep-sleep */ + +#define GD32W51X_PMU_CTL0 (GD32W51X_PMU_BASE + 0x0000) +#define PMU_CTL0_LDOLP (1 << 0) /* LDO low power mode */ +#define PMU_CTL0_STBMOD (1 << 1) /* Standby mode */ +#define PMU_CTL0_LDLP (1 << 10) /* Low-driver with low power LDO */ +#define PMU_CTL0_LDNP (1 << 11) /* Low-driver with normal LDO */ +#define PMU_CTL0_LDEN (3 << 18) /* Low-driver mode enable */ + +/* DBG_CTL0: keep the debugger connection in deep-sleep, so that a sleeping + * board can still be programmed. + */ + +#define GD32W51X_DBG_CTL0 0xe0044004 +#define DBG_CTL0_DSLP_HOLD (1 << 1) + +/* The console UART, emptied before deep-sleep stops its clock */ + +#if defined(CONFIG_USART0_SERIAL_CONSOLE) +# define CONSOLE_BASE GD32W51X_USART0_BASE +#elif defined(CONFIG_UART1_SERIAL_CONSOLE) +# define CONSOLE_BASE GD32W51X_UART1_BASE +#elif defined(CONFIG_UART2_SERIAL_CONSOLE) +# define CONSOLE_BASE GD32W51X_UART2_BASE +#endif + +#define CONSOLE_FLUSH_TIMEOUT 1000000 + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: arm_pminitialize + * + * Description: + * Initialize the power management, early in up_initialize(). + * + ****************************************************************************/ + +void arm_pminitialize(void) +{ + modifyreg32(GD32W51X_DBG_CTL0, 0, DBG_CTL0_DSLP_HOLD); + pm_initialize(); +} + +/**************************************************************************** + * Name: gd32w51x_pmstop + * + * Description: + * Enter the deep-sleep mode: the core and bus clocks stop, SRAM and + * registers keep their contents. Any EXTI interrupt wakes the chip up, + * and so does the RTC wake-up timer after + * CONFIG_GD32W51X_PM_WAKEUP_MS milliseconds (when it is not zero). On + * return the clock tree is set up again and the system time is + * corrected with the time spent asleep. + * + * Called with the interrupts disabled. + * + ****************************************************************************/ + +void gd32w51x_pmstop(void) +{ +#ifdef CONSOLE_BASE + int timeout; +#endif + uint8_t basepri; + +#ifdef CONSOLE_BASE + /* Let the console finish the character on the wire */ + + for (timeout = CONSOLE_FLUSH_TIMEOUT; timeout > 0; timeout--) + { + if ((getreg32(CONSOLE_BASE + GD32W51X_UART_STAT_OFFSET) & + UART_STAT_TC) != 0) + { + break; + } + } +#endif + +#if defined(CONFIG_RTC) && CONFIG_GD32W51X_PM_WAKEUP_MS > 0 + gd32w51x_rtc_setwakeup(CONFIG_GD32W51X_PM_WAKEUP_MS); +#endif + + /* LDO in low power mode and low-driver mode during deep-sleep */ + + modifyreg32(GD32W51X_PMU_CTL0, + PMU_CTL0_STBMOD | PMU_CTL0_LDOLP | PMU_CTL0_LDEN | + PMU_CTL0_LDNP | PMU_CTL0_LDLP, + PMU_CTL0_LDOLP | PMU_CTL0_LDEN | PMU_CTL0_LDLP); + + /* WFI only wakes up on an interrupt that could be taken: the critical + * section raises BASEPRI, which would mask the wake-up interrupts. Mask + * them with PRIMASK instead while waiting, so that they wake the core up + * and are serviced once the critical section is left. + */ + + basepri = getbasepri(); + cpsid(); + setbasepri(0); + + modifyreg32(NVIC_SYSCON, 0, NVIC_SYSCON_SLEEPDEEP); + UP_DSB(); + asm volatile ("wfi"); + UP_ISB(); + modifyreg32(NVIC_SYSCON, NVIC_SYSCON_SLEEPDEEP, 0); + + setbasepri(basepri); + cpsie(); + + /* The chip wakes up on the IRC16M: bring the PLL back */ + + gd32w51x_clockconfig(); + +#ifdef CONFIG_RTC + gd32w51x_rtc_setwakeup(0); + gd32w51x_rtc_resync(); + + /* The system timer was stopped: add the time spent asleep */ + + clock_resynchronize(NULL); +#endif +} + +#endif /* CONFIG_PM */ diff --git a/arch/arm/src/gd32w51x/gd32w51x_pm.h b/arch/arm/src/gd32w51x/gd32w51x_pm.h new file mode 100644 index 00000000000..e966c29eb86 --- /dev/null +++ b/arch/arm/src/gd32w51x/gd32w51x_pm.h @@ -0,0 +1,85 @@ +/**************************************************************************** + * arch/arm/src/gd32w51x/gd32w51x_pm.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_GD32W51X_GD32W51X_PM_H +#define __ARCH_ARM_SRC_GD32W51X_GD32W51X_PM_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef CONFIG_PM + +/**************************************************************************** + * Name: gd32w51x_pmstop + * + * Description: + * Enter the deep-sleep mode: the core and bus clocks stop, SRAM and + * registers keep their contents. Any EXTI interrupt wakes the chip up, + * and so does the RTC wake-up timer after + * CONFIG_GD32W51X_PM_WAKEUP_MS milliseconds (when it is not zero). On + * return the clock tree is set up again and the system time is + * corrected with the time spent asleep. + * + * Called with the interrupts disabled. + * + ****************************************************************************/ + +void gd32w51x_pmstop(void); + +#endif /* CONFIG_PM */ + +#ifdef CONFIG_RTC + +/**************************************************************************** + * Name: gd32w51x_rtc_setwakeup + * + * Description: + * Start the RTC wake-up timer to raise its interrupt (EXTI line 24) in + * 'msec' milliseconds, at most about 32 s, or stop it if 'msec' is 0. + * + ****************************************************************************/ + +int gd32w51x_rtc_setwakeup(uint32_t msec); + +/**************************************************************************** + * Name: gd32w51x_rtc_resync + * + * Description: + * Wait for the calendar shadow registers to be updated, which they are + * not in deep-sleep. + * + ****************************************************************************/ + +int gd32w51x_rtc_resync(void); + +#endif /* CONFIG_RTC */ + +#endif /* __ARCH_ARM_SRC_GD32W51X_GD32W51X_PM_H */ diff --git a/arch/arm/src/gd32w51x/gd32w51x_rtc.c b/arch/arm/src/gd32w51x/gd32w51x_rtc.c index 4298eeaff8b..89d80307f51 100644 --- a/arch/arm/src/gd32w51x/gd32w51x_rtc.c +++ b/arch/arm/src/gd32w51x/gd32w51x_rtc.c @@ -34,13 +34,17 @@ #include <debug.h> #include <nuttx/arch.h> +#include <nuttx/clock.h> +#include <nuttx/irq.h> #include <arch/board/board.h> #include "arm_internal.h" #include "nvic.h" #include "dwt.h" +#include "hardware/gd32w51x_exti.h" #include "hardware/gd32w51x_rcu.h" #include "chip.h" +#include "gd32w51x_pm.h" #ifdef CONFIG_RTC @@ -55,15 +59,36 @@ #define GD32W51X_RTC_CTL (GD32W51X_RTC_BASE + 0x0008) #define GD32W51X_RTC_ICS (GD32W51X_RTC_BASE + 0x000c) #define GD32W51X_RTC_PSC (GD32W51X_RTC_BASE + 0x0010) +#define GD32W51X_RTC_WUT (GD32W51X_RTC_BASE + 0x0014) #define GD32W51X_RTC_WPK (GD32W51X_RTC_BASE + 0x0024) #define GD32W51X_RTC_SS (GD32W51X_RTC_BASE + 0x0028) +#define GD32W51X_RTC_STATC (GD32W51X_RTC_BASE + 0x0064) + +/* CTL bits */ + +#define RTC_CTL_WTCS_MASK (7 << 0) /* Wake-up timer clock */ +#define RTC_CTL_WTCS_DIV16 (0 << 0) /* RTC clock / 16 */ +#define RTC_CTL_WTEN (1 << 10) /* Wake-up timer enable */ +#define RTC_CTL_WTIE (1 << 14) /* Wake-up timer interrupt */ /* STAT bits */ +#define RTC_ICS_WTWF (1 << 2) /* Wake-up timer writable */ #define RTC_ICS_RSYNF (1 << 5) /* Registers synchronized */ #define RTC_ICS_INITF (1 << 6) /* In initialization state */ #define RTC_ICS_INITM (1 << 7) /* Enter initialization mode */ +#define RTC_STATC_WTFC (1 << 2) /* Clear the wake-up timer flag */ + +/* The wake-up timer counts the RTC clock divided by 16, on 16 bits; its + * interrupt goes through EXTI line 24 (the non-secure line, line 25 is the + * secure one). + */ + +#define RTC_WUT_DIV 16 +#define RTC_WUT_MAX 0xffff +#define RTC_WKUP_EXTI_LINE (1 << 24) + /* Write-protection key sequence */ #define RTC_UNLOCK_KEY1 0xca @@ -125,6 +150,14 @@ volatile bool g_rtc_enabled = false; +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* RTC clock frequency, measured at boot */ + +static uint32_t g_rtc_frequency = RTC_IRC32K_FREQUENCY; + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -263,10 +296,28 @@ static uint32_t rtc_sync_prescaler(void) } rtcinfo("IRC32K: %" PRIu32 " Hz\n", freq); + g_rtc_frequency = freq; return (freq + (RTC_PSC_FACTOR_A + 1) / 2) / (RTC_PSC_FACTOR_A + 1) - 1; } +/**************************************************************************** + * Name: rtc_wakeup_interrupt + * + * Description: + * The RTC wake-up timer interrupt: its only job is to end deep-sleep. + * + ****************************************************************************/ + +#ifdef CONFIG_PM +static int rtc_wakeup_interrupt(int irq, void *context, void *arg) +{ + putreg32(RTC_STATC_WTFC, GD32W51X_RTC_STATC); + putreg32(RTC_WKUP_EXTI_LINE, GD32W51X_EXTI_PD); + return OK; +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -367,14 +418,39 @@ int up_rtc_initialize(void) int up_rtc_getdatetime(struct tm *tp) { + long nsec; + + return up_rtc_getdatetime_with_subseconds(tp, &nsec); +} + +/**************************************************************************** + * Name: up_rtc_getdatetime_with_subseconds + * + * Description: + * Get the current date and time from the hardware RTC, with the part of + * the current second in nanoseconds. + * + ****************************************************************************/ + +int up_rtc_getdatetime_with_subseconds(struct tm *tp, long *nsec) +{ + uint32_t sync; + uint32_t ss; uint32_t time; uint32_t date; - /* Reading TIME locks DATE until DATE is read, so this pair is atomic */ + /* Reading SS locks TIME and DATE until DATE is read, so the three reads + * are a single sample. SS counts down from the synchronous prescaler. + */ + sync = getreg32(GD32W51X_RTC_PSC) & 0x7fff; + ss = getreg32(GD32W51X_RTC_SS) & RTC_SS_MASK; time = getreg32(GD32W51X_RTC_TIME); date = getreg32(GD32W51X_RTC_DATE); + ss = MIN(ss, sync); + *nsec = (long)(((uint64_t)(sync - ss) * NSEC_PER_SEC) / (sync + 1)); + tp->tm_sec = BCD2DEC(time & 0x7f); tp->tm_min = BCD2DEC((time >> 8) & 0x7f); tp->tm_hour = BCD2DEC((time >> 16) & 0x3f); @@ -451,4 +527,89 @@ int up_rtc_settime(const struct timespec *tp) return rtc_synchwait(); } +/**************************************************************************** + * Name: gd32w51x_rtc_setwakeup + * + * Description: + * Start the RTC wake-up timer to raise its interrupt (EXTI line 24) in + * 'msec' milliseconds, at most about 32 s, or stop it if 'msec' is 0. + * + ****************************************************************************/ + +int gd32w51x_rtc_setwakeup(uint32_t msec) +{ + uint64_t ticks; + int timeout; + int ret = OK; + +#ifdef CONFIG_PM + static bool attached; + + if (!attached) + { + irq_attach(GD32W51X_IRQ_RTC_WKUP, rtc_wakeup_interrupt, NULL); + up_enable_irq(GD32W51X_IRQ_RTC_WKUP); + attached = true; + } +#endif + + rtc_unlock(); + + /* Stop the timer, then wait until its reload value can be written */ + + modifyreg32(GD32W51X_RTC_CTL, RTC_CTL_WTEN | RTC_CTL_WTIE, 0); + putreg32(RTC_STATC_WTFC, GD32W51X_RTC_STATC); + modifyreg32(GD32W51X_EXTI_INTEN, RTC_WKUP_EXTI_LINE, 0); + putreg32(RTC_WKUP_EXTI_LINE, GD32W51X_EXTI_PD); + + if (msec > 0) + { + for (timeout = 1000000; timeout > 0; timeout--) + { + if ((getreg32(GD32W51X_RTC_ICS) & RTC_ICS_WTWF) != 0) + { + break; + } + } + + if (timeout <= 0) + { + ret = -ETIMEDOUT; + } + else + { + /* The timer counts the reload value plus one clock down to 0 */ + + ticks = ((uint64_t)msec * (g_rtc_frequency / RTC_WUT_DIV)) / + MSEC_PER_SEC; + ticks = MAX(MIN(ticks, RTC_WUT_MAX), 1); + + putreg32((uint32_t)ticks - 1, GD32W51X_RTC_WUT); + modifyreg32(GD32W51X_RTC_CTL, RTC_CTL_WTCS_MASK, + RTC_CTL_WTCS_DIV16); + + modifyreg32(GD32W51X_EXTI_RTEN, 0, RTC_WKUP_EXTI_LINE); + modifyreg32(GD32W51X_EXTI_INTEN, 0, RTC_WKUP_EXTI_LINE); + modifyreg32(GD32W51X_RTC_CTL, 0, RTC_CTL_WTIE | RTC_CTL_WTEN); + } + } + + rtc_lock(); + return ret; +} + +/**************************************************************************** + * Name: gd32w51x_rtc_resync + * + * Description: + * Wait for the calendar shadow registers to be updated, which they are + * not in deep-sleep. + * + ****************************************************************************/ + +int gd32w51x_rtc_resync(void) +{ + return rtc_synchwait(); +} + #endif /* CONFIG_RTC */ diff --git a/arch/arm/src/gd32w51x/gd32w51x_serial.c b/arch/arm/src/gd32w51x/gd32w51x_serial.c index 253d7169125..6ebdb834586 100644 --- a/arch/arm/src/gd32w51x/gd32w51x_serial.c +++ b/arch/arm/src/gd32w51x/gd32w51x_serial.c @@ -37,6 +37,7 @@ #include <nuttx/irq.h> #include <nuttx/arch.h> #include <nuttx/serial/serial.h> +#include <nuttx/power/pm.h> #include <arch/board/board.h> @@ -355,6 +356,12 @@ static int gd32w51x_interrupt(int irq, void *context, void *arg) if ((status & UART_STAT_RBNE) != 0 && (priv->im & UART_CTL0_RBNEIE) != 0) { +#ifdef CONFIG_PM + /* Keep the system awake while characters are received */ + + pm_activity(PM_IDLE_DOMAIN, CONFIG_GD32W51X_PM_SERIAL_ACTIVITY); +#endif + uart_recvchars(dev); } diff --git a/boards/arm/gd32w51x/gd-xd-w515-eval/configs/pm/defconfig b/boards/arm/gd32w51x/gd-xd-w515-eval/configs/pm/defconfig new file mode 100644 index 00000000000..efb76f82929 --- /dev/null +++ b/boards/arm/gd32w51x/gd-xd-w515-eval/configs/pm/defconfig @@ -0,0 +1,54 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_ARCH_LEDS is not set +CONFIG_ARCH="arm" +CONFIG_ARCH_BOARD="gd-xd-w515-eval" +CONFIG_ARCH_BOARD_GD_XD_W515_EVAL=y +CONFIG_ARCH_CHIP="gd32w51x" +CONFIG_ARCH_CHIP_GD32W515PI=y +CONFIG_ARCH_CHIP_GD32W51X=y +CONFIG_ARCH_INTERRUPTSTACK=2048 +CONFIG_ARCH_STACKDUMP=y +CONFIG_BOARDCTL_RESET=y +CONFIG_BOARD_LOOPSPERMSEC=30000 +CONFIG_BUILTIN=y +CONFIG_DEBUG_FULLOPT=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_EXAMPLES_HELLO=y +CONFIG_FS_PROCFS=y +CONFIG_FS_PROCFS_REGISTER=y +CONFIG_GD32W51X_UART2=y +CONFIG_IDLETHREAD_STACKSIZE=2048 +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_INIT_STACKSIZE=3072 +CONFIG_INTELHEX_BINARY=y +CONFIG_LIBC_PERROR_STDOUT=y +CONFIG_LIBC_STRERROR=y +CONFIG_NFILE_DESCRIPTORS_PER_BLOCK=6 +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_MAXARGUMENTS=12 +CONFIG_NSH_READLINE=y +CONFIG_NSH_STRERROR=y +CONFIG_PM=y +CONFIG_PM_GOVERNOR_ACTIVITY=y +CONFIG_PM_PROCFS=y +CONFIG_RAM_SIZE=458752 +CONFIG_RAM_START=0x20000000 +CONFIG_RAW_BINARY=y +CONFIG_RR_INTERVAL=200 +CONFIG_RTC=y +CONFIG_RTC_DATETIME=y +CONFIG_SCHED_WAITPID=y +CONFIG_STACK_COLORATION=y +CONFIG_START_DAY=18 +CONFIG_START_MONTH=10 +CONFIG_START_YEAR=2026 +CONFIG_SYSTEM_NSH=y +CONFIG_TASK_NAME_SIZE=12 +CONFIG_UART2_SERIAL_CONSOLE=y
