diff --git a/components/esp_hw_support/port/esp32c3/rtc_sleep.c b/components/esp_hw_support/port/esp32c3/rtc_sleep.c index 57dea490ea..c03b98295f 100644 --- a/components/esp_hw_support/port/esp32c3/rtc_sleep.c +++ b/components/esp_hw_support/port/esp32c3/rtc_sleep.c @@ -24,8 +24,10 @@ #include "soc/nrx_reg.h" #include "soc/fe_reg.h" #include "soc/timer_group_reg.h" +#include "soc/system_reg.h" #include "soc/rtc.h" #include "esp32c3/rom/ets_sys.h" +#include "esp32c3/rom/rtc.h" #include "regi2c_ctrl.h" /** @@ -140,6 +142,8 @@ void rtc_sleep_set_wakeup_time(uint64_t t) WRITE_PERI_REG(RTC_CNTL_SLP_TIMER1_REG, t >> 32); } +static uint32_t rtc_sleep_finish(uint32_t lslp_mem_inf_fpu); + uint32_t rtc_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp_mem_inf_fpu) { REG_SET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_ENA, wakeup_opt); @@ -152,6 +156,88 @@ uint32_t rtc_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp RTC_CNTL_SLP_REJECT_INT_RAW | RTC_CNTL_SLP_WAKEUP_INT_RAW) == 0) { ; } + + return rtc_sleep_finish(lslp_mem_inf_fpu); +} + +#define STR2(X) #X +#define STR(X) STR2(X) + +uint32_t rtc_deep_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt) +{ + REG_SET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_ENA, wakeup_opt); + WRITE_PERI_REG(RTC_CNTL_SLP_REJECT_CONF_REG, reject_opt); + + /* Calculate RTC Fast Memory CRC (for wake stub) & go to deep sleep + + Because we may be running from RTC memory as stack, we can't easily call any + functions to do this (as registers will spill to stack, corrupting the CRC). + + Instead, load all the values we need into registers then use register ops only to calculate + the CRC value, write it to the RTC CRC value register, and immediately go into deep sleep. + */ + + /* Values used to set the SYSTEM_RTC_FASTMEM_CONFIG_REG value */ + const unsigned CRC_START_ADDR = 0; + const unsigned CRC_LEN = 0x7ff; + + asm volatile( + /* Start CRC calculation */ + "sw %1, 0(%0)\n" // set RTC_MEM_CRC_ADDR & RTC_MEM_CRC_LEN + "or t0, %1, %2\n" + "sw t0, 0(%0)\n" // set RTC_MEM_CRC_START + + /* Wait for the CRC calculation to finish */ + ".Lwaitcrc:\n" + "fence\n" + "lw t0, 0(%0)\n" + "li t1, "STR(SYSTEM_RTC_MEM_CRC_FINISH)"\n" + "and t0, t0, t1\n" + "beqz t0, .Lwaitcrc\n" + "not %2, %2\n" // %2 -> ~DPORT_RTC_MEM_CRC_START + "and t0, t0, %2\n" + "sw t0, 0(%0)\n" // clear RTC_MEM_CRC_START + "fence\n" + "not %2, %2\n" // %2 -> DPORT_RTC_MEM_CRC_START, probably unnecessary but gcc assumes inputs unchanged + + /* Store the calculated value in RTC_MEM_CRC_REG */ + "lw t0, 0(%3)\n" + "sw t0, 0(%4)\n" + "fence\n" + + /* Set register bit to go into deep sleep */ + "lw t0, 0(%5)\n" + "or t0, t0, %6\n" + "sw t0, 0(%5)\n" + "fence\n" + + /* Wait for sleep reject interrupt (never finishes if successful) */ + ".Lwaitsleep:" + "fence\n" + "lw t0, 0(%7)\n" + "and t0, t0, %8\n" + "beqz t0, .Lwaitsleep\n" + + : + : + "r" (SYSTEM_RTC_FASTMEM_CONFIG_REG), // %0 + "r" ( (CRC_START_ADDR << SYSTEM_RTC_MEM_CRC_START_S) + | (CRC_LEN << SYSTEM_RTC_MEM_CRC_LEN_S)), // %1 + "r" (SYSTEM_RTC_MEM_CRC_START), // %2 + "r" (SYSTEM_RTC_FASTMEM_CRC_REG), // %3 + "r" (RTC_MEMORY_CRC_REG), // %4 + "r" (RTC_CNTL_STATE0_REG), // %5 + "r" (RTC_CNTL_SLEEP_EN), // %6 + "r" (RTC_CNTL_INT_RAW_REG), // %7 + "r" (RTC_CNTL_SLP_REJECT_INT_RAW | RTC_CNTL_SLP_WAKEUP_INT_RAW) // %8 + : "t0", "t1" // working registers + ); + + return rtc_sleep_finish(0); +} + +static uint32_t rtc_sleep_finish(uint32_t lslp_mem_inf_fpu) +{ /* In deep sleep mode, we never get here */ uint32_t reject = REG_GET_FIELD(RTC_CNTL_INT_RAW_REG, RTC_CNTL_SLP_REJECT_INT_RAW); SET_PERI_REG_MASK(RTC_CNTL_INT_CLR_REG, @@ -164,8 +250,3 @@ uint32_t rtc_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp } return reject; } - -uint32_t rtc_deep_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt) -{ - abort(); // ESP32-C3 TODO IDF-2560 -}