diff --git a/cpu/gd32v/Kconfig b/cpu/gd32v/Kconfig index e1ac4ac3d6..afbab5b973 100644 --- a/cpu/gd32v/Kconfig +++ b/cpu/gd32v/Kconfig @@ -22,7 +22,9 @@ config CPU_FAM_GD32V select MODULE_PERIPH_CLIC if TEST_KCONFIG select MODULE_PERIPH_WDT if MODULE_PERIPH_PM && HAS_PERIPH_WDT - select PACKAGE_NUCLEI_SDK + select PACKAGE_NMSIS_SDK + +menu "GD32V configuration" config CPU_MODEL_GD32VF103VBT6 bool @@ -48,4 +50,6 @@ config CPU_CORE rsource "periph/Kconfig" +endmenu + source "$(RIOTCPU)/riscv_common/Kconfig" diff --git a/cpu/gd32v/Makefile.default b/cpu/gd32v/Makefile.default new file mode 100644 index 0000000000..034f8c7848 --- /dev/null +++ b/cpu/gd32v/Makefile.default @@ -0,0 +1 @@ +DEFAULT_MODULE += pm_layered diff --git a/cpu/gd32v/Makefile.features b/cpu/gd32v/Makefile.features index 0e1cc22da5..b1d59add66 100644 --- a/cpu/gd32v/Makefile.features +++ b/cpu/gd32v/Makefile.features @@ -12,3 +12,9 @@ FEATURES_PROVIDED += periph_flashpage_in_address_space FEATURES_PROVIDED += periph_flashpage_pagewise include $(RIOTCPU)/riscv_common/Makefile.features + +# This configuration enables modules that are only available when using Kconfig +# module modelling +ifeq (1, $(TEST_KCONFIG)) + KCONFIG_ADD_CONFIG += $(RIOTCPU)/gd32v/gd32v.config +endif diff --git a/cpu/gd32v/cpu.c b/cpu/gd32v/cpu.c index fd34780abd..81ce057c8c 100644 --- a/cpu/gd32v/cpu.c +++ b/cpu/gd32v/cpu.c @@ -29,6 +29,8 @@ extern void __libc_init_array(void); void cpu_init(void) { gd32vf103_clock_init(); + /* enable PMU required for pm_layered */ + periph_clk_en(APB1, RCU_APB1EN_PMUEN_Msk); /* Common RISC-V initialization */ riscv_init(); early_init(); diff --git a/cpu/gd32v/gd32v.config b/cpu/gd32v/gd32v.config new file mode 100644 index 0000000000..811b1f3712 --- /dev/null +++ b/cpu/gd32v/gd32v.config @@ -0,0 +1 @@ +CONFIG_MODULE_PM_LAYERED=y diff --git a/cpu/gd32v/include/periph_cpu.h b/cpu/gd32v/include/periph_cpu.h index df4bae1e71..b6e85c13e4 100644 --- a/cpu/gd32v/include/periph_cpu.h +++ b/cpu/gd32v/include/periph_cpu.h @@ -33,7 +33,39 @@ extern "C" { * @name Power management configuration * @{ */ -#define PROVIDES_PM_SET_LOWEST +/** + * @brief Number of usable low power modes + */ +#define PM_NUM_MODES (3U) /**< Number of usable low power modes */ + +/** + * @brief Power modes + * + * The GD32V has three power modes (terminology as defined by GigaDevice). + * - Sleep: Only the clock of the RISC-V core is switched off. + * - Deep sleep: The RISC-V core including all AHB and APB peripheralsa and all + * high speed clocks are off. The LDO is in operation and the + * SRAM is retained. + * The MCU can be woken up by external interrupts or events + * without restart. + * - Standby: The RISC-V core including all AHB and APB peripherals, all + * high-speed clocks, and the LDO are off. The SRAM is not + * retained. + * The MCU can be woken up by WKUP or the NRST pin, watchdog + * reset and RTC alarms with restart. + */ +enum { + GD32V_PM_STANDBY = 0, /**< STANDBY mode, */ + GD32V_PM_DEEPSLEEP = 1, /**< DEEPSLEEP mode, corresponds to STOP mode of STM32 */ + GD32V_PM_IDLE = 2 /**< IDLE mode */ +}; + +/** + * @brief Wake-up pin used + */ +#ifndef CONFIG_PM_EWUP_USED +#define CONFIG_PM_EWUP_USED (0U) +#endif /** @} */ /** diff --git a/cpu/gd32v/periph/Kconfig b/cpu/gd32v/periph/Kconfig index afb807b287..f092af4a4e 100644 --- a/cpu/gd32v/periph/Kconfig +++ b/cpu/gd32v/periph/Kconfig @@ -8,3 +8,9 @@ config MODULE_PERIPH bool default y + +config PM_EWUP_USED + bool "Use PA0/WKUP pin" + depends on MODULE_PM_LAYERED + help + If enabled, the PA0/WKUP pin can be used to wake up the MCU from standby mode. diff --git a/cpu/gd32v/periph/pm.c b/cpu/gd32v/periph/pm.c index f7c0908bdb..356fb8e42c 100644 --- a/cpu/gd32v/periph/pm.c +++ b/cpu/gd32v/periph/pm.c @@ -1,5 +1,6 @@ /* * Copyright 2020 Koen Zandberg + * 2023 Gunar Schorcht * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level @@ -13,16 +14,67 @@ * @brief Implementation of the CPU power management for Gigadevice GD32V * * @author Koen Zandberg + * @author Gunar Schorcht * @} */ #include +#include #include "periph/pm.h" #include "periph/wdt.h" -void pm_set_lowest(void) +#undef MCAUSE_CAUSE /* redefined in NMSIS header */ + +#include "core_feature_base.h" + +void pm_set(unsigned mode) { - __asm__ volatile ("wfi"); + bool csr_deepsleep = false; /* RISC-V sleep mode */ + + switch (mode) { + case GD32V_PM_STANDBY: + csr_deepsleep = true; + /* set the STANDBY mode flag */ + PMU->CTL |= PMU_CTL_WURST_Msk; + /* reset the wake up flag */ + PMU->CTL |= PMU_CTL_STBMOD_Msk; + break; + case GD32V_PM_DEEPSLEEP: + csr_deepsleep = true; + /* reset the STANDBY mode flag */ + PMU->CTL &= ~PMU_CTL_STBMOD_Msk; + /* use LDO low powered in deep sleep mode */ + PMU->CTL |= PMU_CTL_LDOLP_Msk; + break; + case GD32V_PM_IDLE: + csr_deepsleep = false; + break; + default: + break; + } + + if (csr_deepsleep) { + /* Enable WKUP pin if used */ + PMU->CS &= ~PMU_CS_WUPEN_Msk; + PMU->CS |= (CONFIG_PM_EWUP_USED) ? PMU_CS_WUPEN_Msk : 0; + /* set CSR_SLEEPVALUE bit in RISC-V system control register */ + __set_wfi_sleepmode(WFI_DEEP_SLEEP); + } + else { + /* clear CSR_SLEEPVALUE bit in RISC-V system control register */ + __set_wfi_sleepmode(WFI_SHALLOW_SLEEP); + } + + /* trigger sleeping, TODO wait for wake up event (__WFE) implementation */ + __WFI(); + + if (csr_deepsleep) { + /* clear CSR_SLEEPVALUE bit in RISC-V system control register */ + __set_wfi_sleepmode(WFI_SHALLOW_SLEEP); + /* after deep sleep, the IRC8M is used as clock so that a clock + * reinitialization is required */ + gd32vf103_clock_init(); + } } void pm_reboot(void) diff --git a/cpu/gd32v/periph/uart.c b/cpu/gd32v/periph/uart.c index b54880db29..b8ac773abd 100644 --- a/cpu/gd32v/periph/uart.c +++ b/cpu/gd32v/periph/uart.c @@ -22,6 +22,7 @@ #include "periph_cpu.h" #include "cpu.h" #include "clic.h" +#include "pm_layered.h" #define RXENABLE (USART_CTL0_REN_Msk | USART_CTL0_RBNEIE_Msk) @@ -56,14 +57,18 @@ static inline void uart_init_pins(uart_t uart, uart_rx_cb_t rx_cb) static inline void uart_enable_clock(uart_t uart) { - /* TODO: add pm blocker */ + if (isr_ctx[uart].rx_cb) { + pm_block(GD32V_PM_DEEPSLEEP); + } periph_clk_en(uart_config[uart].bus, uart_config[uart].rcu_mask); } static inline void uart_disable_clock(uart_t uart) { periph_clk_dis(uart_config[uart].bus, uart_config[uart].rcu_mask); - /* TODO remove pm blocker */ + if (isr_ctx[uart].rx_cb) { + pm_unblock(GD32V_PM_DEEPSLEEP); + } } static inline void uart_init_usart(uart_t uart, uint32_t baudrate) diff --git a/pkg/Kconfig b/pkg/Kconfig index 4269e9f8f6..9f2a2ebe8b 100644 --- a/pkg/Kconfig +++ b/pkg/Kconfig @@ -55,6 +55,7 @@ rsource "mynewt-core/Kconfig" rsource "nanocbor/Kconfig" rsource "nanopb/Kconfig" rsource "nanors/Kconfig" +rsource "nmsis_sdk/Kconfig" rsource "nrfx/Kconfig" rsource "qcbor/Kconfig" rsource "qdsa/Kconfig"