Merge branch 'bringup/support_callback_mechanism_in_lightsleep_flow' into 'master'

Power Management: support_callback_mechanism_in_lightsleep_flow

Closes WIFI-5936

See merge request espressif/esp-idf!24597
This commit is contained in:
Lou Tian Hao 2023-10-08 20:16:01 +08:00
commit adae54faca
12 changed files with 496 additions and 4 deletions

View File

@ -28,6 +28,7 @@ if(NOT BOOTLOADER_BUILD)
"rtc_module.c" "rtc_module.c"
"sleep_modes.c" "sleep_modes.c"
"sleep_gpio.c" "sleep_gpio.c"
"sleep_event.c"
"sleep_modem.c" "sleep_modem.c"
"regi2c_ctrl.c" "regi2c_ctrl.c"
"esp_gpio_reserve.c" "esp_gpio_reserve.c"

View File

@ -176,6 +176,20 @@ menu "Hardware Settings"
help help
When using rtc gpio wakeup source during deepsleep without external pull-up/downs, you may want to When using rtc gpio wakeup source during deepsleep without external pull-up/downs, you may want to
make use of the internal ones. make use of the internal ones.
config ESP_SLEEP_EVENT_CALLBACKS
bool "Enable registration of sleep event callbacks"
depends on FREERTOS_USE_TICKLESS_IDLE
default n
help
If enabled, it allows user to register sleep event callbacks. It is primarily designed for internal
developers and customers can use PM_LIGHT_SLEEP_CALLBACKS as an alternative.
NOTE: These callbacks are executed from the IDLE task context hence you cannot have any blocking calls
in your callbacks.
NOTE: Enabling these callbacks may change sleep duration calculations based on time spent in
callback and hence it is highly recommended to keep them as short as possible.
endmenu endmenu
menu "ESP_SLEEP_WORKAROUND" menu "ESP_SLEEP_WORKAROUND"

View File

@ -0,0 +1,122 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stddef.h>
#include <stdbool.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
/**
* Using SLEEP_EVENT to determine the execution of specific
* code at a particular point in the sleep flow.
*/
SLEEP_EVENT_HW_EXIT_SLEEP, // CPU wake up and start to work
SLEEP_EVENT_SW_CLK_READY, // CPU frequency restore
SLEEP_EVENT_SW_EXIT_SLEEP, // End of esp_light_sleep_start
SLEEP_EVENT_SW_GOTO_SLEEP, // Beginning of esp_light_sleep_start
SLEEP_EVENT_HW_TIME_START, // Start timing the sleep time
SLEEP_EVENT_HW_GOTO_SLEEP, // Hardware is about to power off
SLEEP_EVENT_SW_CPU_TO_MEM_START, // CPU registers are starting to be saved
SLEEP_EVENT_SW_CPU_TO_MEM_END, // CPU registers have finished saving
#if CONFIG_IDF_TARGET_ESP32H2
SLEEP_EVENT_HW_FLASH_BBPLL_EN_START, // Beginning of rtc_clk_bbpll_enable when using FLASH_PLL
SLEEP_EVENT_HW_FLASH_BBPLL_EN_STOP, // End of rtc_clk_bbpll_enable when using FLASH_PLL
#endif
SLEEP_EVENT_HW_BBPLL_EN_START, // Beginning of rtc_clk_bbpll_enable
SLEEP_EVENT_HW_BBPLL_EN_STOP, // End of rtc_clk_bbpll_enable
SLEEP_EVENT_CB_INDEX_NUM,
} esp_sleep_event_cb_index_t;
/**
* @brief Function prototype for light sleep event callback functions (if CONFIG_FREERTOS_USE_TICKLESS_IDLE).
* @param user_arg is the user provided argument while registering callbacks.
* @param ext_arg is an externally provided parameter that is used when the callback is executed.
* @return None
*/
typedef void (*esp_sleep_event_cb_t)(void *user_arg, void *ext_arg);
/**
* @brief Function entry parameter types for light sleep event callback functions (if CONFIG_FREERTOS_USE_TICKLESS_IDLE)
*/
struct _esp_sleep_event_cb_config_t {
/**
* Callback function defined by internal developers.
*/
esp_sleep_event_cb_t cb;
/**
* Input parameters of callback function defined by internal developers.
*/
void *user_arg;
/**
* Execution priority of callback function defined by internal developers.
* The smaller the priority, the earlier it executes when call esp_sleep_execute_event_callbacks.
* If functions have the same priority, the function registered first will be executed first.
*/
uint32_t prior;
/**
* Next callback configuration defined by internal developer.
*/
struct _esp_sleep_event_cb_config_t *next;
};
typedef struct _esp_sleep_event_cb_config_t esp_sleep_event_cb_config_t;
struct _esp_sleep_event_cbs_config_t {
/**
* Callback configurations defined by internal developers.
*/
esp_sleep_event_cb_config_t *sleep_event_cb_config[SLEEP_EVENT_CB_INDEX_NUM];
};
typedef struct _esp_sleep_event_cbs_config_t esp_sleep_event_cbs_config_t;
/**
* @brief Register event callbacks for light sleep internal events (if CONFIG_FREERTOS_USE_TICKLESS_IDLE)
* @param event_id Designed to register the corresponding event_cb in g_sleep_event_cbs_config
* @param event_cb_conf Config struct containing event callback function and corresponding argument
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if the input parameter event_cb_conf is NULL or event_id is out of range
* - ESP_ERR_NO_MEM if the remaining memory is insufficient to support malloc
* - ESP_FAIL if register the same function repeatedly
*
* @note Some of these callback functions are called from IDLE task context hence they cannot call any blocking functions
* @note Passing NULL value will not deregister the callbacks, it will silently ignore and return ESP_OK
*/
esp_err_t esp_sleep_register_event_callback(esp_sleep_event_cb_index_t event_id, const esp_sleep_event_cb_config_t *event_cb_conf);
/**
* @brief Unregister event callbacks for light sleep internal events (if CONFIG_FREERTOS_USE_TICKLESS_IDLE)
* @param event_id Designed to unregister the corresponding event_cb in g_sleep_event_cbs_config
* @param event_cb_conf Config struct containing event callback function and corresponding argument
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if the input parameter cb is NULL or event_id is out of range
*/
esp_err_t esp_sleep_unregister_event_callback(esp_sleep_event_cb_index_t event_id, esp_sleep_event_cb_t cb);
/**
* @brief Designed to execute functions in the esp_sleep_event_cb_config_t linked list
*
* @param event_id Designed to annotate the corresponding event_cb in g_sleep_event_cbs_config
* @param ext_arg Designed to pass external parameters
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if event_id is out of range
*/
esp_err_t esp_sleep_execute_event_callbacks(esp_sleep_event_cb_index_t event_id, void *ext_arg);
#ifdef __cplusplus
}
#endif

View File

@ -20,6 +20,7 @@
#include "hal/regi2c_ctrl_ll.h" #include "hal/regi2c_ctrl_ll.h"
#include "soc/io_mux_reg.h" #include "soc/io_mux_reg.h"
#include "soc/lp_aon_reg.h" #include "soc/lp_aon_reg.h"
#include "esp_private/sleep_event.h"
#ifdef BOOTLOADER_BUILD #ifdef BOOTLOADER_BUILD
#include "hal/modem_lpcon_ll.h" #include "hal/modem_lpcon_ll.h"
@ -265,6 +266,10 @@ bool rtc_clk_cpu_freq_mhz_to_config(uint32_t freq_mhz, rtc_cpu_freq_config_t *ou
return true; return true;
} }
__attribute__((weak)) void rtc_clk_set_cpu_switch_to_bbpll(int event_id)
{
}
void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t *config) void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t *config)
{ {
soc_cpu_clk_src_t old_cpu_clk_src = clk_ll_cpu_get_src(); soc_cpu_clk_src_t old_cpu_clk_src = clk_ll_cpu_get_src();
@ -276,10 +281,12 @@ void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t *config)
} }
} else if (config->source == SOC_CPU_CLK_SRC_PLL) { } else if (config->source == SOC_CPU_CLK_SRC_PLL) {
if (old_cpu_clk_src != SOC_CPU_CLK_SRC_PLL) { if (old_cpu_clk_src != SOC_CPU_CLK_SRC_PLL) {
rtc_clk_set_cpu_switch_to_bbpll(SLEEP_EVENT_HW_BBPLL_EN_START);
rtc_clk_bbpll_enable(); rtc_clk_bbpll_enable();
rtc_clk_bbpll_configure(rtc_clk_xtal_freq_get(), config->source_freq_mhz); rtc_clk_bbpll_configure(rtc_clk_xtal_freq_get(), config->source_freq_mhz);
} }
rtc_clk_cpu_freq_to_pll_mhz(config->freq_mhz); rtc_clk_cpu_freq_to_pll_mhz(config->freq_mhz);
rtc_clk_set_cpu_switch_to_bbpll(SLEEP_EVENT_HW_BBPLL_EN_STOP);
} else if (config->source == SOC_CPU_CLK_SRC_RC_FAST) { } else if (config->source == SOC_CPU_CLK_SRC_RC_FAST) {
rtc_clk_cpu_freq_to_8m(); rtc_clk_cpu_freq_to_8m();
if ((old_cpu_clk_src == SOC_CPU_CLK_SRC_PLL) && !s_bbpll_digi_consumers_ref_count) { if ((old_cpu_clk_src == SOC_CPU_CLK_SRC_PLL) && !s_bbpll_digi_consumers_ref_count) {

View File

@ -21,6 +21,7 @@
#include "soc/io_mux_reg.h" #include "soc/io_mux_reg.h"
#include "soc/lp_aon_reg.h" #include "soc/lp_aon_reg.h"
#include "soc/lp_clkrst_reg.h" #include "soc/lp_clkrst_reg.h"
#include "esp_private/sleep_event.h"
#ifdef BOOTLOADER_BUILD #ifdef BOOTLOADER_BUILD
#include "hal/modem_lpcon_ll.h" #include "hal/modem_lpcon_ll.h"
@ -314,6 +315,10 @@ bool rtc_clk_cpu_freq_mhz_to_config(uint32_t freq_mhz, rtc_cpu_freq_config_t *ou
return true; return true;
} }
__attribute__((weak)) void rtc_clk_set_cpu_switch_to_bbpll(int event_id)
{
}
void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t *config) void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t *config)
{ {
soc_cpu_clk_src_t old_cpu_clk_src = clk_ll_cpu_get_src(); soc_cpu_clk_src_t old_cpu_clk_src = clk_ll_cpu_get_src();
@ -325,10 +330,12 @@ void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t *config)
} }
} else if (config->source == SOC_CPU_CLK_SRC_PLL) { } else if (config->source == SOC_CPU_CLK_SRC_PLL) {
if (old_cpu_clk_src != SOC_CPU_CLK_SRC_PLL && old_cpu_clk_src != SOC_CPU_CLK_SRC_FLASH_PLL) { if (old_cpu_clk_src != SOC_CPU_CLK_SRC_PLL && old_cpu_clk_src != SOC_CPU_CLK_SRC_FLASH_PLL) {
rtc_clk_set_cpu_switch_to_bbpll(SLEEP_EVENT_HW_BBPLL_EN_START);
rtc_clk_bbpll_enable(); rtc_clk_bbpll_enable();
rtc_clk_bbpll_configure(rtc_clk_xtal_freq_get(), config->source_freq_mhz); rtc_clk_bbpll_configure(rtc_clk_xtal_freq_get(), config->source_freq_mhz);
} }
rtc_clk_cpu_freq_to_pll_mhz(config->freq_mhz); rtc_clk_cpu_freq_to_pll_mhz(config->freq_mhz);
rtc_clk_set_cpu_switch_to_bbpll(SLEEP_EVENT_HW_BBPLL_EN_STOP);
} else if (config->source == SOC_CPU_CLK_SRC_RC_FAST) { } else if (config->source == SOC_CPU_CLK_SRC_RC_FAST) {
rtc_clk_cpu_freq_to_8m(); rtc_clk_cpu_freq_to_8m();
if ((old_cpu_clk_src == SOC_CPU_CLK_SRC_PLL || old_cpu_clk_src == SOC_CPU_CLK_SRC_FLASH_PLL) && if ((old_cpu_clk_src == SOC_CPU_CLK_SRC_PLL || old_cpu_clk_src == SOC_CPU_CLK_SRC_FLASH_PLL) &&
@ -339,10 +346,12 @@ void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t *config)
if (old_cpu_clk_src != SOC_CPU_CLK_SRC_PLL && old_cpu_clk_src != SOC_CPU_CLK_SRC_FLASH_PLL) { if (old_cpu_clk_src != SOC_CPU_CLK_SRC_PLL && old_cpu_clk_src != SOC_CPU_CLK_SRC_FLASH_PLL) {
// On ESP32H2, FLASH_PLL (64MHz) is directly derived from the BBPLL (96MHz) // On ESP32H2, FLASH_PLL (64MHz) is directly derived from the BBPLL (96MHz)
// Therefore, enabling and configuration are applied to BBPLL. // Therefore, enabling and configuration are applied to BBPLL.
rtc_clk_set_cpu_switch_to_bbpll(SLEEP_EVENT_HW_FLASH_BBPLL_EN_START);
rtc_clk_bbpll_enable(); rtc_clk_bbpll_enable();
rtc_clk_bbpll_configure(rtc_clk_xtal_freq_get(), CLK_LL_PLL_96M_FREQ_MHZ); rtc_clk_bbpll_configure(rtc_clk_xtal_freq_get(), CLK_LL_PLL_96M_FREQ_MHZ);
} }
rtc_clk_cpu_freq_to_flash_pll(config->freq_mhz, config->div); rtc_clk_cpu_freq_to_flash_pll(config->freq_mhz, config->div);
rtc_clk_set_cpu_switch_to_bbpll(SLEEP_EVENT_HW_FLASH_BBPLL_EN_STOP);
} }
} }

View File

@ -20,6 +20,7 @@
#include "esp_heap_caps.h" #include "esp_heap_caps.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "esp_private/sleep_cpu.h" #include "esp_private/sleep_cpu.h"
#include "esp_private/sleep_event.h"
#include "sdkconfig.h" #include "sdkconfig.h"
#if SOC_PMU_SUPPORTED #if SOC_PMU_SUPPORTED
@ -685,6 +686,7 @@ static IRAM_ATTR esp_err_t do_cpu_retention(sleep_cpu_entry_cb_t goto_sleep,
{ {
RvCoreCriticalSleepFrame * frame = rv_core_critical_regs_save(); RvCoreCriticalSleepFrame * frame = rv_core_critical_regs_save();
if ((frame->pmufunc & 0x3) == 0x1) { 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 #if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
/* Minus 2 * sizeof(long) is for bypass `pmufunc` and `frame_crc` field */ /* Minus 2 * sizeof(long) is for bypass `pmufunc` and `frame_crc` field */
update_retention_frame_crc((uint32_t*)frame, RV_SLEEP_CTX_FRMSZ - 2 * sizeof(long), (uint32_t *)(&frame->frame_crc)); update_retention_frame_crc((uint32_t*)frame, RV_SLEEP_CTX_FRMSZ - 2 * sizeof(long), (uint32_t *)(&frame->frame_crc));
@ -704,6 +706,7 @@ static IRAM_ATTR esp_err_t do_cpu_retention(sleep_cpu_entry_cb_t goto_sleep,
esp_err_t IRAM_ATTR esp_sleep_cpu_retention(uint32_t (*goto_sleep)(uint32_t, uint32_t, uint32_t, bool), 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) uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp_mem_inf_fpu, bool dslp)
{ {
esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_CPU_TO_MEM_START, (void *)0);
uint32_t mstatus = save_mstatus_and_disable_global_int(); uint32_t mstatus = save_mstatus_and_disable_global_int();
cpu_domain_dev_regs_save(s_cpu_retention.retent.plic_frame); cpu_domain_dev_regs_save(s_cpu_retention.retent.plic_frame);
@ -728,7 +731,6 @@ esp_err_t IRAM_ATTR esp_sleep_cpu_retention(uint32_t (*goto_sleep)(uint32_t, uin
cpu_domain_dev_regs_restore(s_cpu_retention.retent.intpri_frame); cpu_domain_dev_regs_restore(s_cpu_retention.retent.intpri_frame);
cpu_domain_dev_regs_restore(s_cpu_retention.retent.clint_frame); cpu_domain_dev_regs_restore(s_cpu_retention.retent.clint_frame);
cpu_domain_dev_regs_restore(s_cpu_retention.retent.plic_frame); cpu_domain_dev_regs_restore(s_cpu_retention.retent.plic_frame);
restore_mstatus(mstatus); restore_mstatus(mstatus);
return err; return err;
} }

View File

@ -166,9 +166,16 @@ wait_sync_done:
.section .iram1,"ax" .section .iram1,"ax"
.global rv_core_critical_regs_restore .global rv_core_critical_regs_restore
.weak rv_core_critical_regs_restore
.type rv_core_critical_regs_restore,@function .type rv_core_critical_regs_restore,@function
.global _rv_core_critical_regs_restore
.type _rv_core_critical_regs_restore,@function
.align 4 .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: rv_core_critical_regs_restore:
la t0, rv_core_critical_regs_frame la t0, rv_core_critical_regs_frame

View File

@ -0,0 +1,91 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stddef.h>
#include <string.h>
#include "sdkconfig.h"
#include "soc/soc_caps.h"
#include "esp_private/sleep_event.h"
#include "esp_sleep.h"
#include "esp_log.h"
#include "esp_check.h"
#include "freertos/FreeRTOS.h"
#if CONFIG_ESP_SLEEP_EVENT_CALLBACKS
esp_sleep_event_cbs_config_t g_sleep_event_cbs_config;
static portMUX_TYPE s_sleep_event_mutex = portMUX_INITIALIZER_UNLOCKED;
esp_err_t esp_sleep_register_event_callback(esp_sleep_event_cb_index_t event_id, const esp_sleep_event_cb_config_t *event_cb_conf) {
if (event_cb_conf == NULL || event_id >= SLEEP_EVENT_CB_INDEX_NUM) {
return ESP_ERR_INVALID_ARG;
}
esp_sleep_event_cb_config_t *new_config = (esp_sleep_event_cb_config_t *)heap_caps_malloc(sizeof(esp_sleep_event_cb_config_t), MALLOC_CAP_INTERNAL);
if (new_config == NULL) {
return ESP_ERR_NO_MEM; /* Memory allocation failed */
}
portENTER_CRITICAL(&s_sleep_event_mutex);
esp_sleep_event_cb_config_t **current_ptr = &(g_sleep_event_cbs_config.sleep_event_cb_config[event_id]);
while (*current_ptr != NULL) {
if (((*current_ptr)->cb) == (event_cb_conf->cb)) {
free(new_config);
portEXIT_CRITICAL(&s_sleep_event_mutex);
return ESP_FAIL;
}
current_ptr = &((*current_ptr)->next);
}
*new_config = *event_cb_conf;
while (*current_ptr != NULL && (*current_ptr)->prior <= new_config->prior) {
current_ptr = &((*current_ptr)->next);
}
new_config->next = *current_ptr;
*current_ptr = new_config;
portEXIT_CRITICAL(&s_sleep_event_mutex);
return ESP_OK;
}
esp_err_t esp_sleep_unregister_event_callback(esp_sleep_event_cb_index_t event_id, esp_sleep_event_cb_t cb) {
if (cb == NULL || event_id >= SLEEP_EVENT_CB_INDEX_NUM) {
return ESP_ERR_INVALID_ARG;
}
portENTER_CRITICAL(&s_sleep_event_mutex);
esp_sleep_event_cb_config_t **current_ptr = &(g_sleep_event_cbs_config.sleep_event_cb_config[event_id]);
while (*current_ptr != NULL) {
if (((*current_ptr)->cb) == cb) {
esp_sleep_event_cb_config_t *temp = *current_ptr;
*current_ptr = (*current_ptr)->next;
free(temp);
break;
}
current_ptr = &((*current_ptr)->next);
}
portEXIT_CRITICAL(&s_sleep_event_mutex);
return ESP_OK;
}
#endif
#if CONFIG_ESP_SLEEP_EVENT_CALLBACKS
esp_err_t IRAM_ATTR esp_sleep_execute_event_callbacks(esp_sleep_event_cb_index_t event_id, void *ext_arg)
{
if (event_id >= SLEEP_EVENT_CB_INDEX_NUM) {
return ESP_ERR_INVALID_ARG;
}
esp_sleep_event_cb_config_t *current = g_sleep_event_cbs_config.sleep_event_cb_config[event_id];
while (current != NULL) {
(current->cb)(current->user_arg, ext_arg);
current = current->next;
}
return ESP_OK;
}
#else
esp_err_t IRAM_ATTR esp_sleep_execute_event_callbacks(esp_sleep_event_cb_index_t event_id, void *ext_arg)
{
return ESP_OK;
}
#endif

View File

@ -14,6 +14,7 @@
#include "esp_sleep.h" #include "esp_sleep.h"
#include "esp_private/esp_sleep_internal.h" #include "esp_private/esp_sleep_internal.h"
#include "esp_private/esp_timer_private.h" #include "esp_private/esp_timer_private.h"
#include "esp_private/sleep_event.h"
#include "esp_private/system_internal.h" #include "esp_private/system_internal.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_newlib.h" #include "esp_newlib.h"
@ -762,12 +763,14 @@ static esp_err_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags, esp_sleep_mode_t m
#if SOC_PMU_SUPPORTED #if SOC_PMU_SUPPORTED
#if SOC_PM_CPU_RETENTION_BY_SW #if SOC_PM_CPU_RETENTION_BY_SW
esp_sleep_execute_event_callbacks(SLEEP_EVENT_HW_GOTO_SLEEP, (void *)0);
if (pd_flags & PMU_SLEEP_PD_CPU) { if (pd_flags & PMU_SLEEP_PD_CPU) {
result = esp_sleep_cpu_retention(pmu_sleep_start, s_config.wakeup_triggers, reject_triggers, config.power.hp_sys.dig_power.mem_dslp, deep_sleep); result = esp_sleep_cpu_retention(pmu_sleep_start, s_config.wakeup_triggers, reject_triggers, config.power.hp_sys.dig_power.mem_dslp, deep_sleep);
} else { } else {
#endif #endif
result = call_rtc_sleep_start(reject_triggers, config.power.hp_sys.dig_power.mem_dslp, deep_sleep); result = call_rtc_sleep_start(reject_triggers, config.power.hp_sys.dig_power.mem_dslp, deep_sleep);
} }
esp_sleep_execute_event_callbacks(SLEEP_EVENT_HW_EXIT_SLEEP, (void *)0);
#else #else
result = call_rtc_sleep_start(reject_triggers, config.lslp_mem_inf_fpu, deep_sleep); result = call_rtc_sleep_start(reject_triggers, config.lslp_mem_inf_fpu, deep_sleep);
#endif #endif
@ -809,6 +812,8 @@ static esp_err_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags, esp_sleep_mode_t m
rtc_clk_cpu_freq_set_config(&cpu_freq_config); rtc_clk_cpu_freq_set_config(&cpu_freq_config);
} }
esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_CLK_READY, (void *)0);
if (cpu_freq_config.source == SOC_CPU_CLK_SRC_PLL) { if (cpu_freq_config.source == SOC_CPU_CLK_SRC_PLL) {
// Turn up MSPI speed if switch to PLL // Turn up MSPI speed if switch to PLL
mspi_timing_change_speed_mode_cache_safe(false); mspi_timing_change_speed_mode_cache_safe(false);
@ -975,6 +980,7 @@ FORCE_INLINE_ATTR bool can_power_down_vddsdio(uint32_t pd_flags, const uint32_t
esp_err_t esp_light_sleep_start(void) esp_err_t esp_light_sleep_start(void)
{ {
s_config.ccount_ticks_record = esp_cpu_get_cycle_count(); s_config.ccount_ticks_record = esp_cpu_get_cycle_count();
esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_GOTO_SLEEP, (void *)0);
#if CONFIG_ESP_TASK_WDT_USE_ESP_TIMER #if CONFIG_ESP_TASK_WDT_USE_ESP_TIMER
esp_err_t timerret = ESP_OK; esp_err_t timerret = ESP_OK;
@ -1012,9 +1018,9 @@ esp_err_t esp_light_sleep_start(void)
s_config.rtc_ticks_at_sleep_start = rtc_time_get(); s_config.rtc_ticks_at_sleep_start = rtc_time_get();
#endif #endif
uint32_t ccount_at_sleep_start = esp_cpu_get_cycle_count(); uint32_t ccount_at_sleep_start = esp_cpu_get_cycle_count();
esp_sleep_execute_event_callbacks(SLEEP_EVENT_HW_TIME_START, (void *)0);
uint64_t high_res_time_at_start = esp_timer_get_time(); uint64_t high_res_time_at_start = esp_timer_get_time();
uint32_t sleep_time_overhead_in = (ccount_at_sleep_start - s_config.ccount_ticks_record) / (esp_clk_cpu_freq() / 1000000ULL); uint32_t sleep_time_overhead_in = (ccount_at_sleep_start - s_config.ccount_ticks_record) / (esp_clk_cpu_freq() / 1000000ULL);
esp_ipc_isr_stall_other_cpu(); esp_ipc_isr_stall_other_cpu();
#if CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION && CONFIG_PM_SLP_IRAM_OPT #if CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION && CONFIG_PM_SLP_IRAM_OPT
@ -1196,6 +1202,7 @@ esp_err_t esp_light_sleep_start(void)
} }
#endif // CONFIG_ESP_TASK_WDT_USE_ESP_TIMER #endif // CONFIG_ESP_TASK_WDT_USE_ESP_TIMER
esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_EXIT_SLEEP, (void *)0);
s_config.sleep_time_overhead_out = (esp_cpu_get_cycle_count() - s_config.ccount_ticks_record) / (esp_clk_cpu_freq() / 1000000ULL); s_config.sleep_time_overhead_out = (esp_cpu_get_cycle_count() - s_config.ccount_ticks_record) / (esp_clk_cpu_freq() / 1000000ULL);
return err; return err;
} }

View File

@ -152,4 +152,18 @@ menu "Power Management"
bool bool
default y if PM_ENABLE && BTDM_CTRL_HLI default y if PM_ENABLE && BTDM_CTRL_HLI
config PM_LIGHT_SLEEP_CALLBACKS
bool "Enable registration of pm light sleep callbacks"
depends on FREERTOS_USE_TICKLESS_IDLE
default n
help
If enabled, it allows user to register entry and exit callbacks which are called before and after
entering auto light sleep.
NOTE: These callbacks are executed from the IDLE task context hence you cannot have any blocking calls
in your callbacks.
NOTE: Enabling these callbacks may change sleep duration calculations based on time spent in callback and
hence it is highly recommended to keep them as short as possible
endmenu # "Power Management" endmenu # "Power Management"

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2016-2023 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -192,6 +192,63 @@ esp_err_t esp_pm_lock_delete(esp_pm_lock_handle_t handle);
*/ */
esp_err_t esp_pm_dump_locks(FILE* stream); esp_err_t esp_pm_dump_locks(FILE* stream);
#if CONFIG_PM_LIGHT_SLEEP_CALLBACKS
/**
* @brief Function prototype for light sleep callback functions (if CONFIG_FREERTOS_USE_TICKLESS_IDLE)
*
* @param sleep_time_us supplied by the power management framework.
* For entry callback, sleep_time_us indicates the expected sleep time in us
* For exit callback, sleep_time_us indicates the actual sleep time in us
* @param arg is the user provided argument while registering callbacks
*
* @return
* - ESP_OK allow entry light sleep mode
*/
typedef esp_err_t (*esp_pm_light_sleep_cb_t)(int64_t sleep_time_us, void *arg);
typedef struct {
/**
* Callback function defined by internal developers.
*/
esp_pm_light_sleep_cb_t enter_cb;
esp_pm_light_sleep_cb_t exit_cb;
/**
* Input parameters of callback function defined by internal developers.
*/
void *enter_cb_user_arg;
void *exit_cb_user_arg;
/**
* Execution priority of callback function defined by internal developers.
* The smaller the priority, the earlier it executes when call esp_sleep_execute_event_callbacks.
* If functions have the same priority, the function registered first will be executed first.
*/
uint32_t enter_cb_prior;
uint32_t exit_cb_prior;
} esp_pm_sleep_cbs_register_config_t;
/**
* @brief Register entry or exit callbacks for light sleep (if CONFIG_FREERTOS_USE_TICKLESS_IDLE)
* @param cbs_conf Config struct containing entry or exit callbacks function and corresponding argument
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if the input parameter enter_cb and exit_cb in cbs_conf are NULL
* - ESP_ERR_NO_MEM if the remaining memory is insufficient to support malloc
* - ESP_FAIL if register the same function repeatedly
*
* @note These callback functions are called from IDLE task context hence they cannot call any blocking functions
*/
esp_err_t esp_pm_light_sleep_register_cbs(esp_pm_sleep_cbs_register_config_t *cbs_conf);
/**
* @brief Unregister entry or exit callbacks for light sleep (if CONFIG_FREERTOS_USE_TICKLESS_IDLE)
* @param cbs_conf Config struct containing entry or exit callbacks function and corresponding argument
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if the input parameter enter_cb and exit_cb in cbs_conf are NULL
*/
esp_err_t esp_pm_light_sleep_unregister_cbs(esp_pm_sleep_cbs_register_config_t *cbs_conf);
#endif
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -43,6 +43,7 @@
#include "esp_private/sleep_gpio.h" #include "esp_private/sleep_gpio.h"
#include "esp_private/sleep_modem.h" #include "esp_private/sleep_modem.h"
#include "esp_sleep.h" #include "esp_sleep.h"
#include "esp_memory_utils.h"
#include "sdkconfig.h" #include "sdkconfig.h"
@ -203,6 +204,146 @@ pm_mode_t esp_pm_impl_get_mode(esp_pm_lock_type_t type, int arg)
} }
} }
#if CONFIG_PM_LIGHT_SLEEP_CALLBACKS
/**
* @brief Function entry parameter types for light sleep callback functions (if CONFIG_FREERTOS_USE_TICKLESS_IDLE)
*/
typedef struct {
/**
* Callback function defined by user.
*/
esp_pm_light_sleep_cb_t cb;
/**
* Input parameters of callback function defined by user.
*/
void *arg;
/**
* Execution priority of callback function defined by user.
*/
uint32_t prior;
/**
* Next callback function defined by user.
*/
struct _esp_pm_sleep_cb_config_t *next;
} esp_pm_sleep_cb_config_t;
static esp_pm_sleep_cb_config_t *s_light_sleep_enter_cb_config;
static esp_pm_sleep_cb_config_t *s_light_sleep_exit_cb_config;
static portMUX_TYPE s_sleep_pm_cb_mutex = portMUX_INITIALIZER_UNLOCKED;
esp_err_t esp_pm_light_sleep_register_cbs(esp_pm_sleep_cbs_register_config_t *cbs_conf)
{
if (cbs_conf->enter_cb == NULL && cbs_conf->exit_cb == NULL) {
return ESP_ERR_INVALID_ARG;
}
portENTER_CRITICAL(&s_sleep_pm_cb_mutex);
if (cbs_conf->enter_cb != NULL) {
esp_pm_sleep_cb_config_t **current_enter_ptr = &(s_light_sleep_enter_cb_config);
while (*current_enter_ptr != NULL) {
if (((*current_enter_ptr)->cb) == (cbs_conf->enter_cb)) {
portEXIT_CRITICAL(&s_sleep_pm_cb_mutex);
return ESP_FAIL;
}
current_enter_ptr = &((*current_enter_ptr)->next);
}
esp_pm_sleep_cb_config_t *new_enter_config = (esp_pm_sleep_cb_config_t *)heap_caps_malloc(sizeof(esp_pm_sleep_cb_config_t), MALLOC_CAP_INTERNAL);
if (new_enter_config == NULL) {
portEXIT_CRITICAL(&s_sleep_pm_cb_mutex);
return ESP_ERR_NO_MEM; /* Memory allocation failed */
}
new_enter_config->cb = cbs_conf->enter_cb;
new_enter_config->arg = cbs_conf->enter_cb_user_arg;
new_enter_config->prior = cbs_conf->enter_cb_prior;
while (*current_enter_ptr != NULL && (*current_enter_ptr)->prior <= new_enter_config->prior) {
current_enter_ptr = &((*current_enter_ptr)->next);
}
new_enter_config->next = *current_enter_ptr;
*current_enter_ptr = new_enter_config;
}
if (cbs_conf->exit_cb != NULL) {
esp_pm_sleep_cb_config_t **current_exit_ptr = &(s_light_sleep_exit_cb_config);
while (*current_exit_ptr != NULL) {
if (((*current_exit_ptr)->cb) == (cbs_conf->exit_cb)) {
portEXIT_CRITICAL(&s_sleep_pm_cb_mutex);
return ESP_FAIL;
}
current_exit_ptr = &((*current_exit_ptr)->next);
}
esp_pm_sleep_cb_config_t *new_exit_config = (esp_pm_sleep_cb_config_t *)heap_caps_malloc(sizeof(esp_pm_sleep_cb_config_t), MALLOC_CAP_INTERNAL);
if (new_exit_config == NULL) {
portEXIT_CRITICAL(&s_sleep_pm_cb_mutex);
return ESP_ERR_NO_MEM; /* Memory allocation failed */
}
new_exit_config->cb = cbs_conf->exit_cb;
new_exit_config->arg = cbs_conf->exit_cb_user_arg;
new_exit_config->prior = cbs_conf->exit_cb_prior;
while (*current_exit_ptr != NULL && (*current_exit_ptr)->prior <= new_exit_config->prior) {
current_exit_ptr = &((*current_exit_ptr)->next);
}
new_exit_config->next = *current_exit_ptr;
*current_exit_ptr = new_exit_config;
}
portEXIT_CRITICAL(&s_sleep_pm_cb_mutex);
return ESP_OK;
}
esp_err_t esp_pm_light_sleep_unregister_cbs(esp_pm_sleep_cbs_register_config_t *cbs_conf)
{
if (cbs_conf->enter_cb == NULL && cbs_conf->exit_cb == NULL) {
return ESP_ERR_INVALID_ARG;
}
portENTER_CRITICAL(&s_sleep_pm_cb_mutex);
if (cbs_conf->enter_cb != NULL) {
esp_pm_sleep_cb_config_t **current_enter_ptr = &(s_light_sleep_enter_cb_config);
while (*current_enter_ptr != NULL) {
if ((*current_enter_ptr)->cb == cbs_conf->enter_cb) {
esp_pm_sleep_cb_config_t *temp = *current_enter_ptr;
*current_enter_ptr = (*current_enter_ptr)->next;
free(temp);
break;
}
current_enter_ptr = &((*current_enter_ptr)->next);
}
}
if (cbs_conf->exit_cb != NULL) {
esp_pm_sleep_cb_config_t **current_exit_ptr = &(s_light_sleep_exit_cb_config);
while (*current_exit_ptr != NULL) {
if ((*current_exit_ptr)->cb == cbs_conf->exit_cb) {
esp_pm_sleep_cb_config_t *temp = *current_exit_ptr;
*current_exit_ptr = (*current_exit_ptr)->next;
free(temp);
break;
}
current_exit_ptr = &((*current_exit_ptr)->next);
}
}
portEXIT_CRITICAL(&s_sleep_pm_cb_mutex);
return ESP_OK;
}
static esp_err_t IRAM_ATTR esp_pm_execute_enter_sleep_callbacks(int64_t sleep_time_us)
{
esp_pm_sleep_cb_config_t *enter_current = s_light_sleep_enter_cb_config;
while (enter_current != NULL) {
enter_current->cb(sleep_time_us, enter_current->arg);
enter_current = enter_current->next;
}
return ESP_OK;
}
static esp_err_t IRAM_ATTR esp_pm_execute_exit_sleep_callbacks(int64_t sleep_time_us)
{
esp_pm_sleep_cb_config_t *exit_current = s_light_sleep_exit_cb_config;
while (exit_current != NULL) {
exit_current->cb(sleep_time_us, exit_current->arg);
exit_current = exit_current->next;
}
return ESP_OK;
}
#endif
static esp_err_t esp_pm_sleep_configure(const void *vconfig) static esp_err_t esp_pm_sleep_configure(const void *vconfig)
{ {
esp_err_t err = ESP_OK; esp_err_t err = ESP_OK;
@ -629,6 +770,20 @@ void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
int64_t wakeup_delay_us = portTICK_PERIOD_MS * 1000LL * xExpectedIdleTime; int64_t wakeup_delay_us = portTICK_PERIOD_MS * 1000LL * xExpectedIdleTime;
int64_t sleep_time_us = MIN(wakeup_delay_us, time_until_next_alarm); int64_t sleep_time_us = MIN(wakeup_delay_us, time_until_next_alarm);
if (sleep_time_us >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP * portTICK_PERIOD_MS * 1000LL) { if (sleep_time_us >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP * portTICK_PERIOD_MS * 1000LL) {
int64_t slept_us = 0;
#if CONFIG_PM_LIGHT_SLEEP_CALLBACKS
if (s_light_sleep_enter_cb_config != NULL && s_light_sleep_enter_cb_config->cb) {
uint32_t cycle = esp_cpu_get_cycle_count();
esp_err_t err = esp_pm_execute_enter_sleep_callbacks(sleep_time_us);
if (err != ESP_OK) {
portEXIT_CRITICAL(&s_switch_lock);
return;
}
sleep_time_us -= (esp_cpu_get_cycle_count() - cycle) / (esp_clk_cpu_freq() / 1000000ULL);
}
if (sleep_time_us >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP * portTICK_PERIOD_MS * 1000LL)
{
#endif
esp_sleep_enable_timer_wakeup(sleep_time_us - LIGHT_SLEEP_EARLY_WAKEUP_US); esp_sleep_enable_timer_wakeup(sleep_time_us - LIGHT_SLEEP_EARLY_WAKEUP_US);
#if CONFIG_PM_TRACE && SOC_PM_SUPPORT_RTC_PERIPH_PD #if CONFIG_PM_TRACE && SOC_PM_SUPPORT_RTC_PERIPH_PD
/* to force tracing GPIOs to keep state */ /* to force tracing GPIOs to keep state */
@ -644,7 +799,7 @@ void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
s_light_sleep_counts++; s_light_sleep_counts++;
#endif #endif
} }
int64_t slept_us = esp_timer_get_time() - sleep_start; slept_us = esp_timer_get_time() - sleep_start;
ESP_PM_TRACE_EXIT(SLEEP, core_id); ESP_PM_TRACE_EXIT(SLEEP, core_id);
uint32_t slept_ticks = slept_us / (portTICK_PERIOD_MS * 1000LL); uint32_t slept_ticks = slept_us / (portTICK_PERIOD_MS * 1000LL);
@ -667,6 +822,12 @@ void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
#endif #endif
} }
other_core_should_skip_light_sleep(core_id); other_core_should_skip_light_sleep(core_id);
#if CONFIG_PM_LIGHT_SLEEP_CALLBACKS
}
if (s_light_sleep_exit_cb_config != NULL && s_light_sleep_exit_cb_config->cb) {
esp_pm_execute_exit_sleep_callbacks(slept_us);
}
#endif
} }
} }
portEXIT_CRITICAL(&s_switch_lock); portEXIT_CRITICAL(&s_switch_lock);