diff --git a/components/esp_hw_support/lowpower/port/esp32s31/rvsleep-frames.h b/components/esp_hw_support/lowpower/port/esp32s31/rvsleep-frames.h new file mode 100644 index 00000000000..cbf7257b02b --- /dev/null +++ b/components/esp_hw_support/lowpower/port/esp32s31/rvsleep-frames.h @@ -0,0 +1,225 @@ +/* + * SPDX-FileCopyrightText: 2026 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_MINTTHRESH, mintthresh) /* Machine intr threshold */ + + /* FPU context */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT0, fpu_ft0) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT1, fpu_ft1) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT2, fpu_ft2) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT3, fpu_ft3) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT4, fpu_ft4) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT5, fpu_ft5) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT6, fpu_ft6) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT7, fpu_ft7) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS0, fpu_fs0) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS1, fpu_fs1) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA0, fpu_fa0) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA1, fpu_fa1) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA2, fpu_fa2) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA3, fpu_fa3) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA4, fpu_fa4) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA5, fpu_fa5) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA6, fpu_fa6) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA7, fpu_fa7) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS2, fpu_fs2) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS3, fpu_fs3) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS4, fpu_fs4) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS5, fpu_fs5) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS6, fpu_fs6) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS7, fpu_fs7) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS8, fpu_fs8) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS9, fpu_fs9) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS10, fpu_fs10) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS11, fpu_fs11) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT8, fpu_ft8) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT9, fpu_ft9) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT10, fpu_ft10) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT11, fpu_ft11) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FCSR, fpu_fcsr) + 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_MISA, misa) + STRUCT_FIELD (long, 4, RV_SLP_CTX_MTVT, mtvt) /* Machine mode vector interrupt base address register */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MSCRATCHCSW, mscratchcsw) /* Machine mode multi-mode data backup register */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MSCRATCHCSWL, mscratchcswl) /* Machine mode interrupt data backup register */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MXSTATUS, mxstatus) /* Extended status register */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MHCR, mhcr) /* Hardware control register */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MHINT, mhint) /* Implicit operation register */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MRADDR, mraddr) /* Processor reset start address indication register */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MEXSTATUS, mexstatus) /* Extended exception status register */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_JVT, jvt) /* Table Jump base vector and control register */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MEDELEG, medeleg) /* Table Jump base vector and control register */ + 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_MCYCLE, mcycle) + STRUCT_FIELD (long, 4, RV_SLP_CTX_MCYCLEH, mcycleh) +#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__ */ diff --git a/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu.c b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu.c new file mode 100644 index 00000000000..495465573cf --- /dev/null +++ b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu.c @@ -0,0 +1,525 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include "esp_attr.h" +#include "esp_check.h" +#include "esp_ipc_isr.h" +#include "esp_sleep.h" +#include "esp_log.h" +#include "esp_rom_crc.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/portmacro.h" +#include "riscv/csr.h" +#include "soc/clic_reg.h" +#include "soc/rtc_periph.h" +#include "soc/soc_caps.h" +#include "soc/hp_sys_clkrst_reg.h" +#include "esp_private/sleep_cpu.h" +#include "esp_private/sleep_event.h" +#include "sdkconfig.h" +#include "esp_private/esp_pmu.h" + +#include "esp32s31/rom/ets_sys.h" +#include "esp32s31/rom/rtc.h" +#include "rvsleep-frames.h" +#include "sleep_cpu_retention.h" + +#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME +#include "esp_private/system_internal.h" +#include "hal/uart_hal.h" +#endif + + +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE +static DRAM_ATTR smp_retention_state_t s_smp_retention_state[portNUM_PROCESSORS]; +#endif + +static bool s_fpu_saved[portNUM_PROCESSORS]; + +ESP_LOG_ATTR_TAG(TAG, "sleep"); + +static DRAM_ATTR __attribute__((unused)) sleep_cpu_retention_t s_cpu_retention; + +extern RvCoreCriticalSleepFrame *rv_core_critical_regs_frame[portNUM_PROCESSORS]; + +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); +} + +#define CUSTOM_CSR_MTVT (0x307) +#define CUSTOM_CSR_MSCRATCHCSW (0x348) +#define CUSTOM_CSR_MSCRATCHCSWL (0x349) +#define CUSTOM_CSR_MXSTATUS (0x7c0) +#define CUSTOM_CSR_MHCR (0x7c1) +#define CUSTOM_CSR_MHINT (0x7c5) +#define CUSTOM_CSR_MRADDR (0x7e0) +#define CUSTOM_CSR_MEXSTATUS (0x7e1) +#define CUSTOM_CSR_JVT (0x017) +#define CUSTOM_CSR_UINTTHRESH (0x047) +#define CUSTOM_CSR_UINTSTATUS (0xCB1) + +static IRAM_ATTR RvCoreNonCriticalSleepFrame * rv_core_noncritical_regs_save(void) +{ + RvCoreNonCriticalSleepFrame *frame = s_cpu_retention.retent.non_critical_frame[esp_cpu_get_core_id()]; + + frame->mscratch = RV_READ_CSR(mscratch); + frame->misa = RV_READ_CSR(misa); + frame->mtvt = RV_READ_CSR(CUSTOM_CSR_MTVT); + frame->mscratchcsw = RV_READ_CSR(CUSTOM_CSR_MSCRATCHCSW); + frame->mscratchcswl = RV_READ_CSR(CUSTOM_CSR_MSCRATCHCSWL); + frame->mxstatus = RV_READ_CSR(CUSTOM_CSR_MXSTATUS); + frame->mhcr = RV_READ_CSR(CUSTOM_CSR_MHCR); + frame->mhint = RV_READ_CSR(CUSTOM_CSR_MHINT); + frame->mraddr = RV_READ_CSR(CUSTOM_CSR_MRADDR); + frame->mexstatus = RV_READ_CSR(CUSTOM_CSR_MEXSTATUS); + frame->jvt = RV_READ_CSR(CUSTOM_CSR_JVT); + frame->medeleg = RV_READ_CSR(medeleg); + 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->mcycle = RV_READ_CSR(mcycle); + frame->mcycleh = RV_READ_CSR(mcycleh); + return frame; +} + +static IRAM_ATTR void rv_core_noncritical_regs_restore(void) +{ + RvCoreNonCriticalSleepFrame *frame = s_cpu_retention.retent.non_critical_frame[esp_cpu_get_core_id()]; + + 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(mscratch, frame->mscratch); + RV_WRITE_CSR(misa, frame->misa); + RV_WRITE_CSR(CUSTOM_CSR_MTVT, frame->mtvt); + RV_WRITE_CSR(CUSTOM_CSR_MSCRATCHCSW, frame->mscratchcsw); + RV_WRITE_CSR(CUSTOM_CSR_MSCRATCHCSWL, frame->mscratchcswl); + RV_WRITE_CSR(CUSTOM_CSR_MXSTATUS, frame->mxstatus); + RV_WRITE_CSR(CUSTOM_CSR_MHCR, frame->mhcr); + RV_WRITE_CSR(CUSTOM_CSR_MHINT, frame->mhint); + RV_WRITE_CSR(CUSTOM_CSR_MRADDR, frame->mraddr); + RV_WRITE_CSR(CUSTOM_CSR_MEXSTATUS, frame->mexstatus); + RV_WRITE_CSR(CUSTOM_CSR_JVT, frame->jvt); + RV_WRITE_CSR(medeleg, frame->medeleg); + 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(mcycle, frame->mcycle); + RV_WRITE_CSR(mcycleh, frame->mcycleh); +} + +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); +extern void rv_core_fpu_save(RvCoreCriticalSleepFrame *frame); +extern void rv_core_fpu_restore(RvCoreCriticalSleepFrame *frame); +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) +{ + uint8_t core_id = esp_cpu_get_core_id(); + bool reject = false; + RvCoreCriticalSleepFrame *frame = s_cpu_retention.retent.critical_frame[core_id]; + /* mstatus is core privated CSR, do it near the core critical regs restore */ + uint32_t mstatus = save_mstatus_and_disable_global_int(); + s_fpu_saved[core_id] = xPortFPUContextIsDirty(core_id); + if (s_fpu_saved[core_id]) { + rv_core_fpu_save(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_SZ1 - 2 * sizeof(long), (uint32_t *)(&frame->frame_crc)); +#endif + REG_WRITE(RTC_SLEEP_WAKE_STUB_ADDR_REG, (uint32_t)rv_core_critical_regs_restore); + +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE + atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_DONE); + while (atomic_load(&s_smp_retention_state[!core_id]) != SMP_BACKUP_DONE) { + ; + } +#endif + + reject = (*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_SZ1 - 2 * sizeof(long), (uint32_t *)(&frame->frame_crc)); + } +#endif + if (s_fpu_saved[core_id]) { + rv_core_fpu_restore(frame); + } + restore_mstatus(mstatus); + return reject ? reject : 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); + uint8_t core_id = esp_cpu_get_core_id(); +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE + atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_START); +#endif + cpu_domain_dev_regs_save(s_cpu_retention.retent.clic_frame[core_id]); + rv_core_noncritical_regs_save(); + +#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME + RvCoreNonCriticalSleepFrame *frame = s_cpu_retention.retent.non_critical_frame[core_id]; + /* 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 (err == ESP_OK) { +#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME + validate_retention_frame_crc((uint32_t*)frame, sizeof(RvCoreNonCriticalSleepFrame) - sizeof(long), (uint32_t *)(&frame->frame_crc)); +#endif + } +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE + // Start core1 + if (core_id == 0) { + REG_SET_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_CPU_CLK_EN); + REG_CLR_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_GLOBAL_RST_EN); + } + + atomic_store(&s_smp_retention_state[core_id], SMP_RESTORE_START); +#endif + if (err == ESP_OK) { + cpu_domain_dev_regs_restore(s_cpu_retention.retent.clic_frame[core_id]); + rv_core_noncritical_regs_restore(); + } + +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE + atomic_store(&s_smp_retention_state[core_id], SMP_RESTORE_DONE); +#endif + return err; +} + +esp_err_t esp_sleep_cpu_retention_init(void) +{ +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE + return esp_sleep_cpu_retention_init_impl(& s_cpu_retention, s_smp_retention_state); +#else + return esp_sleep_cpu_retention_init_impl(& s_cpu_retention); +#endif +} + +esp_err_t esp_sleep_cpu_retention_deinit(void) +{ + return esp_sleep_cpu_retention_deinit_impl(& s_cpu_retention); +} + +bool cpu_domain_pd_allowed(void) +{ + bool allowed = true; + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + allowed &= (s_cpu_retention.retent.critical_frame[core_id] != NULL); + allowed &= (s_cpu_retention.retent.non_critical_frame[core_id] != NULL); + } + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + allowed &= (s_cpu_retention.retent.clic_frame[core_id] != NULL); + } + return allowed; +} + +esp_err_t sleep_cpu_configure(bool light_sleep_enable) +{ +#if CONFIG_PM_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; +} + +#if !CONFIG_FREERTOS_UNICORE +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU +static IRAM_ATTR void smp_core_do_retention(void) +{ + uint8_t core_id = esp_cpu_get_core_id(); + + if (core_id == 0) { + WRITE_PERI_REG(HP_SYSTEM_CPU_INT_FROM_CPU_2_REG, 0); + } else { + WRITE_PERI_REG(HP_SYSTEM_CPU_INT_FROM_CPU_3_REG, 0); + } + + // Wait another core start to do retention + bool smp_skip_retention = false; + smp_retention_state_t another_core_state; + ESP_COMPILER_DIAGNOSTIC_PUSH_IGNORE("-Wanalyzer-infinite-loop") + while (1) { + another_core_state = atomic_load(&s_smp_retention_state[!core_id]); + if (another_core_state == SMP_SKIP_RETENTION) { + // If another core skips the retention, the current core should also have to skip it. + smp_skip_retention = true; + break; + } else if (another_core_state == SMP_BACKUP_START) { + break; + } + } + ESP_COMPILER_DIAGNOSTIC_POP("-Wanalyzer-infinite-loop") + + if (!smp_skip_retention) { + atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_START); + rv_core_noncritical_regs_save(); + cpu_domain_dev_regs_save(s_cpu_retention.retent.clic_frame[core_id]); + RvCoreCriticalSleepFrame *frame_critical = s_cpu_retention.retent.critical_frame[core_id]; + uint32_t mstatus = save_mstatus_and_disable_global_int(); + s_fpu_saved[core_id] = xPortFPUContextIsDirty(core_id); + if (s_fpu_saved[core_id]) { + rv_core_fpu_save(frame_critical); + } + rv_core_critical_regs_save(); + if ((frame_critical->pmufunc & 0x3) == 0x1) { + atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_DONE); + // wait another core trigger sleep and wakeup + while (1) { + // If another core's sleep request is rejected by the hardware, jumps out of blocking. + another_core_state = atomic_load(&s_smp_retention_state[!core_id]); + if (another_core_state == SMP_SKIP_RETENTION) { + break; + } + } + } else { + // Start core1 + if (core_id == 0) { + REG_SET_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_CPU_CLK_EN); + REG_CLR_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_GLOBAL_RST_EN); + } + atomic_store(&s_smp_retention_state[core_id], SMP_RESTORE_START); + if (s_fpu_saved[core_id]) { + rv_core_fpu_restore(frame_critical); + } + restore_mstatus(mstatus); + cpu_domain_dev_regs_restore(s_cpu_retention.retent.clic_frame[core_id]); + rv_core_noncritical_regs_restore(); + atomic_store(&s_smp_retention_state[core_id], SMP_RESTORE_DONE); + } + } + // wait another core out sleep + while (atomic_load(&s_smp_retention_state[!core_id]) != SMP_IDLE) { + ; + } + atomic_store(&s_smp_retention_state[core_id], SMP_IDLE); +} + + +IRAM_ATTR void esp_sleep_cpu_skip_retention(void) { + atomic_store(&s_smp_retention_state[esp_cpu_get_core_id()], SMP_SKIP_RETENTION); +} +#endif + +void sleep_smp_cpu_sleep_prepare(void) +{ +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU + while (atomic_load(&s_smp_retention_state[!esp_cpu_get_core_id()]) != SMP_IDLE) { + ; + } + esp_ipc_isr_call((esp_ipc_isr_func_t)smp_core_do_retention, NULL); +#else + esp_ipc_isr_stall_other_cpu(); +#endif +} + +void sleep_smp_cpu_wakeup_prepare(void) +{ +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU + uint8_t core_id = esp_cpu_get_core_id(); + if (atomic_load(&s_smp_retention_state[core_id]) == SMP_RESTORE_DONE) { + ESP_COMPILER_DIAGNOSTIC_PUSH_IGNORE("-Wanalyzer-infinite-loop") + while (atomic_load(&s_smp_retention_state[!core_id]) != SMP_RESTORE_DONE) { + ; + } + ESP_COMPILER_DIAGNOSTIC_POP("-Wanalyzer-infinite-loop") + } + atomic_store(&s_smp_retention_state[core_id], SMP_IDLE); +#else + esp_ipc_isr_release_other_cpu(); +#endif +} +#endif //!CONFIG_FREERTOS_UNICORE diff --git a/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_asm.S b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_asm.S new file mode 100644 index 00000000000..d40d61364de --- /dev/null +++ b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_asm.S @@ -0,0 +1,219 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/soc.h" +#include "rvsleep-frames.h" +#include "freertos/FreeRTOSConfig.h" +#include "sdkconfig.h" +#include "riscv/csr_clic.h" + + .section .data1,"aw" + .global rv_core_critical_regs_frame + .type rv_core_critical_regs_frame,@object + .align 4 +rv_core_critical_regs_frame: + .rept (portNUM_PROCESSORS) + .word 0 + .endr + +/* +-------------------------------------------------------------------------------- + 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 a0, rv_core_critical_regs_frame + csrr t1, mhartid + slli t1, t1, 2 + add a0, a0, t1 + lw t0, 0(a0) /* 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) + csrr t2, MINTTHRESH_CSR + sw t2, RV_SLP_CTX_MINTTHRESH(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 + sw t0, RV_SLP_CTX_T0(t3) + 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 + +/* +-------------------------------------------------------------------------------- + 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 + csrr t1, mhartid + slli t1, t1, 2 + add t0, t0, t1 + 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_MINTTHRESH(t0) + csrw MINTTHRESH_CSR, t2 + 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 diff --git a/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_dynamic.c b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_dynamic.c new file mode 100644 index 00000000000..87b4cfae52c --- /dev/null +++ b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_dynamic.c @@ -0,0 +1,147 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_check.h" +#include "esp_heap_caps.h" + +#include "soc/clic_reg.h" +#include "esp_sleep.h" + +#include "rvsleep-frames.h" +#include "sleep_cpu_retention.h" + + +extern RvCoreCriticalSleepFrame *rv_core_critical_regs_frame[portNUM_PROCESSORS]; + +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_clic_sleep_frame_alloc_and_init(uint8_t core_id) +{ + const static cpu_domain_dev_regs_region_t regions[portNUM_PROCESSORS][3] = { + [0 ... portNUM_PROCESSORS - 1] = { + { .start = CLIC_INT_CONFIG_REG, .end = CLIC_INT_CONFIG_REG + 4 }, + { .start = CLIC_INT_THRESH_REG, .end = CLIC_INT_THRESH_REG + 4 }, + { .start = CLIC_INT_CTRL_REG(0), .end = CLIC_INT_CTRL_REG(47) + 4 }, + } + }; + return cpu_domain_dev_sleep_frame_alloc_and_init(regions[core_id], sizeof(regions[core_id]) / sizeof(cpu_domain_dev_regs_region_t)); +} + +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE +esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr, smp_retention_state_t *s_smp_retention_state) +{ + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.critical_frame[core_id] == NULL) { + void *frame = heap_caps_calloc(1, RV_SLEEP_CTX_FRMSZ, MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL); + if (frame == NULL) { + goto err; + } + sleep_cpu_retention_ptr->retent.critical_frame[core_id] = (RvCoreCriticalSleepFrame *)frame; + rv_core_critical_regs_frame[core_id] = (RvCoreCriticalSleepFrame *)frame; + } + if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] == NULL) { + void *frame = heap_caps_calloc(1, sizeof(RvCoreNonCriticalSleepFrame), MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL); + if (frame == NULL) { + goto err; + } + sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = (RvCoreNonCriticalSleepFrame *)frame; + } + } + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.clic_frame[core_id] == NULL) { + void *frame = cpu_domain_clic_sleep_frame_alloc_and_init(core_id); + if (frame == NULL) { + goto err; + } + sleep_cpu_retention_ptr->retent.clic_frame[core_id] = (cpu_domain_dev_sleep_frame_t *)frame; + } + } + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + atomic_init(&s_smp_retention_state[core_id], SMP_IDLE); + } + return ESP_OK; +err: + esp_sleep_cpu_retention_deinit(); + return ESP_ERR_NO_MEM; +} +#else +esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr) +{ + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.critical_frame[core_id] == NULL) { + void *frame = heap_caps_calloc(1, RV_SLEEP_CTX_FRMSZ, MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL); + if (frame == NULL) { + goto err; + } + sleep_cpu_retention_ptr->retent.critical_frame[core_id] = (RvCoreCriticalSleepFrame *)frame; + rv_core_critical_regs_frame[core_id] = (RvCoreCriticalSleepFrame *)frame; + } + if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] == NULL) { + void *frame = heap_caps_calloc(1, sizeof(RvCoreNonCriticalSleepFrame), MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL); + if (frame == NULL) { + goto err; + } + sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = (RvCoreNonCriticalSleepFrame *)frame; + } + } + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.clic_frame[core_id] == NULL) { + void *frame = cpu_domain_clic_sleep_frame_alloc_and_init(core_id); + if (frame == NULL) { + goto err; + } + sleep_cpu_retention_ptr->retent.clic_frame[core_id] = (cpu_domain_dev_sleep_frame_t *)frame; + } + } + return ESP_OK; +err: + esp_sleep_cpu_retention_deinit(); + return ESP_ERR_NO_MEM; +} +#endif + +esp_err_t esp_sleep_cpu_retention_deinit_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr) +{ + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.critical_frame[core_id]) { + heap_caps_free((void *)sleep_cpu_retention_ptr->retent.critical_frame[core_id]); + sleep_cpu_retention_ptr->retent.critical_frame[core_id] = NULL; + rv_core_critical_regs_frame[core_id] = NULL; + } + if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id]) { + heap_caps_free((void *)sleep_cpu_retention_ptr->retent.non_critical_frame[core_id]); + sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = NULL; + } + } + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.clic_frame[core_id]) { + heap_caps_free((void *)sleep_cpu_retention_ptr->retent.clic_frame[core_id]); + sleep_cpu_retention_ptr->retent.clic_frame[core_id] = NULL; + } + } + return ESP_OK; +} diff --git a/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_retention.h b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_retention.h new file mode 100644 index 00000000000..55f340c2f9e --- /dev/null +++ b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_retention.h @@ -0,0 +1,57 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SLEEP_CPU_RETENTION_H__ +#define __SLEEP_CPU_RETENTION_H__ + +#include "rvsleep-frames.h" +#include "freertos/FreeRTOS.h" +#include "esp_err.h" + +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE +#include +#include "soc/hp_system_reg.h" +typedef enum { + SMP_IDLE, + SMP_BACKUP_START, + SMP_BACKUP_DONE, + SMP_RESTORE_START, + SMP_RESTORE_DONE, + SMP_SKIP_RETENTION, +} smp_retention_state_t; +#endif + +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[portNUM_PROCESSORS]; + RvCoreNonCriticalSleepFrame *non_critical_frame[portNUM_PROCESSORS]; + cpu_domain_dev_sleep_frame_t *clic_frame[portNUM_PROCESSORS]; + } retent; +} sleep_cpu_retention_t; + +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE + esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr, smp_retention_state_t *s_smp_retention_state); +#else + esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr); +#endif + +esp_err_t esp_sleep_cpu_retention_deinit_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr); + +#endif /* #ifndef __SLEEP_CPU_RETENTION_H__ */ diff --git a/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_static.c b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_static.c new file mode 100644 index 00000000000..317362fe6f9 --- /dev/null +++ b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_static.c @@ -0,0 +1,146 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_check.h" +#include "soc/clic_reg.h" + +#include "rvsleep-frames.h" +#include "sleep_cpu_retention.h" + + +extern RvCoreCriticalSleepFrame *rv_core_critical_regs_frame[portNUM_PROCESSORS]; + +#define R_CONCAT(s1, s2) _R_CONCAT(s1, s2) +#define _R_CONCAT(s1, s2) s1 ## s2 + +#define CPU_DOMAIN_DEV_SZ0 (CPU_DOMAIN_DEV_END_ADDR0 - CPU_DOMAIN_DEV_START_ADDR0) +#define CPU_DOMAIN_DEV_SZ1 (CPU_DOMAIN_DEV_END_ADDR1 - CPU_DOMAIN_DEV_START_ADDR1) +#define CPU_DOMAIN_DEV_SZ2 (CPU_DOMAIN_DEV_END_ADDR2 - CPU_DOMAIN_DEV_START_ADDR2) +#define CPU_DOMAIN_DEV_SZ3 (CPU_DOMAIN_DEV_END_ADDR3 - CPU_DOMAIN_DEV_START_ADDR3) +#define CPU_DOMAIN_DEV_SZ4 (CPU_DOMAIN_DEV_END_ADDR4 - CPU_DOMAIN_DEV_START_ADDR4) +#define CPU_DOMAIN_DEV_SZ5 (CPU_DOMAIN_DEV_END_ADDR5 - CPU_DOMAIN_DEV_START_ADDR5) +#define CPU_DOMAIN_DEV_SZ6 (CPU_DOMAIN_DEV_END_ADDR6 - CPU_DOMAIN_DEV_START_ADDR6) +#define CPU_DOMAIN_DEV_SZ7 (CPU_DOMAIN_DEV_END_ADDR7 - CPU_DOMAIN_DEV_START_ADDR7) + +#define CPU_DOMAIN_DEV_TOTAL_SZ(n) (R_CONCAT(__TOTAL_SZ, n)) + +#define __TOTAL_SZ0 (sizeof(cpu_domain_dev_sleep_frame_t)) +#define __TOTAL_SZ1 ((__TOTAL_SZ0) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ0) +#define __TOTAL_SZ2 ((__TOTAL_SZ1) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ1) +#define __TOTAL_SZ3 ((__TOTAL_SZ2) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ2) +#define __TOTAL_SZ4 ((__TOTAL_SZ3) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ3) +#define __TOTAL_SZ5 ((__TOTAL_SZ4) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ4) +#define __TOTAL_SZ6 ((__TOTAL_SZ5) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ5) +#define __TOTAL_SZ7 ((__TOTAL_SZ6) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ6) +#define __TOTAL_SZ8 ((__TOTAL_SZ7) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ7) + + +static void * cpu_domain_dev_sleep_frame_alloc_and_init(const cpu_domain_dev_regs_region_t *regions, const int region_num, void * frame) +{ + 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; + } + 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_clic_sleep_frame_alloc_and_init(uint8_t core_id) +{ + static DRAM_ATTR cpu_domain_dev_regs_region_t regions[portNUM_PROCESSORS][2] = { + [0 ... portNUM_PROCESSORS - 1] = { + { .start = CLIC_INT_CONFIG_REG, .end = CLIC_INT_CONFIG_REG + 4 }, + { .start = CLIC_INT_THRESH_REG, .end = CLIC_INT_THRESH_REG + 4 }, + { .start = CLIC_INT_CTRL_REG(0), .end = CLIC_INT_CTRL_REG(47) + 4 }, + } + }; + static DRAM_ATTR uint8_t sleep_frame[portNUM_PROCESSORS][CPU_DOMAIN_DEV_TOTAL_SZ(2)] __attribute__((aligned(4))); + return cpu_domain_dev_sleep_frame_alloc_and_init(regions[core_id], sizeof(regions[core_id]) / sizeof(regions[core_id][0]), sleep_frame[core_id]); +} + +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE +esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr, smp_retention_state_t *s_smp_retention_state) +{ + static DRAM_ATTR uint8_t rv_core_critical_regs[RV_SLEEP_CTX_FRMSZ * portNUM_PROCESSORS] __attribute__((aligned(4))); + static DRAM_ATTR uint8_t rv_core_non_critical_regs[sizeof(RvCoreNonCriticalSleepFrame)* portNUM_PROCESSORS] __attribute__((aligned(4))); + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.critical_frame[core_id] == NULL) { + void * regs = rv_core_critical_regs + core_id * RV_SLEEP_CTX_FRMSZ; + sleep_cpu_retention_ptr->retent.critical_frame[core_id] = (RvCoreCriticalSleepFrame *) regs; + rv_core_critical_regs_frame[core_id] = (RvCoreCriticalSleepFrame *) regs; + } + if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] == NULL) { + sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = (RvCoreNonCriticalSleepFrame *)(rv_core_non_critical_regs+core_id * sizeof(RvCoreNonCriticalSleepFrame)); + } + } + + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.clic_frame[core_id] == NULL) { + void *frame = cpu_domain_clic_sleep_frame_alloc_and_init(core_id); + sleep_cpu_retention_ptr->retent.clic_frame[core_id] = (cpu_domain_dev_sleep_frame_t *)frame; + } + } + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + atomic_init(&s_smp_retention_state[core_id], SMP_IDLE); + } + return ESP_OK; +} +#else +esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr) +{ + static DRAM_ATTR uint8_t rv_core_critical_regs[RV_SLEEP_CTX_FRMSZ * portNUM_PROCESSORS] __attribute__((aligned(4))); + static DRAM_ATTR uint8_t rv_core_non_critical_regs[sizeof(RvCoreNonCriticalSleepFrame)* portNUM_PROCESSORS] __attribute__((aligned(4))); + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.critical_frame[core_id] == NULL) { + void * regs = rv_core_critical_regs + core_id * RV_SLEEP_CTX_FRMSZ; + sleep_cpu_retention_ptr->retent.critical_frame[core_id] = (RvCoreCriticalSleepFrame *) regs; + rv_core_critical_regs_frame[core_id] = (RvCoreCriticalSleepFrame *) regs; + } + if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] == NULL) { + sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = (RvCoreNonCriticalSleepFrame *)(rv_core_non_critical_regs+core_id * sizeof(RvCoreNonCriticalSleepFrame)); + } + } + + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.clic_frame[core_id] == NULL) { + void *frame = cpu_domain_clic_sleep_frame_alloc_and_init(core_id); + sleep_cpu_retention_ptr->retent.clic_frame[core_id] = (cpu_domain_dev_sleep_frame_t *)frame; + } + } + return ESP_OK; +} +#endif + +esp_err_t esp_sleep_cpu_retention_deinit_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr) +{ + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.critical_frame[core_id]) { + sleep_cpu_retention_ptr->retent.critical_frame[core_id] = NULL; + rv_core_critical_regs_frame[core_id] = NULL; + } + if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id]) { + sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = NULL; + } + } + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.clic_frame[core_id]) { + sleep_cpu_retention_ptr->retent.clic_frame[core_id] = NULL; + } + } + return ESP_OK; +} diff --git a/components/esp_hw_support/lowpower/port/esp32s31/sleep_fpu_asm.S b/components/esp_hw_support/lowpower/port/esp32s31/sleep_fpu_asm.S new file mode 100644 index 00000000000..30b632672ee --- /dev/null +++ b/components/esp_hw_support/lowpower/port/esp32s31/sleep_fpu_asm.S @@ -0,0 +1,118 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "rvsleep-frames.h" +#include "freertos/FreeRTOSConfig.h" +#include "sdkconfig.h" + + .section .iram1,"ax" + .global rv_core_fpu_save + .type rv_core_fpu_save,@function + .align 4 + +/* Bit to set in mstatus to enable the FPU */ +#define CSR_MSTATUS_FPU_ENABLE (1 << 13) +/* Bit to clear in mstatus to disable the FPU */ +#define CSR_MSTATUS_FPU_DISABLE (3 << 13) + +.macro fpu_enable reg + li \reg, CSR_MSTATUS_FPU_ENABLE + csrs mstatus, \reg +.endm + +rv_core_fpu_save: + fpu_enable t0 + mv t0, a0 /* t0 = frame (arg0) */ + fsw ft0, RV_SLP_CTX_FPU_FT0(t0) + fsw ft1, RV_SLP_CTX_FPU_FT1(t0) + fsw ft2, RV_SLP_CTX_FPU_FT2(t0) + fsw ft3, RV_SLP_CTX_FPU_FT3(t0) + fsw ft4, RV_SLP_CTX_FPU_FT4(t0) + fsw ft5, RV_SLP_CTX_FPU_FT5(t0) + fsw ft6, RV_SLP_CTX_FPU_FT6(t0) + fsw ft7, RV_SLP_CTX_FPU_FT7(t0) + fsw fs0, RV_SLP_CTX_FPU_FS0(t0) + fsw fs1, RV_SLP_CTX_FPU_FS1(t0) + fsw fa0, RV_SLP_CTX_FPU_FA0(t0) + fsw fa1, RV_SLP_CTX_FPU_FA1(t0) + fsw fa2, RV_SLP_CTX_FPU_FA2(t0) + fsw fa3, RV_SLP_CTX_FPU_FA3(t0) + fsw fa4, RV_SLP_CTX_FPU_FA4(t0) + fsw fa5, RV_SLP_CTX_FPU_FA5(t0) + fsw fa6, RV_SLP_CTX_FPU_FA6(t0) + fsw fa7, RV_SLP_CTX_FPU_FA7(t0) + fsw fs2, RV_SLP_CTX_FPU_FS2(t0) + fsw fs3, RV_SLP_CTX_FPU_FS3(t0) + fsw fs4, RV_SLP_CTX_FPU_FS4(t0) + fsw fs5, RV_SLP_CTX_FPU_FS5(t0) + fsw fs6, RV_SLP_CTX_FPU_FS6(t0) + fsw fs7, RV_SLP_CTX_FPU_FS7(t0) + fsw fs8, RV_SLP_CTX_FPU_FS8(t0) + fsw fs9, RV_SLP_CTX_FPU_FS9(t0) + fsw fs10, RV_SLP_CTX_FPU_FS10(t0) + fsw fs11, RV_SLP_CTX_FPU_FS11(t0) + fsw ft8, RV_SLP_CTX_FPU_FT8(t0) + fsw ft9, RV_SLP_CTX_FPU_FT9(t0) + fsw ft10, RV_SLP_CTX_FPU_FT10(t0) + fsw ft11, RV_SLP_CTX_FPU_FT11(t0) + csrr t1, fcsr + sw t1, RV_SLP_CTX_FPU_FCSR(t0) + /* Chip will go to sleep and FPU will be powered down, not necessary to disable FPU here. */ + ret + + .size rv_core_fpu_save, . - rv_core_fpu_save + +/* +-------------------------------------------------------------------------------- + FPU restore: a0 = RvCoreCriticalSleepFrame *. Restore all FP registers from the frame. +-------------------------------------------------------------------------------- +*/ + .section .iram1,"ax" + .global rv_core_fpu_restore + .type rv_core_fpu_restore,@function + .align 4 + +rv_core_fpu_restore: + fpu_enable t0 + mv t0, a0 /* t0 = frame (arg0) */ + flw ft0, RV_SLP_CTX_FPU_FT0(t0) + flw ft1, RV_SLP_CTX_FPU_FT1(t0) + flw ft2, RV_SLP_CTX_FPU_FT2(t0) + flw ft3, RV_SLP_CTX_FPU_FT3(t0) + flw ft4, RV_SLP_CTX_FPU_FT4(t0) + flw ft5, RV_SLP_CTX_FPU_FT5(t0) + flw ft6, RV_SLP_CTX_FPU_FT6(t0) + flw ft7, RV_SLP_CTX_FPU_FT7(t0) + flw fs0, RV_SLP_CTX_FPU_FS0(t0) + flw fs1, RV_SLP_CTX_FPU_FS1(t0) + flw fa0, RV_SLP_CTX_FPU_FA0(t0) + flw fa1, RV_SLP_CTX_FPU_FA1(t0) + flw fa2, RV_SLP_CTX_FPU_FA2(t0) + flw fa3, RV_SLP_CTX_FPU_FA3(t0) + flw fa4, RV_SLP_CTX_FPU_FA4(t0) + flw fa5, RV_SLP_CTX_FPU_FA5(t0) + flw fa6, RV_SLP_CTX_FPU_FA6(t0) + flw fa7, RV_SLP_CTX_FPU_FA7(t0) + flw fs2, RV_SLP_CTX_FPU_FS2(t0) + flw fs3, RV_SLP_CTX_FPU_FS3(t0) + flw fs4, RV_SLP_CTX_FPU_FS4(t0) + flw fs5, RV_SLP_CTX_FPU_FS5(t0) + flw fs6, RV_SLP_CTX_FPU_FS6(t0) + flw fs7, RV_SLP_CTX_FPU_FS7(t0) + flw fs8, RV_SLP_CTX_FPU_FS8(t0) + flw fs9, RV_SLP_CTX_FPU_FS9(t0) + flw fs10, RV_SLP_CTX_FPU_FS10(t0) + flw fs11, RV_SLP_CTX_FPU_FS11(t0) + flw ft8, RV_SLP_CTX_FPU_FT8(t0) + flw ft9, RV_SLP_CTX_FPU_FT9(t0) + flw ft10, RV_SLP_CTX_FPU_FT10(t0) + flw ft11, RV_SLP_CTX_FPU_FT11(t0) + lw t1, RV_SLP_CTX_FPU_FCSR(t0) + csrw fcsr, t1 + /* Caller restores mstatus (and thus FPU state); not necessary to disable FPU here. */ + ret + + .size rv_core_fpu_restore, . - rv_core_fpu_restore diff --git a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in index d876a82bcd8..539f4691de8 100644 --- a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in @@ -1083,6 +1083,10 @@ config SOC_PM_SUPPORT_TOUCH_SENSOR_WAKEUP bool default y +config SOC_PM_SUPPORT_CPU_PD + bool + default y + config SOC_PM_SUPPORT_XTAL32K_PD bool default y @@ -1131,6 +1135,10 @@ config SOC_PM_CPU_RETENTION_BY_SW bool default y +config SOC_PM_FPU_RETENTION_BY_SW + bool + default y + config SOC_PM_CACHE_RETENTION_BY_PAU bool default y diff --git a/components/soc/esp32s31/include/soc/soc_caps.h b/components/soc/esp32s31/include/soc/soc_caps.h index 1b70b3d4327..6ea35c1ab58 100644 --- a/components/soc/esp32s31/include/soc/soc_caps.h +++ b/components/soc/esp32s31/include/soc/soc_caps.h @@ -441,7 +441,7 @@ #define SOC_PM_SUPPORT_WIFI_WAKEUP (1) #define SOC_PM_SUPPORT_BEACON_WAKEUP (1) #define SOC_PM_SUPPORT_TOUCH_SENSOR_WAKEUP (1) /*!