Merge branch 'feat/esp32h21_system_sleep_cpu' into 'master'

Stage 4: Support esp32h21 CPU domain power down and retention

Closes PM-351

See merge request espressif/esp-idf!37510
This commit is contained in:
Jiang Jiang Jian
2025-04-08 13:55:29 +08:00
5 changed files with 1028 additions and 0 deletions

View File

@@ -0,0 +1,194 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __RVSLEEP_FRAMES_H__
#define __RVSLEEP_FRAMES_H__
#include "sdkconfig.h"
/* Align a value up to nearest n-byte boundary, where n is a power of 2. */
#define ALIGNUP(n, val) (((val) + (n) - 1) & -(n))
#ifdef STRUCT_BEGIN
#undef STRUCT_BEGIN
#undef STRUCT_FIELD
#undef STRUCT_AFIELD
#undef STRUCT_END
#endif
#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__)
#ifdef __clang__
#define STRUCT_BEGIN .set RV_STRUCT_OFFSET, 0
#define STRUCT_FIELD(ctype,size,asname,name) .set asname, RV_STRUCT_OFFSET; .set RV_STRUCT_OFFSET, asname + size
#define STRUCT_AFIELD(ctype,size,asname,name,n) .set asname, RV_STRUCT_OFFSET;\
.set RV_STRUCT_OFFSET, asname + (size)*(n);
#define STRUCT_END(sname) .set sname##Size, RV_STRUCT_OFFSET;
#else // __clang__
#define STRUCT_BEGIN .pushsection .text; .struct 0
#define STRUCT_FIELD(ctype,size,asname,name) asname: .space size
#define STRUCT_AFIELD(ctype,size,asname,name,n) asname: .space (size)*(n)
#define STRUCT_END(sname) sname##Size:; .popsection
#endif // __clang__
#else
#define STRUCT_BEGIN typedef struct {
#define STRUCT_FIELD(ctype,size,asname,name) ctype name;
#define STRUCT_AFIELD(ctype,size,asname,name,n) ctype name[n];
#define STRUCT_END(sname) } sname;
#endif
/*
* -------------------------------------------------------------------------------
* RISC-V CORE CRITICAL REGISTER CONTEXT LAYOUT FOR SLEEP
* -------------------------------------------------------------------------------
*/
STRUCT_BEGIN
STRUCT_FIELD (long, 4, RV_SLP_CTX_MEPC, mepc) /* Machine Exception Program Counter */
STRUCT_FIELD (long, 4, RV_SLP_CTX_RA, ra) /* Return address */
STRUCT_FIELD (long, 4, RV_SLP_CTX_SP, sp) /* Stack pointer */
STRUCT_FIELD (long, 4, RV_SLP_CTX_GP, gp) /* Global pointer */
STRUCT_FIELD (long, 4, RV_SLP_CTX_TP, tp) /* Thread pointer */
STRUCT_FIELD (long, 4, RV_SLP_CTX_T0, t0) /* Temporary/alternate link register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_T1, t1) /* t1-2: Temporaries */
STRUCT_FIELD (long, 4, RV_SLP_CTX_T2, t2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S0, s0) /* Saved register/frame pointer */
STRUCT_FIELD (long, 4, RV_SLP_CTX_S1, s1) /* Saved register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_A0, a0) /* a0-1: Function arguments/return address */
STRUCT_FIELD (long, 4, RV_SLP_CTX_A1, a1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_A2, a2) /* a2-7: Function arguments */
STRUCT_FIELD (long, 4, RV_SLP_CTX_A3, a3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_A4, a4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_A5, a5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_A6, a6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_A7, a7)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S2, s2) /* s2-11: Saved registers */
STRUCT_FIELD (long, 4, RV_SLP_CTX_S3, s3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S4, s4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S5, s5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S6, s6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S7, s7)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S8, s8)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S9, s9)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S10, s10)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S11, s11)
STRUCT_FIELD (long, 4, RV_SLP_CTX_T3, t3) /* t3-6: Temporaries */
STRUCT_FIELD (long, 4, RV_SLP_CTX_T4, t4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_T5, t5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_T6, t6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_MSTATUS, mstatus) /* Machine Status */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MTVEC, mtvec) /* Machine Trap-Vector Base Address */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MCAUSE, mcause) /* Machine Trap Cause */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MTVAL, mtval) /* Machine Trap Value */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MIE, mie) /* Machine intr enable */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MIP, mip) /* Machine intr pending */
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMUFUNC, pmufunc) /* A field is used to identify whether it is going
* to sleep or has just been awakened. We use the
* lowest 2 bits as indication information, 3 means
* being awakened, 1 means going to sleep */
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
STRUCT_FIELD (long, 4, RV_SLP_CSF_CTX_CRC, frame_crc) /* Used to check RvCoreCriticalSleepFrame integrity */
#endif
STRUCT_END(RvCoreCriticalSleepFrame)
#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__)
#define RV_SLEEP_CTX_SZ1 RvCoreCriticalSleepFrameSize
#else
#define RV_SLEEP_CTX_SZ1 sizeof(RvCoreCriticalSleepFrame)
#endif
/*
* Sleep stack frame size, after align up to 16 bytes boundary
*/
#define RV_SLEEP_CTX_FRMSZ (ALIGNUP(0x10, RV_SLEEP_CTX_SZ1))
/*
* -------------------------------------------------------------------------------
* RISC-V CORE NON-CRITICAL REGISTER CONTEXT LAYOUT FOR SLEEP
* -------------------------------------------------------------------------------
*/
STRUCT_BEGIN
STRUCT_FIELD (long, 4, RV_SLP_CTX_MSCRATCH, mscratch)
STRUCT_FIELD (long, 4, RV_SLP_CTX_MIDELEG, mideleg)
STRUCT_FIELD (long, 4, RV_SLP_CTX_MISA, misa)
STRUCT_FIELD (long, 4, RV_SLP_CTX_TSELECT, tselect)
STRUCT_FIELD (long, 4, RV_SLP_CTX_TDATA1, tdata1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_TDATA2, tdata2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_TCONTROL, tcontrol)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR0, pmpaddr0)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR1, pmpaddr1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR2, pmpaddr2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR3, pmpaddr3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR4, pmpaddr4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR5, pmpaddr5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR6, pmpaddr6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR7, pmpaddr7)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR8, pmpaddr8)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR9, pmpaddr9)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR10, pmpaddr10)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR11, pmpaddr11)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR12, pmpaddr12)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR13, pmpaddr13)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR14, pmpaddr14)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR15, pmpaddr15)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPCFG0, pmpcfg0)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPCFG1, pmpcfg1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPCFG2, pmpcfg2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPCFG3, pmpcfg3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR0, pmaaddr0)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR1, pmaaddr1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR2, pmaaddr2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR3, pmaaddr3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR4, pmaaddr4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR5, pmaaddr5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR6, pmaaddr6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR7, pmaaddr7)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR8, pmaaddr8)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR9, pmaaddr9)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR10, pmaaddr10)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR11, pmaaddr11)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR12, pmaaddr12)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR13, pmaaddr13)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR14, pmaaddr14)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR15, pmaaddr15)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG0, pmacfg0)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG1, pmacfg1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG2, pmacfg2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG3, pmacfg3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG4, pmacfg4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG5, pmacfg5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG6, pmacfg6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG7, pmacfg7)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG8, pmacfg8)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG9, pmacfg9)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG10, pmacfg10)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG11, pmacfg11)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG12, pmacfg12)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG13, pmacfg13)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG14, pmacfg14)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG15, pmacfg15)
STRUCT_FIELD (long, 4, RV_SLP_CTX_UTVEC, utvec)
STRUCT_FIELD (long, 4, RV_SLP_CTX_USTATUS, ustatus)
STRUCT_FIELD (long, 4, RV_SLP_CTX_UEPC, uepc)
STRUCT_FIELD (long, 4, RV_SLP_CTX_UCAUSE, ucause)
STRUCT_FIELD (long, 4, RV_SLP_CTX_MPCER, mpcer)
STRUCT_FIELD (long, 4, RV_SLP_CTX_MPCMR, mpcmr)
STRUCT_FIELD (long, 4, RV_SLP_CTX_MPCCR, mpccr)
STRUCT_FIELD (long, 4, RV_SLP_CTX_CPU_TESTBUS_CTRL, cpu_testbus_ctrl)
STRUCT_FIELD (long, 4, RV_SLP_CTX_UPCER, upcer)
STRUCT_FIELD (long, 4, RV_SLP_CTX_UPCMR, upcmr)
STRUCT_FIELD (long, 4, RV_SLP_CTX_UPCCR, upccr)
STRUCT_FIELD (long, 4, RV_SLP_CTX_UGPIO_OEN, ugpio_oen)
STRUCT_FIELD (long, 4, RV_SLP_CTX_UGPIO_IN, ugpio_in)
STRUCT_FIELD (long, 4, RV_SLP_CTX_UGPIO_OUT, ugpio_out)
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
STRUCT_FIELD (long, 4, RV_SLP_NCSF_CTX_CRC, frame_crc) /* Used to check RvCoreNonCriticalSleepFrame integrity */
#endif
STRUCT_END(RvCoreNonCriticalSleepFrame)
#endif /* #ifndef __RVSLEEP_FRAMES_H__ */

View File

@@ -0,0 +1,127 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_private/sleep_clock.h"
#include "soc/i2c_ana_mst_reg.h"
#include "soc/regi2c_defs.h"
#include "soc/pcr_reg.h"
#include "soc/regi2c_defs.h"
#include "modem/modem_syscon_reg.h"
#include "modem/modem_lpcon_reg.h"
static const char *TAG = "sleep_clock";
esp_err_t sleep_clock_system_retention_init(void *arg)
{
#define N_REGS_PCR() (((PCR_PWDET_SAR_CLK_CONF_REG - DR_REG_PCR_BASE) / 4) + 1)
const static sleep_retention_entries_config_t pcr_regs_retention[] = {
/* Enable i2c master clock */
[0] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_PCR_LINK(0), MODEM_LPCON_CLK_CONF_REG, MODEM_LPCON_CLK_I2C_MST_EN, MODEM_LPCON_CLK_I2C_MST_EN_M, 1, 0), .owner = ENTRY(0) },
/* Start BBPLL self-calibration */
[1] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_PCR_LINK(1), I2C_MST_ANA_CONF0_REG, 0, I2C_MST_BBPLL_STOP_FORCE_HIGH, 1, 0), .owner = ENTRY(0) },
[2] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_PCR_LINK(2), I2C_MST_ANA_CONF0_REG, I2C_MST_BBPLL_STOP_FORCE_LOW, I2C_MST_BBPLL_STOP_FORCE_LOW, 1, 0), .owner = ENTRY(0) },
/* Wait calibration done */
[3] = { .config = REGDMA_LINK_WAIT_INIT (REGDMA_PCR_LINK(3), I2C_MST_ANA_CONF0_REG, I2C_MST_BBPLL_CAL_DONE, I2C_MST_BBPLL_CAL_DONE, 1, 0), .owner = ENTRY(0) },
/* Stop BBPLL self-calibration */
[4] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_PCR_LINK(4), I2C_MST_ANA_CONF0_REG, 0, I2C_MST_BBPLL_STOP_FORCE_LOW, 1, 0), .owner = ENTRY(0) },
[5] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_PCR_LINK(5), I2C_MST_ANA_CONF0_REG, I2C_MST_BBPLL_STOP_FORCE_HIGH, I2C_MST_BBPLL_STOP_FORCE_HIGH, 1, 0), .owner = ENTRY(0) },
/* Clock configuration retention */
[6] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_PCR_LINK(6), DR_REG_PCR_BASE, DR_REG_PCR_BASE, N_REGS_PCR(), 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[7] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_PCR_LINK(7), PCR_RESET_EVENT_BYPASS_REG, PCR_RESET_EVENT_BYPASS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[8] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_PCR_LINK(8), PCR_BUS_CLK_UPDATE_REG, PCR_BUS_CLOCK_UPDATE, PCR_BUS_CLOCK_UPDATE_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) },
[9] = { .config = REGDMA_LINK_WAIT_INIT (REGDMA_PCR_LINK(9), PCR_BUS_CLK_UPDATE_REG, 0x0, PCR_BUS_CLOCK_UPDATE_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) },
};
esp_err_t err = sleep_retention_entries_create(pcr_regs_retention, ARRAY_SIZE(pcr_regs_retention), REGDMA_LINK_PRI_SYS_CLK, SLEEP_RETENTION_MODULE_CLOCK_SYSTEM);
ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for system (PCR) retention");
ESP_LOGI(TAG, "System Power, Clock and Reset sleep retention initialization");
return ESP_OK;
#undef N_REGS_PCR
}
#if CONFIG_MAC_BB_PD || CONFIG_BT_LE_SLEEP_ENABLE || CONFIG_IEEE802154_SLEEP_ENABLE
esp_err_t sleep_clock_modem_retention_init(void *arg)
{
#define N_REGS_SYSCON() (((MODEM_SYSCON_MEM_CONF_REG - MODEM_SYSCON_TEST_CONF_REG) / 4) + 1)
#define N_REGS_LPCON() (((MODEM_LPCON_MEM_CONF_REG - MODEM_LPCON_TEST_CONF_REG) / 4) + 1)
const static sleep_retention_entries_config_t modem_regs_retention[] = {
[0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_MODEMSYSCON_LINK(0), MODEM_SYSCON_TEST_CONF_REG, MODEM_SYSCON_TEST_CONF_REG, N_REGS_SYSCON(), 0, 0), .owner = ENTRY(0) | ENTRY(1) }, /* MODEM SYSCON */
#if SOC_PM_RETENTION_SW_TRIGGER_REGDMA
[1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_MODEMLPCON_LINK(0), MODEM_LPCON_TEST_CONF_REG, MODEM_LPCON_TEST_CONF_REG, N_REGS_LPCON(), 0, 0), .owner = ENTRY(0) | ENTRY(1) } /* MODEM LPCON */
#endif
};
esp_err_t err = sleep_retention_entries_create(modem_regs_retention, ARRAY_SIZE(modem_regs_retention), REGDMA_LINK_PRI_MODEM_CLK, SLEEP_RETENTION_MODULE_CLOCK_MODEM);
ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for modem (SYSCON) retention, 1 level priority");
ESP_LOGI(TAG, "Modem Power, Clock and Reset sleep retention initialization");
return ESP_OK;
#undef N_REGS_LPCON
#undef N_REGS_SYSCON
}
#endif
bool clock_domain_pd_allowed(void)
{
const sleep_retention_module_bitmap_t inited_modules = sleep_retention_get_inited_modules();
const sleep_retention_module_bitmap_t created_modules = sleep_retention_get_created_modules();
const sleep_retention_module_bitmap_t sys_clk_dep_modules = (sleep_retention_module_bitmap_t){ .bitmap[SLEEP_RETENTION_MODULE_SYS_PERIPH >> 5] = BIT(SLEEP_RETENTION_MODULE_SYS_PERIPH % 32) };
/* The clock and reset of MODEM (WiFi, BLE and 15.4) modules are managed
* through MODEM_SYSCON, when one or more MODEMs are initialized, it is
* necessary to check the state of CLOCK_MODEM to determine MODEM domain on
* or off. The clock and reset of digital peripherals are managed through
* PCR, with TOP domain similar to MODEM domain. */
sleep_retention_module_bitmap_t modem_clk_dep_modules = (sleep_retention_module_bitmap_t){ .bitmap = { 0 } };
#if SOC_BT_SUPPORTED
modem_clk_dep_modules.bitmap[SLEEP_RETENTION_MODULE_BLE_MAC >> 5] |= BIT(SLEEP_RETENTION_MODULE_BLE_MAC % 32);
modem_clk_dep_modules.bitmap[SLEEP_RETENTION_MODULE_BT_BB >> 5] |= BIT(SLEEP_RETENTION_MODULE_BT_BB % 32);
#endif
#if SOC_IEEE802154_SUPPORTED
modem_clk_dep_modules.bitmap[SLEEP_RETENTION_MODULE_802154_MAC >> 5] |= BIT(SLEEP_RETENTION_MODULE_802154_MAC % 32);
modem_clk_dep_modules.bitmap[SLEEP_RETENTION_MODULE_BT_BB >> 5] |= BIT(SLEEP_RETENTION_MODULE_BT_BB % 32);
#endif
const sleep_retention_module_bitmap_t null_module = (sleep_retention_module_bitmap_t){ .bitmap = { 0 } };
sleep_retention_module_bitmap_t mask = (sleep_retention_module_bitmap_t){ .bitmap = { 0 } };
const sleep_retention_module_bitmap_t system_modules = sleep_retention_module_bitmap_and(inited_modules, sys_clk_dep_modules);
if (!sleep_retention_module_bitmap_eq(system_modules, null_module)) {
mask.bitmap[SLEEP_RETENTION_MODULE_CLOCK_SYSTEM >> 5] |= BIT(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM % 32);
}
#if SOC_BT_SUPPORTED || SOC_IEEE802154_SUPPORTED
const sleep_retention_module_bitmap_t modem_modules = sleep_retention_module_bitmap_and(inited_modules, modem_clk_dep_modules);
if (!sleep_retention_module_bitmap_eq(modem_modules, null_module)) {
mask.bitmap[SLEEP_RETENTION_MODULE_CLOCK_MODEM >> 5] |= BIT(SLEEP_RETENTION_MODULE_CLOCK_MODEM % 32);
}
#endif
const sleep_retention_module_bitmap_t clock_domain_inited_modules = sleep_retention_module_bitmap_and(inited_modules, mask);
const sleep_retention_module_bitmap_t clock_domain_created_modules = sleep_retention_module_bitmap_and(created_modules, mask);
return sleep_retention_module_bitmap_eq(clock_domain_inited_modules, clock_domain_created_modules);
}
ESP_SYSTEM_INIT_FN(sleep_clock_startup_init, SECONDARY, BIT(0), 106)
{
sleep_retention_module_init_param_t init_param = {
.cbs = { .create = { .handle = sleep_clock_system_retention_init, .arg = NULL } },
.attribute = SLEEP_RETENTION_MODULE_ATTR_PASSIVE
};
sleep_retention_module_init(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM, &init_param);
#if CONFIG_MAC_BB_PD || CONFIG_BT_LE_SLEEP_ENABLE || CONFIG_IEEE802154_SLEEP_ENABLE
init_param = (sleep_retention_module_init_param_t) {
.cbs = { .create = { .handle = sleep_clock_modem_retention_init, .arg = NULL } },
.attribute = SLEEP_RETENTION_MODULE_ATTR_PASSIVE
};
sleep_retention_module_init(SLEEP_RETENTION_MODULE_CLOCK_MODEM, &init_param);
#endif
return ESP_OK;
}

View File

@@ -0,0 +1,490 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stddef.h>
#include <string.h>
#include <inttypes.h>
#include <sys/lock.h>
#include <sys/param.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_check.h"
#include "esp_sleep.h"
#include "esp_log.h"
#include "esp_rom_crc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_heap_caps.h"
#include "riscv/csr.h"
#include "soc/soc_caps.h"
#include "soc/cache_reg.h"
#include "soc/intpri_reg.h"
#include "soc/rtc_periph.h"
#include "esp_private/esp_pmu.h"
#include "esp_private/sleep_cpu.h"
#include "esp_private/sleep_event.h"
#include "esp32h21/rom/rtc.h"
#include "esp32h21/rom/cache.h"
#include "rvsleep-frames.h"
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
#include "esp_private/system_internal.h"
#include "hal/uart_hal.h"
#endif
static __attribute__((unused)) const char *TAG = "sleep";
typedef struct {
uint32_t start;
uint32_t end;
} cpu_domain_dev_regs_region_t;
typedef struct {
cpu_domain_dev_regs_region_t *region;
int region_num;
uint32_t *regs_frame;
} cpu_domain_dev_sleep_frame_t;
/**
* Internal structure which holds all requested light sleep cpu retention parameters
*/
typedef struct {
struct {
RvCoreCriticalSleepFrame *critical_frame;
RvCoreNonCriticalSleepFrame *non_critical_frame;
cpu_domain_dev_sleep_frame_t *intpri_frame;
cpu_domain_dev_sleep_frame_t *cache_config_frame;
} retent;
} sleep_cpu_retention_t;
static DRAM_ATTR sleep_cpu_retention_t s_cpu_retention;
#define CUSTOM_CSR_PCER_MACHINE 0x7e0
#define CUSTOM_CSR_PCMR_MACHINE 0x7e1
#define CUSTOM_CSR_PCCR_MACHINE 0x7e2
#define CUSTOM_CSR_CPU_TESTBUS_CTRL 0x7e3
#define CUSTOM_CSR_PCER_USER 0x800
#define CUSTOM_CSR_PCMR_USER 0x801
#define CUSTOM_CSR_PCCR_USER 0x802
#define CUSTOM_CSR_GPIO_OEN_USER 0x803
#define CUSTOM_CSR_GPIO_IN_USER 0x804
#define CUSTOM_CSR_GPIO_OUT_USER 0x805
#define CUSTOM_CSR_CO_EXCEPTION_CAUSE 0x7f0
#define CUSTOM_CSR_CO_HWLP 0x7f1
#define CUSTOM_CSR_CO_AIA 0x7f2
extern RvCoreCriticalSleepFrame *rv_core_critical_regs_frame;
static void * cpu_domain_dev_sleep_frame_alloc_and_init(const cpu_domain_dev_regs_region_t *regions, const int region_num)
{
const int region_sz = sizeof(cpu_domain_dev_regs_region_t) * region_num;
int regs_frame_sz = 0;
for (int num = 0; num < region_num; num++) {
regs_frame_sz += regions[num].end - regions[num].start;
}
void *frame = heap_caps_malloc(sizeof(cpu_domain_dev_sleep_frame_t) + region_sz + regs_frame_sz, MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL);
if (frame) {
cpu_domain_dev_regs_region_t *region = (cpu_domain_dev_regs_region_t *)(frame + sizeof(cpu_domain_dev_sleep_frame_t));
memcpy(region, regions, region_num * sizeof(cpu_domain_dev_regs_region_t));
void *regs_frame = frame + sizeof(cpu_domain_dev_sleep_frame_t) + region_sz;
memset(regs_frame, 0, regs_frame_sz);
*(cpu_domain_dev_sleep_frame_t *)frame = (cpu_domain_dev_sleep_frame_t) {
.region = region,
.region_num = region_num,
.regs_frame = (uint32_t *)regs_frame
};
}
return frame;
}
static inline void * cpu_domain_intpri_sleep_frame_alloc_and_init(void)
{
const static cpu_domain_dev_regs_region_t regions[] = {
{ .start = INTPRI_CORE0_CPU_INT_ENABLE_REG, .end = INTPRI_CORE0_CPU_INT_TYPE_REG + 4 },
{ .start = INTPRI_CORE0_CPU_INT_PRI_0_REG, .end = INTPRI_CORE0_CPU_INT_THRESH_REG + 4 },
{ .start = INTPRI_CLOCK_GATE_REG, .end = INTPRI_CORE0_CPU_INT_CLEAR_REG + 4 },
};
return cpu_domain_dev_sleep_frame_alloc_and_init(regions, sizeof(regions) / sizeof(regions[0]));
}
static inline void * cpu_domain_cache_config_sleep_frame_alloc_and_init(void)
{
const static cpu_domain_dev_regs_region_t regions[] = {
{ .start = CACHE_L1_CACHE_WRAP_AROUND_CTRL_REG, .end = CACHE_L1_CACHE_WRAP_AROUND_CTRL_REG + 4 }
};
return cpu_domain_dev_sleep_frame_alloc_and_init(regions, sizeof(regions) / sizeof(regions[0]));
}
static esp_err_t esp_sleep_cpu_retention_init_impl(void)
{
if (s_cpu_retention.retent.critical_frame == NULL) {
void *frame = heap_caps_calloc(1, RV_SLEEP_CTX_FRMSZ, MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL);
if (frame == NULL) {
goto err;
}
s_cpu_retention.retent.critical_frame = (RvCoreCriticalSleepFrame *)frame;
rv_core_critical_regs_frame = (RvCoreCriticalSleepFrame *)frame;
}
if (s_cpu_retention.retent.non_critical_frame == NULL) {
void *frame = heap_caps_calloc(1, sizeof(RvCoreNonCriticalSleepFrame), MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL);
if (frame == NULL) {
goto err;
}
s_cpu_retention.retent.non_critical_frame = (RvCoreNonCriticalSleepFrame *)frame;
}
if (s_cpu_retention.retent.intpri_frame == NULL) {
void *frame = cpu_domain_intpri_sleep_frame_alloc_and_init();
if (frame == NULL) {
goto err;
}
s_cpu_retention.retent.intpri_frame = (cpu_domain_dev_sleep_frame_t *)frame;
}
if (s_cpu_retention.retent.cache_config_frame == NULL) {
void *frame = cpu_domain_cache_config_sleep_frame_alloc_and_init();
if (frame == NULL) {
goto err;
}
s_cpu_retention.retent.cache_config_frame = (cpu_domain_dev_sleep_frame_t *)frame;
}
return ESP_OK;
err:
esp_sleep_cpu_retention_deinit();
return ESP_ERR_NO_MEM;
}
static esp_err_t esp_sleep_cpu_retention_deinit_impl(void)
{
if (s_cpu_retention.retent.critical_frame) {
heap_caps_free((void *)s_cpu_retention.retent.critical_frame);
s_cpu_retention.retent.critical_frame = NULL;
rv_core_critical_regs_frame = NULL;
}
if (s_cpu_retention.retent.non_critical_frame) {
heap_caps_free((void *)s_cpu_retention.retent.non_critical_frame);
s_cpu_retention.retent.non_critical_frame = NULL;
}
if (s_cpu_retention.retent.intpri_frame) {
heap_caps_free((void *)s_cpu_retention.retent.intpri_frame);
s_cpu_retention.retent.intpri_frame = NULL;
}
if (s_cpu_retention.retent.cache_config_frame) {
heap_caps_free((void *)s_cpu_retention.retent.cache_config_frame);
s_cpu_retention.retent.cache_config_frame = NULL;
}
return ESP_OK;
}
FORCE_INLINE_ATTR uint32_t save_mstatus_and_disable_global_int(void)
{
return RV_READ_MSTATUS_AND_DISABLE_INTR();
}
FORCE_INLINE_ATTR void restore_mstatus(uint32_t mstatus_val)
{
RV_WRITE_CSR(mstatus, mstatus_val);
}
static IRAM_ATTR RvCoreNonCriticalSleepFrame * rv_core_noncritical_regs_save(void)
{
assert(s_cpu_retention.retent.non_critical_frame);
RvCoreNonCriticalSleepFrame *frame = s_cpu_retention.retent.non_critical_frame;
frame->mscratch = RV_READ_CSR(mscratch);
frame->mideleg = RV_READ_CSR(mideleg);
frame->misa = RV_READ_CSR(misa);
frame->tselect = RV_READ_CSR(tselect);
frame->tdata1 = RV_READ_CSR(tdata1);
frame->tdata2 = RV_READ_CSR(tdata2);
frame->tcontrol = RV_READ_CSR(tcontrol);
frame->pmpaddr0 = RV_READ_CSR(pmpaddr0);
frame->pmpaddr1 = RV_READ_CSR(pmpaddr1);
frame->pmpaddr2 = RV_READ_CSR(pmpaddr2);
frame->pmpaddr3 = RV_READ_CSR(pmpaddr3);
frame->pmpaddr4 = RV_READ_CSR(pmpaddr4);
frame->pmpaddr5 = RV_READ_CSR(pmpaddr5);
frame->pmpaddr6 = RV_READ_CSR(pmpaddr6);
frame->pmpaddr7 = RV_READ_CSR(pmpaddr7);
frame->pmpaddr8 = RV_READ_CSR(pmpaddr8);
frame->pmpaddr9 = RV_READ_CSR(pmpaddr9);
frame->pmpaddr10 = RV_READ_CSR(pmpaddr10);
frame->pmpaddr11 = RV_READ_CSR(pmpaddr11);
frame->pmpaddr12 = RV_READ_CSR(pmpaddr12);
frame->pmpaddr13 = RV_READ_CSR(pmpaddr13);
frame->pmpaddr14 = RV_READ_CSR(pmpaddr14);
frame->pmpaddr15 = RV_READ_CSR(pmpaddr15);
frame->pmpcfg0 = RV_READ_CSR(pmpcfg0);
frame->pmpcfg1 = RV_READ_CSR(pmpcfg1);
frame->pmpcfg2 = RV_READ_CSR(pmpcfg2);
frame->pmpcfg3 = RV_READ_CSR(pmpcfg3);
frame->pmaaddr0 = RV_READ_CSR(CSR_PMAADDR(0));
frame->pmaaddr1 = RV_READ_CSR(CSR_PMAADDR(1));
frame->pmaaddr2 = RV_READ_CSR(CSR_PMAADDR(2));
frame->pmaaddr3 = RV_READ_CSR(CSR_PMAADDR(3));
frame->pmaaddr4 = RV_READ_CSR(CSR_PMAADDR(4));
frame->pmaaddr5 = RV_READ_CSR(CSR_PMAADDR(5));
frame->pmaaddr6 = RV_READ_CSR(CSR_PMAADDR(6));
frame->pmaaddr7 = RV_READ_CSR(CSR_PMAADDR(7));
frame->pmaaddr8 = RV_READ_CSR(CSR_PMAADDR(8));
frame->pmaaddr9 = RV_READ_CSR(CSR_PMAADDR(9));
frame->pmaaddr10 = RV_READ_CSR(CSR_PMAADDR(10));
frame->pmaaddr11 = RV_READ_CSR(CSR_PMAADDR(11));
frame->pmaaddr12 = RV_READ_CSR(CSR_PMAADDR(12));
frame->pmaaddr13 = RV_READ_CSR(CSR_PMAADDR(13));
frame->pmaaddr14 = RV_READ_CSR(CSR_PMAADDR(14));
frame->pmaaddr15 = RV_READ_CSR(CSR_PMAADDR(15));
frame->pmacfg0 = RV_READ_CSR(CSR_PMACFG(0));
frame->pmacfg1 = RV_READ_CSR(CSR_PMACFG(1));
frame->pmacfg2 = RV_READ_CSR(CSR_PMACFG(2));
frame->pmacfg3 = RV_READ_CSR(CSR_PMACFG(3));
frame->pmacfg4 = RV_READ_CSR(CSR_PMACFG(4));
frame->pmacfg5 = RV_READ_CSR(CSR_PMACFG(5));
frame->pmacfg6 = RV_READ_CSR(CSR_PMACFG(6));
frame->pmacfg7 = RV_READ_CSR(CSR_PMACFG(7));
frame->pmacfg8 = RV_READ_CSR(CSR_PMACFG(8));
frame->pmacfg9 = RV_READ_CSR(CSR_PMACFG(9));
frame->pmacfg10 = RV_READ_CSR(CSR_PMACFG(10));
frame->pmacfg11 = RV_READ_CSR(CSR_PMACFG(11));
frame->pmacfg12 = RV_READ_CSR(CSR_PMACFG(12));
frame->pmacfg13 = RV_READ_CSR(CSR_PMACFG(13));
frame->pmacfg14 = RV_READ_CSR(CSR_PMACFG(14));
frame->pmacfg15 = RV_READ_CSR(CSR_PMACFG(15));
frame->utvec = RV_READ_CSR(utvec);
frame->ustatus = RV_READ_CSR(ustatus);
frame->uepc = RV_READ_CSR(uepc);
frame->ucause = RV_READ_CSR(ucause);
frame->mpcer = RV_READ_CSR(CUSTOM_CSR_PCER_MACHINE);
frame->mpcmr = RV_READ_CSR(CUSTOM_CSR_PCMR_MACHINE);
frame->mpccr = RV_READ_CSR(CUSTOM_CSR_PCCR_MACHINE);
frame->cpu_testbus_ctrl = RV_READ_CSR(CUSTOM_CSR_CPU_TESTBUS_CTRL);
frame->upcer = RV_READ_CSR(CUSTOM_CSR_PCER_USER);
frame->upcmr = RV_READ_CSR(CUSTOM_CSR_PCMR_USER);
frame->upccr = RV_READ_CSR(CUSTOM_CSR_PCCR_USER);
frame->ugpio_oen = RV_READ_CSR(CUSTOM_CSR_GPIO_OEN_USER);
frame->ugpio_in = RV_READ_CSR(CUSTOM_CSR_GPIO_IN_USER);
frame->ugpio_out = RV_READ_CSR(CUSTOM_CSR_GPIO_OUT_USER);
return frame;
}
static IRAM_ATTR void rv_core_noncritical_regs_restore(RvCoreNonCriticalSleepFrame *frame)
{
assert(frame);
RV_WRITE_CSR(mscratch, frame->mscratch);
RV_WRITE_CSR(mideleg, frame->mideleg);
RV_WRITE_CSR(misa, frame->misa);
RV_WRITE_CSR(tselect, frame->tselect);
RV_WRITE_CSR(tdata1, frame->tdata1);
RV_WRITE_CSR(tdata2, frame->tdata2);
RV_WRITE_CSR(tcontrol, frame->tcontrol);
RV_WRITE_CSR(pmpaddr0, frame->pmpaddr0);
RV_WRITE_CSR(pmpaddr1, frame->pmpaddr1);
RV_WRITE_CSR(pmpaddr2, frame->pmpaddr2);
RV_WRITE_CSR(pmpaddr3, frame->pmpaddr3);
RV_WRITE_CSR(pmpaddr4, frame->pmpaddr4);
RV_WRITE_CSR(pmpaddr5, frame->pmpaddr5);
RV_WRITE_CSR(pmpaddr6, frame->pmpaddr6);
RV_WRITE_CSR(pmpaddr7, frame->pmpaddr7);
RV_WRITE_CSR(pmpaddr8, frame->pmpaddr8);
RV_WRITE_CSR(pmpaddr9, frame->pmpaddr9);
RV_WRITE_CSR(pmpaddr10,frame->pmpaddr10);
RV_WRITE_CSR(pmpaddr11,frame->pmpaddr11);
RV_WRITE_CSR(pmpaddr12,frame->pmpaddr12);
RV_WRITE_CSR(pmpaddr13,frame->pmpaddr13);
RV_WRITE_CSR(pmpaddr14,frame->pmpaddr14);
RV_WRITE_CSR(pmpaddr15,frame->pmpaddr15);
RV_WRITE_CSR(pmpcfg0, frame->pmpcfg0);
RV_WRITE_CSR(pmpcfg1, frame->pmpcfg1);
RV_WRITE_CSR(pmpcfg2, frame->pmpcfg2);
RV_WRITE_CSR(pmpcfg3, frame->pmpcfg3);
RV_WRITE_CSR(CSR_PMAADDR(0), frame->pmaaddr0);
RV_WRITE_CSR(CSR_PMAADDR(1), frame->pmaaddr1);
RV_WRITE_CSR(CSR_PMAADDR(2), frame->pmaaddr2);
RV_WRITE_CSR(CSR_PMAADDR(3), frame->pmaaddr3);
RV_WRITE_CSR(CSR_PMAADDR(4), frame->pmaaddr4);
RV_WRITE_CSR(CSR_PMAADDR(5), frame->pmaaddr5);
RV_WRITE_CSR(CSR_PMAADDR(6), frame->pmaaddr6);
RV_WRITE_CSR(CSR_PMAADDR(7), frame->pmaaddr7);
RV_WRITE_CSR(CSR_PMAADDR(8), frame->pmaaddr8);
RV_WRITE_CSR(CSR_PMAADDR(9), frame->pmaaddr9);
RV_WRITE_CSR(CSR_PMAADDR(10),frame->pmaaddr10);
RV_WRITE_CSR(CSR_PMAADDR(11),frame->pmaaddr11);
RV_WRITE_CSR(CSR_PMAADDR(12),frame->pmaaddr12);
RV_WRITE_CSR(CSR_PMAADDR(13),frame->pmaaddr13);
RV_WRITE_CSR(CSR_PMAADDR(14),frame->pmaaddr14);
RV_WRITE_CSR(CSR_PMAADDR(15),frame->pmaaddr15);
RV_WRITE_CSR(CSR_PMACFG(0), frame->pmacfg0);
RV_WRITE_CSR(CSR_PMACFG(1), frame->pmacfg1);
RV_WRITE_CSR(CSR_PMACFG(2), frame->pmacfg2);
RV_WRITE_CSR(CSR_PMACFG(3), frame->pmacfg3);
RV_WRITE_CSR(CSR_PMACFG(4), frame->pmacfg4);
RV_WRITE_CSR(CSR_PMACFG(5), frame->pmacfg5);
RV_WRITE_CSR(CSR_PMACFG(6), frame->pmacfg6);
RV_WRITE_CSR(CSR_PMACFG(7), frame->pmacfg7);
RV_WRITE_CSR(CSR_PMACFG(8), frame->pmacfg8);
RV_WRITE_CSR(CSR_PMACFG(9), frame->pmacfg9);
RV_WRITE_CSR(CSR_PMACFG(10), frame->pmacfg10);
RV_WRITE_CSR(CSR_PMACFG(11), frame->pmacfg11);
RV_WRITE_CSR(CSR_PMACFG(12), frame->pmacfg12);
RV_WRITE_CSR(CSR_PMACFG(13), frame->pmacfg13);
RV_WRITE_CSR(CSR_PMACFG(14), frame->pmacfg14);
RV_WRITE_CSR(CSR_PMACFG(15), frame->pmacfg15);
RV_WRITE_CSR(utvec, frame->utvec);
RV_WRITE_CSR(ustatus, frame->ustatus);
RV_WRITE_CSR(uepc, frame->uepc);
RV_WRITE_CSR(ucause, frame->ucause);
RV_WRITE_CSR(CUSTOM_CSR_PCER_MACHINE, frame->mpcer);
RV_WRITE_CSR(CUSTOM_CSR_PCMR_MACHINE, frame->mpcmr);
RV_WRITE_CSR(CUSTOM_CSR_PCCR_MACHINE, frame->mpccr);
RV_WRITE_CSR(CUSTOM_CSR_CPU_TESTBUS_CTRL, frame->cpu_testbus_ctrl);
RV_WRITE_CSR(CUSTOM_CSR_PCER_USER, frame->upcer);
RV_WRITE_CSR(CUSTOM_CSR_PCMR_USER, frame->upcmr);
RV_WRITE_CSR(CUSTOM_CSR_PCCR_USER, frame->upccr);
RV_WRITE_CSR(CUSTOM_CSR_GPIO_OEN_USER,frame->ugpio_oen);
RV_WRITE_CSR(CUSTOM_CSR_GPIO_IN_USER, frame->ugpio_in);
RV_WRITE_CSR(CUSTOM_CSR_GPIO_OUT_USER,frame->ugpio_out);
}
static IRAM_ATTR void cpu_domain_dev_regs_save(cpu_domain_dev_sleep_frame_t *frame)
{
assert(frame);
cpu_domain_dev_regs_region_t *region = frame->region;
uint32_t *regs_frame = frame->regs_frame;
int offset = 0;
for (int i = 0; i < frame->region_num; i++) {
for (uint32_t addr = region[i].start; addr < region[i].end; addr+=4) {
regs_frame[offset++] = *(uint32_t *)addr;
}
}
}
static IRAM_ATTR void cpu_domain_dev_regs_restore(cpu_domain_dev_sleep_frame_t *frame)
{
assert(frame);
cpu_domain_dev_regs_region_t *region = frame->region;
uint32_t *regs_frame = frame->regs_frame;
int offset = 0;
for (int i = 0; i < frame->region_num; i++) {
for (uint32_t addr = region[i].start; addr < region[i].end; addr+=4) {
*(uint32_t *)addr = regs_frame[offset++];
}
}
}
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
static IRAM_ATTR void update_retention_frame_crc(uint32_t *frame_ptr, uint32_t frame_check_size, uint32_t *frame_crc_ptr)
{
*(frame_crc_ptr) = esp_rom_crc32_le(0, (void *)frame_ptr, frame_check_size);
}
static IRAM_ATTR void validate_retention_frame_crc(uint32_t *frame_ptr, uint32_t frame_check_size, uint32_t *frame_crc_ptr)
{
if(*(frame_crc_ptr) != esp_rom_crc32_le(0, (void *)(frame_ptr), frame_check_size)){
// resume uarts
for (int i = 0; i < SOC_UART_NUM; ++i) {
if (!uart_ll_is_enabled(i)) {
continue;
}
uart_ll_force_xon(i);
}
/* Since it is still in the critical now, use ESP_EARLY_LOG */
ESP_EARLY_LOGE(TAG, "Sleep retention frame is corrupted");
esp_restart_noos();
}
}
#endif
extern RvCoreCriticalSleepFrame * rv_core_critical_regs_save(void);
extern RvCoreCriticalSleepFrame * rv_core_critical_regs_restore(void);
typedef uint32_t (* sleep_cpu_entry_cb_t)(uint32_t, uint32_t, uint32_t, bool);
static IRAM_ATTR esp_err_t do_cpu_retention(sleep_cpu_entry_cb_t goto_sleep,
uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp_mem_inf_fpu, bool dslp)
{
RvCoreCriticalSleepFrame * frame = rv_core_critical_regs_save();
if ((frame->pmufunc & 0x3) == 0x1) {
esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_CPU_TO_MEM_END, (void *)0);
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
/* Minus 2 * sizeof(long) is for bypass `pmufunc` and `frame_crc` field */
update_retention_frame_crc((uint32_t*)frame, RV_SLEEP_CTX_FRMSZ - 2 * sizeof(long), (uint32_t *)(&frame->frame_crc));
#endif
REG_WRITE(RTC_SLEEP_WAKE_STUB_ADDR_REG, (uint32_t)rv_core_critical_regs_restore);
return (*goto_sleep)(wakeup_opt, reject_opt, lslp_mem_inf_fpu, dslp);
}
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
else {
validate_retention_frame_crc((uint32_t*)frame, RV_SLEEP_CTX_FRMSZ - 2 * sizeof(long), (uint32_t *)(&frame->frame_crc));
}
#endif
return pmu_sleep_finish(dslp);
}
esp_err_t IRAM_ATTR esp_sleep_cpu_retention(uint32_t (*goto_sleep)(uint32_t, uint32_t, uint32_t, bool),
uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp_mem_inf_fpu, bool dslp)
{
esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_CPU_TO_MEM_START, (void *)0);
uint32_t mstatus = save_mstatus_and_disable_global_int();
cpu_domain_dev_regs_save(s_cpu_retention.retent.intpri_frame);
cpu_domain_dev_regs_save(s_cpu_retention.retent.cache_config_frame);
RvCoreNonCriticalSleepFrame *frame = rv_core_noncritical_regs_save();
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
/* Minus sizeof(long) is for bypass `frame_crc` field */
update_retention_frame_crc((uint32_t*)frame, sizeof(RvCoreNonCriticalSleepFrame) - sizeof(long), (uint32_t *)(&frame->frame_crc));
#endif
esp_err_t err = do_cpu_retention(goto_sleep, wakeup_opt, reject_opt, lslp_mem_inf_fpu, dslp);
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
validate_retention_frame_crc((uint32_t*)frame, sizeof(RvCoreNonCriticalSleepFrame) - sizeof(long), (uint32_t *)(&frame->frame_crc));
#endif
rv_core_noncritical_regs_restore(frame);
cpu_domain_dev_regs_restore(s_cpu_retention.retent.cache_config_frame);
cpu_domain_dev_regs_restore(s_cpu_retention.retent.intpri_frame);
restore_mstatus(mstatus);
return err;
}
esp_err_t esp_sleep_cpu_retention_init(void)
{
return esp_sleep_cpu_retention_init_impl();
}
esp_err_t esp_sleep_cpu_retention_deinit(void)
{
return esp_sleep_cpu_retention_deinit_impl();
}
bool cpu_domain_pd_allowed(void)
{
return (s_cpu_retention.retent.critical_frame != NULL) && \
(s_cpu_retention.retent.non_critical_frame != NULL) && \
(s_cpu_retention.retent.intpri_frame != NULL) && \
(s_cpu_retention.retent.cache_config_frame != NULL);
}
esp_err_t sleep_cpu_configure(bool light_sleep_enable)
{
#if ESP_SLEEP_POWER_DOWN_CPU
if (light_sleep_enable) {
ESP_RETURN_ON_ERROR(esp_sleep_cpu_retention_init(), TAG, "Failed to enable CPU power down during light sleep.");
} else {
ESP_RETURN_ON_ERROR(esp_sleep_cpu_retention_deinit(), TAG, "Failed to release CPU retention memory");
}
#endif
return ESP_OK;
}

View File

@@ -0,0 +1,216 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/soc.h"
#include "rvsleep-frames.h"
#include "soc/soc_caps.h"
#include "sdkconfig.h"
.section .data1,"aw"
.global rv_core_critical_regs_frame
.type rv_core_critical_regs_frame,@object
.align 4
rv_core_critical_regs_frame:
.word 0
/*
--------------------------------------------------------------------------------
This assembly subroutine is used to save the critical registers of the CPU
core to the internal RAM before sleep, and modify the PMU control flag to
indicate that the system needs to sleep. When the subroutine returns, it
will return the memory pointer that saves the context information of the CPU
critical registers.
--------------------------------------------------------------------------------
*/
.section .iram1,"ax"
.global rv_core_critical_regs_save
.type rv_core_critical_regs_save,@function
.align 4
rv_core_critical_regs_save:
/* arrived here in critical section. we need:
save riscv core critical registers to RvCoreCriticalSleepFrame
*/
csrw mscratch, t0 /* use mscratch as temp storage */
la t0, rv_core_critical_regs_frame
lw t0, 0(t0) /* t0 pointer to RvCoreCriticalSleepFrame object */
sw ra, RV_SLP_CTX_RA(t0)
sw sp, RV_SLP_CTX_SP(t0)
sw gp, RV_SLP_CTX_GP(t0)
sw tp, RV_SLP_CTX_TP(t0)
sw t1, RV_SLP_CTX_T1(t0)
sw t2, RV_SLP_CTX_T2(t0)
sw s0, RV_SLP_CTX_S0(t0)
sw s1, RV_SLP_CTX_S1(t0)
/* a0 is caller saved, so it does not need to be saved, but it should be the
pointer value of RvCoreCriticalSleepFrame for return.
*/
mv a0, t0
sw a0, RV_SLP_CTX_A0(t0)
sw a1, RV_SLP_CTX_A1(t0)
sw a2, RV_SLP_CTX_A2(t0)
sw a3, RV_SLP_CTX_A3(t0)
sw a4, RV_SLP_CTX_A4(t0)
sw a5, RV_SLP_CTX_A5(t0)
sw a6, RV_SLP_CTX_A6(t0)
sw a7, RV_SLP_CTX_A7(t0)
sw s2, RV_SLP_CTX_S2(t0)
sw s3, RV_SLP_CTX_S3(t0)
sw s4, RV_SLP_CTX_S4(t0)
sw s5, RV_SLP_CTX_S5(t0)
sw s6, RV_SLP_CTX_S6(t0)
sw s7, RV_SLP_CTX_S7(t0)
sw s8, RV_SLP_CTX_S8(t0)
sw s9, RV_SLP_CTX_S9(t0)
sw s10, RV_SLP_CTX_S10(t0)
sw s11, RV_SLP_CTX_S11(t0)
sw t3, RV_SLP_CTX_T3(t0)
sw t4, RV_SLP_CTX_T4(t0)
sw t5, RV_SLP_CTX_T5(t0)
sw t6, RV_SLP_CTX_T6(t0)
csrr t1, mstatus
sw t1, RV_SLP_CTX_MSTATUS(t0)
csrr t2, mtvec
sw t2, RV_SLP_CTX_MTVEC(t0)
csrr t3, mcause
sw t3, RV_SLP_CTX_MCAUSE(t0)
csrr t1, mtval
sw t1, RV_SLP_CTX_MTVAL(t0)
csrr t2, mie
sw t2, RV_SLP_CTX_MIE(t0)
csrr t3, mip
sw t3, RV_SLP_CTX_MIP(t0)
csrr t1, mepc
sw t1, RV_SLP_CTX_MEPC(t0)
/*
!!! Let idf knows it's going to sleep !!!
RV_SLP_STK_PMUFUNC field is used to identify whether it is going to sleep or
has just been awakened. We use the lowest 2 bits as indication information,
3 means being awakened, 1 means going to sleep.
*/
li t1, ~0x3
lw t2, RV_SLP_CTX_PMUFUNC(t0)
and t2, t1, t2
ori t2, t2, 0x1
sw t2, RV_SLP_CTX_PMUFUNC(t0)
mv t3, t0
csrr t0, mscratch
lw t1, RV_SLP_CTX_T1(t3)
lw t2, RV_SLP_CTX_T2(t3)
lw t3, RV_SLP_CTX_T3(t3)
ret
.size rv_core_critical_regs_save, . - rv_core_critical_regs_save
#define CSR_PCER_U 0x800
#define CSR_PCMR_U 0x801
#define PCER_CYCLES (1<<0) /* count clock cycles */
#define PCMR_GLOBAL_EN (1<<0) /* enable count */
#define pcer CSR_PCER_U
#define pcmr CSR_PCMR_U
/*
--------------------------------------------------------------------------------
This assembly subroutine is used to restore the CPU core critical register
context before sleep after system wakes up, modify the PMU control
information, and return the critical register context memory object pointer.
After the subroutine returns, continue to restore other modules of the
system.
--------------------------------------------------------------------------------
*/
.section .iram1,"ax"
.global rv_core_critical_regs_restore
.weak rv_core_critical_regs_restore
.type rv_core_critical_regs_restore,@function
.global _rv_core_critical_regs_restore
.type _rv_core_critical_regs_restore,@function
.align 4
_rv_core_critical_regs_restore: /* export a strong symbol to jump to here, used
* for a static callback */
nop
rv_core_critical_regs_restore:
la t0, rv_core_critical_regs_frame
lw t0, 0(t0) /* t0 pointer to RvCoreCriticalSleepFrame object */
beqz t0, .skip_restore /* make sure we do not jump to zero address */
/*
!!! Let idf knows it's sleep awake. !!!
RV_SLP_STK_PMUFUNC field is used to identify whether it is going to sleep or
has just been awakened. We use the lowest 2 bits as indication information,
3 means being awakened, 1 means going to sleep.
*/
lw t1, RV_SLP_CTX_PMUFUNC(t0)
ori t1, t1, 0x3
sw t1, RV_SLP_CTX_PMUFUNC(t0)
lw t2, RV_SLP_CTX_MEPC(t0)
csrw mepc, t2
lw t3, RV_SLP_CTX_MIP(t0)
csrw mip, t3
lw t1, RV_SLP_CTX_MIE(t0)
csrw mie, t1
lw t2, RV_SLP_CTX_MSTATUS(t0)
csrw mstatus, t2
lw t3, RV_SLP_CTX_MTVEC(t0)
csrw mtvec, t3
lw t1, RV_SLP_CTX_MCAUSE(t0)
csrw mcause, t1
lw t2, RV_SLP_CTX_MTVAL(t0)
csrw mtval, t2
lw t6, RV_SLP_CTX_T6(t0)
lw t5, RV_SLP_CTX_T5(t0)
lw t4, RV_SLP_CTX_T4(t0)
lw t3, RV_SLP_CTX_T3(t0)
lw s11, RV_SLP_CTX_S11(t0)
lw s10, RV_SLP_CTX_S10(t0)
lw s9, RV_SLP_CTX_S9(t0)
lw s8, RV_SLP_CTX_S8(t0)
lw s7, RV_SLP_CTX_S7(t0)
lw s6, RV_SLP_CTX_S6(t0)
lw s5, RV_SLP_CTX_S5(t0)
lw s4, RV_SLP_CTX_S4(t0)
lw s3, RV_SLP_CTX_S3(t0)
lw s2, RV_SLP_CTX_S2(t0)
lw a7, RV_SLP_CTX_A7(t0)
lw a6, RV_SLP_CTX_A6(t0)
lw a5, RV_SLP_CTX_A5(t0)
lw a4, RV_SLP_CTX_A4(t0)
lw a3, RV_SLP_CTX_A3(t0)
lw a2, RV_SLP_CTX_A2(t0)
lw a1, RV_SLP_CTX_A1(t0)
lw a0, RV_SLP_CTX_A0(t0)
lw s1, RV_SLP_CTX_S1(t0)
lw s0, RV_SLP_CTX_S0(t0)
lw t2, RV_SLP_CTX_T2(t0)
lw t1, RV_SLP_CTX_T1(t0)
lw tp, RV_SLP_CTX_TP(t0)
lw gp, RV_SLP_CTX_GP(t0)
lw sp, RV_SLP_CTX_SP(t0)
lw ra, RV_SLP_CTX_RA(t0)
lw t0, RV_SLP_CTX_T0(t0)
.skip_restore:
ret
.size rv_core_critical_regs_restore, . - rv_core_critical_regs_restore

View File

@@ -84,6 +84,7 @@ SECONDARY: 106: sleep_clock_startup_init in components/esp_hw_support/lowpower/p
SECONDARY: 106: sleep_clock_startup_init in components/esp_hw_support/lowpower/port/esp32c6/sleep_clock.c on BIT(0)
SECONDARY: 106: sleep_clock_startup_init in components/esp_hw_support/lowpower/port/esp32c61/sleep_clock.c on BIT(0)
SECONDARY: 106: sleep_clock_startup_init in components/esp_hw_support/lowpower/port/esp32h2/sleep_clock.c on BIT(0)
SECONDARY: 106: sleep_clock_startup_init in components/esp_hw_support/lowpower/port/esp32h21/sleep_clock.c on BIT(0)
SECONDARY: 106: sleep_clock_startup_init in components/esp_hw_support/lowpower/port/esp32p4/sleep_clock.c on BIT(0)
SECONDARY: 107: sleep_sys_periph_startup_init in components/esp_hw_support/sleep_system_peripheral.c on BIT(0)
SECONDARY: 108: sleep_mmu_startup_init in components/esp_hw_support/lowpower/port/esp32c5/sleep_mmu.c on BIT(0)