/**************************************************************************** * arch/risc-v/src/esp32c3/esp32c3_pm.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #ifdef CONFIG_PM #include #include #include #include #include #include #include #include "chip.h" #include "riscv_arch.h" #include "hardware/esp32c3_rtccntl.h" #include "hardware/esp32c3_system.h" #include "hardware/esp32c3_syscon.h" #include "hardware/esp32c3_soc.h" #include "hardware/esp32c3_uart.h" #include "hardware/esp32c3_gpio.h" #include "hardware/apb_ctrl_reg.h" #include "esp32c3_attr.h" #include "esp32c3_rtc.h" #include "esp32c3_clockconfig.h" #include "esp32c3_pm.h" #ifdef CONFIG_ESP32C3_RT_TIMER #include "esp32c3_rt_timer.h" #endif #ifdef CONFIG_SCHED_TICKLESS #include "esp32c3_tickless.h" #endif /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* If light sleep time is less than that, don't power down flash */ #define FLASH_PD_MIN_SLEEP_TIME_US 2000 /* Minimal amount of time we can sleep for. */ #define LIGHT_SLEEP_MIN_TIME_US 200 #define RTC_MODULE_SLEEP_PREPARE_CYCLES (6) #ifndef MAX #define MAX(a,b) a > b ? a : b #endif /* Time from VDD_SDIO power up to first flash read in ROM code */ #define VDD_SDIO_POWERUP_TO_FLASH_READ_US (700) /* Extra time it takes to enter and exit light sleep and deep sleep */ #define LIGHT_SLEEP_TIME_OVERHEAD_US (37) #ifdef CONFIG_ESP32C3_CPU_FREQ_MHZ #define DEFAULT_CPU_FREQ_MHZ CONFIG_ESP32C3_CPU_FREQ_MHZ #else #define DEFAULT_CPU_FREQ_MHZ (160) #endif #define DEFAULT_SLEEP_OUT_OVERHEAD_US (105) #define DEEP_SLEEP_TIME_OVERHEAD_US (250 + 100 * 240 / DEFAULT_CPU_FREQ_MHZ) #define DEEP_SLEEP_WAKEUP_DELAY 0 #define RTC_VDDSDIO_TIEH_1_8V 0 /* TIEH field value for 1.8V VDDSDIO */ #define RTC_VDDSDIO_TIEH_3_3V 1 /* TIEH field value for 3.3V VDDSDIO */ #define RTC_GPIO_TRIG_EN BIT(2) /* GPIO wakeup */ #define RTC_TIMER_TRIG_EN BIT(3) /* Timer wakeup */ #define RTC_WIFI_TRIG_EN BIT(5) /* Wi-Fi wakeup (light sleep only) */ #define RTC_UART0_TRIG_EN BIT(6) /* UART0 wakeup (light sleep only) */ #define RTC_UART1_TRIG_EN BIT(7) /* UART1 wakeup (light sleep only) */ #define RTC_BT_TRIG_EN BIT(10) /* BT wakeup (light sleep only) */ #define RTC_XTAL32K_DEAD_TRIG_EN BIT(12) #define RTC_USB_TRIG_EN BIT(14) #define RTC_BROWNOUT_DET_TRIG_EN BIT(16) #define PERIPH_INFORM_OUT_SLEEP_OVERHEAD_NO (1) #define PERIPH_SKIP_SLEEP_NO (1) /**************************************************************************** * Private Types ****************************************************************************/ /* Power down options */ enum esp32c3_sleep_pd_option_e { /* Power down the power domain in sleep mode */ ESP_PD_OPTION_OFF, /* Keep power domain enabled during sleep mode */ ESP_PD_OPTION_ON, /* Keep power domain enabled in sleep mode if it is needed * by one of the wakeup options, otherwise power it down. */ ESP_PD_OPTION_AUTO }; /* Power domains which can be powered down in sleep mode. */ enum esp32c3_sleep_pd_domain_e { ESP_PD_DOMAIN_RTC_PERIPH = 0, /* RTC IO, sensors */ ESP_PD_DOMAIN_RTC_SLOW_MEM, /* RTC slow memory */ ESP_PD_DOMAIN_RTC_FAST_MEM, /* RTC fast memory */ ESP_PD_DOMAIN_XTAL, /* XTAL oscillator */ ESP_PD_DOMAIN_CPU, /* CPU core */ ESP_PD_DOMAIN_MAX /* Number of domains */ }; /* Internal structure which holds all requested deep sleep parameters. */ struct esp32c3_sleep_config_s { enum esp32c3_sleep_pd_option_e pd_options[ESP_PD_DOMAIN_MAX]; uint64_t sleep_duration; uint32_t wakeup_triggers : 15; uint32_t ext1_trigger_mode : 1; uint32_t ext1_rtc_gpio_mask : 18; uint32_t ext0_trigger_level : 1; uint32_t ext0_rtc_gpio_num : 5; uint32_t gpio_wakeup_mask : 6; uint32_t gpio_trigger_mode : 6; uint32_t sleep_time_adjustment; uint32_t ccount_ticks_record; uint32_t sleep_time_overhead_out; uint32_t rtc_clk_cal_period; uint64_t rtc_ticks_at_sleep_start; void *cpu_pd_mem; }; /* Structure describing vddsdio configuration. */ struct esp32c3_rtc_vddsdio_config_s { uint32_t force : 1; /* If 1, use configuration from RTC registers; * if 0, use EFUSE/bootstrapping pins. */ uint32_t enable : 1; /* Enable VDDSDIO regulator */ uint32_t tieh : 1; /* Select VDDSDIO voltage. One of * RTC_VDDSDIO_TIEH_1_8V, RTC_VDDSDIO_TIEH_3_3V */ uint32_t drefh : 2; /* Tuning parameter for VDDSDIO regulator */ uint32_t drefm : 2; /* Tuning parameter for VDDSDIO regulator */ uint32_t drefl : 2; /* Tuning parameter for VDDSDIO regulator */ }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static inline void IRAM_ATTR esp32c3_uart_tx_wait_idle(uint8_t uart_no); static void IRAM_ATTR esp32c3_flush_uarts(void); static void IRAM_ATTR esp32c3_suspend_uarts(void); static void IRAM_ATTR esp32c3_resume_uarts(void); static void IRAM_ATTR esp32c3_timer_wakeup_prepare(void); static uint32_t IRAM_ATTR esp32c3_get_power_down_flags(void); static void IRAM_ATTR esp32c3_set_vddsdio_config( struct esp32c3_rtc_vddsdio_config_s config); static int IRAM_ATTR esp32c3_get_vddsdio_config( struct esp32c3_rtc_vddsdio_config_s *config); static int IRAM_ATTR esp32c3_light_sleep_inner(uint32_t pd_flags, uint32_t time_us, struct esp32c3_rtc_vddsdio_config_s config); static int IRAM_ATTR esp32c3_sleep_start(uint32_t pd_flags); /**************************************************************************** * Private Data ****************************************************************************/ static struct esp32c3_sleep_config_s s_config = { .pd_options = { ESP_PD_OPTION_AUTO, ESP_PD_OPTION_AUTO, ESP_PD_OPTION_AUTO, ESP_PD_OPTION_AUTO, ESP_PD_OPTION_AUTO }, .ccount_ticks_record = 0, .sleep_time_overhead_out = DEFAULT_SLEEP_OUT_OVERHEAD_US, .wakeup_triggers = 0 }; static _Atomic uint32_t pm_wakelock = 0; /* Inform peripherals of light sleep wakeup overhead time */ inform_out_sleep_overhead_cb_t g_periph_inform_out_sleep_overhead_cb[PERIPH_INFORM_OUT_SLEEP_OVERHEAD_NO]; /* Indicates if light sleep shoule be skipped by peripherals. */ skip_light_sleep_cb_t g_periph_skip_sleep_cb[PERIPH_SKIP_SLEEP_NO]; /**************************************************************************** * Private Functions ****************************************************************************/ /* Set the real CPU ticks per us to the ets, * so that ets_delay_us will be accurate. */ extern void ets_update_cpu_frequency(uint32_t ticks_per_us); /* Pauses execution for us microseconds. */ extern void esp_rom_delay_us(uint32_t us); /**************************************************************************** * Name: esp32c3_uart_tx_wait_idle * * Description: * Wait until uart tx full empty and the last char send ok. * * Input Parameters: * uart_no - 0 for UART0, 1 for UART1, 2 for UART2 * * Returned Value: * None * ****************************************************************************/ static inline void IRAM_ATTR esp32c3_uart_tx_wait_idle(uint8_t uart_no) { uint32_t status; do { status = getreg32(UART_STATUS_REG(uart_no)); } while ((status & (UART_ST_UTX_OUT_M | UART_TXFIFO_CNT_M)) != 0); } /**************************************************************************** * Name: esp32c3_flush_uarts * * Description: * Wait until UART0/UART1 tx full empty and the last char send ok * * Input Parameters: * None * * Returned Value: * None * ****************************************************************************/ static void IRAM_ATTR esp32c3_flush_uarts(void) { int i; for (i = 0; i < ESP32C3_NUARTS; ++i) { if (esp32c3_periph_ll_periph_enabled(PERIPH_UART0_MODULE + i)) { esp32c3_uart_tx_wait_idle(i); } } } /**************************************************************************** * Name: esp32c3_suspend_uarts * * Description: * Suspend UART0/UART1 output * * Input Parameters: * None * * Returned Value: * None * ****************************************************************************/ static void IRAM_ATTR esp32c3_suspend_uarts(void) { int i; uint32_t uart_fsm = 0; for (i = 0; i < ESP32C3_NUARTS; ++i) { if (!esp32c3_periph_ll_periph_enabled(PERIPH_UART0_MODULE + i)) { continue; } modifyreg32(UART_FLOW_CONF_REG(i), 0, UART_FORCE_XOFF); do { uart_fsm = REG_GET_FIELD(UART_STATUS_REG(i), UART_ST_UTX_OUT); } while (!(uart_fsm == UART_FSM_IDLE || uart_fsm == UART_FSM_TX_WAIT_SEND)); } } /**************************************************************************** * Name: esp32c3_resume_uarts * * Description: * Re-enable UART0/UART1/UART2 output * * Input Parameters: * None * * Returned Value: * None * ****************************************************************************/ static void IRAM_ATTR esp32c3_resume_uarts(void) { int i; for (i = 0; i < ESP32C3_NUARTS; ++i) { if (!esp32c3_periph_ll_periph_enabled(PERIPH_UART0_MODULE + i)) { continue; } REG_CLR_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XOFF); REG_SET_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XON); REG_CLR_BIT(UART_FLOW_CONF_REG(i), UART_SW_FLOW_CON_EN | UART_FORCE_XON); REG_SET_BIT(UART_ID_REG(i), UART_UPDATE); } } /**************************************************************************** * Name: esp32c3_get_power_down_flags * * Description: * Get power domains that can be powered down * * Input Parameters: * None * * Returned Value: * Power domains * ****************************************************************************/ static uint32_t IRAM_ATTR esp32c3_get_power_down_flags(void) { uint32_t pd_flags = 0; s_config.pd_options[ESP_PD_DOMAIN_RTC_FAST_MEM] = ESP_PD_OPTION_ON; if (s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] == ESP_PD_OPTION_AUTO) { if (s_config.wakeup_triggers & RTC_GPIO_TRIG_EN) { s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] = ESP_PD_OPTION_ON; } else { s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] = ESP_PD_OPTION_OFF; } } if (s_config.cpu_pd_mem == NULL) { s_config.pd_options[ESP_PD_DOMAIN_CPU] = ESP_PD_OPTION_ON; } if (s_config.pd_options[ESP_PD_DOMAIN_XTAL] == ESP_PD_OPTION_AUTO) { s_config.pd_options[ESP_PD_DOMAIN_XTAL] = ESP_PD_OPTION_OFF; } /* Prepare flags based on the selected options */ if (s_config.pd_options[ESP_PD_DOMAIN_RTC_FAST_MEM] != ESP_PD_OPTION_ON) { pd_flags |= RTC_SLEEP_PD_RTC_FAST_MEM; } if (s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] != ESP_PD_OPTION_ON) { pd_flags |= RTC_SLEEP_PD_RTC_PERIPH; } if (s_config.pd_options[ESP_PD_DOMAIN_CPU] != ESP_PD_OPTION_ON) { pd_flags |= RTC_SLEEP_PD_CPU; } return pd_flags; } /**************************************************************************** * Name: esp32c3_timer_wakeup_prepare * * Description: * Configure timer to wake-up * * Input Parameters: * None * * Returned Value: * None * ****************************************************************************/ static void IRAM_ATTR esp32c3_timer_wakeup_prepare(void) { int64_t ticks; int64_t sleep_duration = (int64_t)s_config.sleep_duration - (int64_t) s_config.sleep_time_adjustment; if (sleep_duration < 0) { sleep_duration = 0; } ticks = esp32c3_rtc_time_us_to_slowclk(sleep_duration, s_config.rtc_clk_cal_period); esp32c3_rtc_sleep_set_wakeup_time(s_config.rtc_ticks_at_sleep_start + ticks); } /**************************************************************************** * Name: esp32c3_set_vddsdio_config * * Description: * Set new VDDSDIO configuration using RTC registers. * * Input Parameters: * New VDDSDIO configuration * * Returned Value: * None * ****************************************************************************/ static void IRAM_ATTR esp32c3_set_vddsdio_config( struct esp32c3_rtc_vddsdio_config_s config) { uint32_t val = 0; val |= (config.force << RTC_CNTL_SDIO_FORCE_S); val |= (config.enable << RTC_CNTL_XPD_SDIO_REG_S); val |= (config.drefh << RTC_CNTL_DREFH_SDIO_S); val |= (config.drefm << RTC_CNTL_DREFM_SDIO_S); val |= (config.drefl << RTC_CNTL_DREFL_SDIO_S); val |= (config.tieh << RTC_CNTL_SDIO_TIEH_S); val |= RTC_CNTL_SDIO_PD_EN; putreg32((uint32_t)val, RTC_CNTL_SDIO_CONF_REG); } /**************************************************************************** * Name: esp32c3_get_vddsdio_config * * Description: * Get current VDDSDIO configuration. * * Input Parameters: * Incoming parameter address of VDDSDIO configuration to be saved * * Returned Value: * Zero (OK) is returned on success. * ****************************************************************************/ static int IRAM_ATTR esp32c3_get_vddsdio_config( struct esp32c3_rtc_vddsdio_config_s *config) { struct esp32c3_rtc_vddsdio_config_s *result = config; uint32_t strap_reg; uint32_t sdio_conf_reg = getreg32(RTC_CNTL_SDIO_CONF_REG); result->drefh = (sdio_conf_reg & RTC_CNTL_DREFH_SDIO_M) >> RTC_CNTL_DREFH_SDIO_S; result->drefm = (sdio_conf_reg & RTC_CNTL_DREFM_SDIO_M) >> RTC_CNTL_DREFM_SDIO_S; result->drefl = (sdio_conf_reg & RTC_CNTL_DREFL_SDIO_M) >> RTC_CNTL_DREFL_SDIO_S; if (sdio_conf_reg & RTC_CNTL_SDIO_FORCE) { /* Get configuration from RTC */ result->force = 1; result->enable = (sdio_conf_reg & RTC_CNTL_XPD_SDIO_REG_M) >> RTC_CNTL_XPD_SDIO_REG_S; result->tieh = (sdio_conf_reg & RTC_CNTL_SDIO_TIEH_M) >> RTC_CNTL_SDIO_TIEH_S; return OK; } /* Otherwise, VDD_SDIO is controlled by bootstrapping pin */ strap_reg = getreg32(GPIO_STRAP_REG); result->force = 0; result->tieh = (strap_reg & BIT(5)) ? RTC_VDDSDIO_TIEH_1_8V : RTC_VDDSDIO_TIEH_3_3V; result->enable = 1; return OK; } /**************************************************************************** * Name: esp32c3_sleep_start * * Description: * Enter low power mode. * * Input Parameters: * Power domains * * Returned Value: * 0 is returned on success or a negated errno value is returned * ****************************************************************************/ static int IRAM_ATTR esp32c3_sleep_start(uint32_t pd_flags) { int result; struct esp32c3_cpu_freq_config_s cpu_freq_config; bool deep_sleep = pd_flags & RTC_SLEEP_PD_DIG; /* Stop UART output so that output is not lost due to APB frequency change. * For light sleep, suspend UART output — it will resume after wakeup. * For deep sleep, wait for the contents of UART FIFO to be sent. */ if (deep_sleep) { esp32c3_flush_uarts(); } else { esp32c3_suspend_uarts(); } /* Save current frequency and switch to XTAL */ esp32c3_rtc_clk_cpu_freq_get_config(&cpu_freq_config); esp32c3_rtc_cpu_freq_set_xtal(); /* Enter sleep */ esp32c3_rtc_sleep_init(pd_flags); esp32c3_rtc_sleep_low_init(s_config.rtc_clk_cal_period); /* Set state machine time for light sleep */ if (deep_sleep == false) { esp32c3_rtc_sleep_low_init(s_config.rtc_clk_cal_period); } /* Configure timer wakeup */ if ((s_config.wakeup_triggers & RTC_TIMER_TRIG_EN) && s_config.sleep_duration > 0) { esp32c3_timer_wakeup_prepare(); } if (deep_sleep) { /* Otherwise, need to call the dedicated soc function for this */ result = esp32c3_rtc_deep_sleep_start(s_config.wakeup_triggers, 0); } else { result = esp32c3_rtc_sleep_start(s_config.wakeup_triggers, 0, 1); } /* Restore CPU frequency */ esp32c3_rtc_clk_cpu_freq_set_config(&cpu_freq_config); if (!deep_sleep) { s_config.ccount_ticks_record = esp32c3_cpu_cycle_count(); } REG_CLR_BIT(RTC_CNTL_RETENTION_CTRL_REG, RTC_CNTL_RETENTION_EN); /* Re-enable UART output */ esp32c3_resume_uarts(); return result; } /**************************************************************************** * Name: esp32c3_light_sleep_inner * * Description: * Enter low power mode, then wait for flash to be ready on wakeup * * Input Parameters: * pd_flags - Power domains * time_us - Time to wait for spi_flash become ready * config - VDDSDIO configuration * * Returned Value: * 0 is returned on success or a negated errno value is returned * ****************************************************************************/ static int IRAM_ATTR esp32c3_light_sleep_inner(uint32_t pd_flags, uint32_t time_us, struct esp32c3_rtc_vddsdio_config_s config) { /* Enter sleep */ int err = esp32c3_sleep_start(pd_flags); /* If VDDSDIO regulator was controlled by RTC registers before sleep. * restore the configuration. */ if (config.force) { esp32c3_set_vddsdio_config(config); } /* If SPI flash was powered down, wait for it to become ready. */ if (pd_flags & RTC_SLEEP_PD_VDDSDIO) { /* Wait for the flash chip to start up. */ esp_rom_delay_us(time_us); } return err; } /**************************************************************************** * Name: esp32c3_periph_should_skip_sleep * * Description: * Indicates if light sleep shoule be skipped by peripherals * * Input Parameters: * None * * Returned Value: * True is returned on success. Otherwise false. * ****************************************************************************/ static inline bool IRAM_ATTR esp32c3_periph_should_skip_sleep(void) { for (int i = 0; i < PERIPH_SKIP_SLEEP_NO; i++) { if (g_periph_skip_sleep_cb[i]) { if (g_periph_skip_sleep_cb[i]() == true) { return true; } } } return false; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: esp32c3_pm_register_skip_sleep_callback * * Description: * Unregister callback function of skipping light sleep. * * Input Parameters: * None * * Returned Value: * Zero (OK) is returned on success. Otherwise -1 (ERROR). * ****************************************************************************/ int esp32c3_pm_register_skip_sleep_callback(skip_light_sleep_cb_t cb) { for (int i = 0; i < PERIPH_SKIP_SLEEP_NO; i++) { if (g_periph_skip_sleep_cb[i] == cb) { return OK; } else if (g_periph_skip_sleep_cb[i] == NULL) { g_periph_skip_sleep_cb[i] = cb; return OK; } } return ERROR; } /**************************************************************************** * Name: esp32c3_pm_unregister_skip_sleep_callback * * Description: * Register callback function of skipping light sleep. * * Input Parameters: * None * * Returned Value: * Zero (OK) is returned on success. Otherwise -1 (ERROR). * ****************************************************************************/ int esp32c3_pm_unregister_skip_sleep_callback(skip_light_sleep_cb_t cb) { for (int i = 0; i < PERIPH_SKIP_SLEEP_NO; i++) { if (g_periph_skip_sleep_cb[i] == cb) { g_periph_skip_sleep_cb[i] = NULL; return OK; } } return ERROR; } /**************************************************************************** * Name: esp32c3_should_skip_light_sleep * * Description: * Indicates if light sleep shoule be skipped. * * Input Parameters: * None * * Returned Value: * True is returned on success. Otherwise false. * ****************************************************************************/ bool IRAM_ATTR esp32c3_should_skip_light_sleep(void) { if (esp32c3_periph_should_skip_sleep() == true) { return true; } return false; } /**************************************************************************** * Name: esp32c3_pm_register_inform_out_sleep_overhead_callback * * Description: * Register informing peripherals of light sleep wakeup overhead time * callback function. * * Input Parameters: * cb - callback function * * Returned Value: * Zero (OK) is returned on success. Otherwise -1 (ERROR). * ****************************************************************************/ int esp32c3_pm_register_inform_out_sleep_overhead_callback( inform_out_sleep_overhead_cb_t cb) { for (int i = 0; i < PERIPH_INFORM_OUT_SLEEP_OVERHEAD_NO; i++) { if (g_periph_inform_out_sleep_overhead_cb[i] == cb) { return ERROR; } else if (g_periph_inform_out_sleep_overhead_cb[i] == NULL) { g_periph_inform_out_sleep_overhead_cb[i] = cb; return OK; } } return ERROR; } /**************************************************************************** * Name: esp32c3_pm_unregister_inform_out_sleep_overhead_callback * * Description: * Unregister informing peripherals of light sleep wakeup overhead time * callback function. * * Input Parameters: * cb - callback function * * Returned Value: * None * ****************************************************************************/ int esp32c3_pm_unregister_inform_out_sleep_overhead_callback( inform_out_sleep_overhead_cb_t cb) { for (int i = 0; i < PERIPH_INFORM_OUT_SLEEP_OVERHEAD_NO; i++) { if (g_periph_inform_out_sleep_overhead_cb[i] == cb) { g_periph_inform_out_sleep_overhead_cb[i] = NULL; return OK; } } return ERROR; } /**************************************************************************** * Name: esp32c3_periph_inform_out_sleep_overhead * * Description: * Inform peripherals of light sleep wakeup overhead time * * Input Parameters: * us - light sleep wakeup overhead time * * Returned Value: * None * ****************************************************************************/ void IRAM_ATTR esp32c3_periph_inform_out_sleep_overhead(uint32_t us) { for (int i = 0; i < PERIPH_INFORM_OUT_SLEEP_OVERHEAD_NO; i++) { if (g_periph_inform_out_sleep_overhead_cb[i]) { g_periph_inform_out_sleep_overhead_cb[i](us); } } } /**************************************************************************** * Name: esp32c3_sleep_enable_rtc_timer_wakeup * * Description: * Configure RTC TIMER wake-up interval * * Input Parameters: * time_in_us - Configure wake-up time interval * * Returned Value: * None * ****************************************************************************/ void IRAM_ATTR esp32c3_sleep_enable_rtc_timer_wakeup(uint64_t time_in_us) { s_config.wakeup_triggers |= RTC_TIMER_TRIG_EN; s_config.sleep_duration = time_in_us; } /**************************************************************************** * Name: esp32c3_sleep_enable_wifi_wakeup * * Description: * Configure Wi-Fi wake-up source * * Input Parameters: * None * * Returned Value: * None * ****************************************************************************/ void esp32c3_sleep_enable_wifi_wakeup(void) { s_config.wakeup_triggers |= RTC_WIFI_TRIG_EN; } /**************************************************************************** * Name: esp32c3_light_sleep_start * * Description: * Enter light sleep mode * * Input Parameters: * sleep_time - Actual sleep time * * Returned Value: * 0 is returned on success or a negated errno value is returned * ****************************************************************************/ int IRAM_ATTR esp32c3_light_sleep_start(uint64_t *sleep_time) { int ret = OK; irqstate_t flags; uint32_t pd_flags; uint32_t flash_enable_time_us; uint32_t vddsdio_pd_sleep_duration; struct esp32c3_rtc_vddsdio_config_s vddsdio_config; uint32_t rtc_cntl_xtl_buf_wait_cycles; uint32_t sleep_time_overhead_in; uint32_t ccount_at_sleep_start; flags = enter_critical_section(); s_config.ccount_ticks_record = esp32c3_cpu_cycle_count(); s_config.rtc_ticks_at_sleep_start = esp32c3_rtc_time_get(); ccount_at_sleep_start = esp32c3_cpu_cycle_count(); sleep_time_overhead_in = (ccount_at_sleep_start - s_config.ccount_ticks_record) / (esp32c3_clk_cpu_freq() / 1000000ULL); /* Decide which power domains can be powered down */ pd_flags = esp32c3_get_power_down_flags(); s_config.rtc_clk_cal_period = esp32c3_rtc_clk_cal(RTC_CAL_RTC_MUX, RTC_CLK_SRC_CAL_CYCLES); /* Adjustment time consists of parts below: * 1. Hardware time waiting for internal 8M oscilate clock and XTAL; * 2. Hardware state swithing time of the rtc main state machine; * 3. Code execution time when clock is not stable; * 4. Code execution time which can be measured; */ rtc_cntl_xtl_buf_wait_cycles = esp32c3_rtc_time_us_to_slowclk( RTC_CNTL_XTL_BUF_WAIT_SLP_US, s_config.rtc_clk_cal_period); s_config.sleep_time_adjustment = LIGHT_SLEEP_TIME_OVERHEAD_US + sleep_time_overhead_in + s_config.sleep_time_overhead_out + esp32c3_rtc_time_slowclk_to_us(rtc_cntl_xtl_buf_wait_cycles + RTC_CNTL_CK8M_WAIT_SLP_CYCLES + RTC_CNTL_WAKEUP_DELAY_CYCLES, s_config.rtc_clk_cal_period); /* Decide if VDD_SDIO needs to be powered down; * If it needs to be powered down, adjust sleep time. */ flash_enable_time_us = VDD_SDIO_POWERUP_TO_FLASH_READ_US + DEEP_SLEEP_WAKEUP_DELAY; /* When SPIRAM is disabled in menuconfig, the minimum sleep time of the * system needs to meet the sum below: * 1. Wait time for the flash power-on after waking up; * 2. The execution time of codes between RTC Timer get start time * with hardware starts to switch state to sleep; * 3. The hardware state switching time of the rtc state machine during * sleep and wake-up. This process requires 6 cycles to complete. * The specific hardware state switching process and the cycles * consumed are rtc_cpu_run_stall(1), cut_pll_rtl(2), cut_8m(1), * min_protect(2); * 4. All the adjustment time which is * s_config.sleep_time_adjustment below. */ vddsdio_pd_sleep_duration = MAX(FLASH_PD_MIN_SLEEP_TIME_US, flash_enable_time_us + LIGHT_SLEEP_MIN_TIME_US + s_config.sleep_time_adjustment + esp32c3_rtc_time_slowclk_to_us( RTC_MODULE_SLEEP_PREPARE_CYCLES, s_config.rtc_clk_cal_period)); if (s_config.sleep_duration > vddsdio_pd_sleep_duration) { pd_flags |= RTC_SLEEP_PD_VDDSDIO; if (s_config.sleep_time_overhead_out < flash_enable_time_us) { s_config.sleep_time_adjustment += flash_enable_time_us; } } else { if (s_config.sleep_time_overhead_out > flash_enable_time_us) { s_config.sleep_time_adjustment -= flash_enable_time_us; } } esp32c3_periph_inform_out_sleep_overhead( s_config.sleep_time_adjustment - sleep_time_overhead_in); esp32c3_get_vddsdio_config(&vddsdio_config); /* Enter sleep, then wait for flash to be ready on wakeup */ ret = esp32c3_light_sleep_inner(pd_flags, flash_enable_time_us, vddsdio_config); if (sleep_time != NULL) { *sleep_time = esp32c3_rtc_time_slowclk_to_us(esp32c3_rtc_time_get() - s_config.rtc_ticks_at_sleep_start, s_config.rtc_clk_cal_period); } s_config.sleep_time_overhead_out = (esp32c3_cpu_cycle_count() - s_config.ccount_ticks_record) / (esp32c3_clk_cpu_freq() / 1000000ULL); leave_critical_section(flags); return ret; } /**************************************************************************** * Name: esp32c3_pmstandby * * Description: * Enter force sleep. * * Input Parameters: * time_in_us - force sleep time interval * * Returned Value: * None * ****************************************************************************/ void esp32c3_pmstandby(uint64_t time_in_us) { uint64_t rtc_diff_us; #ifdef CONFIG_ESP32C3_RT_TIMER uint64_t hw_start_us; uint64_t hw_end_us; uint64_t hw_diff_us; #endif /* don't power down XTAL — powering it up takes different time on. */ esp32c3_sleep_enable_rtc_timer_wakeup(time_in_us); #ifdef CONFIG_ESP32C3_RT_TIMER /* Get rt-timer timestamp before entering sleep */ hw_start_us = rt_timer_time_us(); #endif esp32c3_light_sleep_start(&rtc_diff_us); #ifdef CONFIG_ESP32C3_RT_TIMER /* Get rt-timer timestamp after waking up from sleep */ hw_end_us = rt_timer_time_us(); hw_diff_us = hw_end_us - hw_start_us; DEBUGASSERT(rtc_diff_us > hw_diff_us); rt_timer_calibration(rtc_diff_us - hw_diff_us); #endif #ifdef CONFIG_SCHED_TICKLESS up_step_idletime((uint32_t)time_in_us); #endif pwrinfo("Returned from auto-sleep, slept for %" PRIu32 " ms\n", (uint32_t)(rtc_diff_us) / 1000); } /**************************************************************************** * Name: esp32c3_deep_sleep_start * * Description: * Enter deep sleep mode * * Input Parameters: * None * * Returned Value: * None * ****************************************************************************/ void IRAM_ATTR esp32c3_deep_sleep_start(void) { uint32_t pd_flags; /* record current RTC time */ s_config.rtc_ticks_at_sleep_start = esp32c3_rtc_time_get(); /* Decide which power domains can be powered down */ pd_flags = esp32c3_get_power_down_flags(); s_config.rtc_clk_cal_period = getreg32(RTC_SLOW_CLK_CAL_REG); /* Correct the sleep time */ s_config.sleep_time_adjustment = DEEP_SLEEP_TIME_OVERHEAD_US; /* Enter deep sleep */ esp32c3_sleep_start(RTC_SLEEP_PD_DIG | RTC_SLEEP_PD_VDDSDIO | pd_flags); /* Because RTC is in a slower clock domain than the CPU, it * can take several CPU cycles for the sleep mode to start. */ while (1); } /**************************************************************************** * Name: esp32c3_pmsleep * * Description: * Enter deep sleep. * * Input Parameters: * time_in_us - deep sleep time interval * * Returned Value: * None * ****************************************************************************/ void esp32c3_pmsleep(uint64_t time_in_us) { esp32c3_sleep_enable_rtc_timer_wakeup(time_in_us); esp32c3_deep_sleep_start(); } /**************************************************************************** * Name: esp32c3_pm_lockacquire * * Description: * Take a power management lock * ****************************************************************************/ void IRAM_ATTR esp32c3_pm_lockacquire(void) { ++pm_wakelock; } /**************************************************************************** * Name: esp32c3_pm_lockrelease * * Description: * Release the lock taken using esp32c3_pm_lockacquire. * ****************************************************************************/ void IRAM_ATTR esp32c3_pm_lockrelease(void) { --pm_wakelock; } /**************************************************************************** * Name: esp32c3_pm_lockstatus * * Description: * Return power management lock status. * ****************************************************************************/ uint32_t IRAM_ATTR esp32c3_pm_lockstatus(void) { return pm_wakelock; } #endif /* CONFIG_PM */