kopia lustrzana https://github.com/espressif/esp-idf
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!24597pull/12384/head
commit
adae54faca
|
@ -28,6 +28,7 @@ if(NOT BOOTLOADER_BUILD)
|
|||
"rtc_module.c"
|
||||
"sleep_modes.c"
|
||||
"sleep_gpio.c"
|
||||
"sleep_event.c"
|
||||
"sleep_modem.c"
|
||||
"regi2c_ctrl.c"
|
||||
"esp_gpio_reserve.c"
|
||||
|
|
|
@ -176,6 +176,20 @@ menu "Hardware Settings"
|
|||
help
|
||||
When using rtc gpio wakeup source during deepsleep without external pull-up/downs, you may want to
|
||||
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
|
||||
|
||||
menu "ESP_SLEEP_WORKAROUND"
|
||||
|
|
|
@ -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
|
|
@ -20,6 +20,7 @@
|
|||
#include "hal/regi2c_ctrl_ll.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
#include "soc/lp_aon_reg.h"
|
||||
#include "esp_private/sleep_event.h"
|
||||
|
||||
#ifdef BOOTLOADER_BUILD
|
||||
#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;
|
||||
}
|
||||
|
||||
__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)
|
||||
{
|
||||
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) {
|
||||
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_configure(rtc_clk_xtal_freq_get(), config->source_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) {
|
||||
rtc_clk_cpu_freq_to_8m();
|
||||
if ((old_cpu_clk_src == SOC_CPU_CLK_SRC_PLL) && !s_bbpll_digi_consumers_ref_count) {
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "soc/io_mux_reg.h"
|
||||
#include "soc/lp_aon_reg.h"
|
||||
#include "soc/lp_clkrst_reg.h"
|
||||
#include "esp_private/sleep_event.h"
|
||||
|
||||
#ifdef BOOTLOADER_BUILD
|
||||
#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;
|
||||
}
|
||||
|
||||
__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)
|
||||
{
|
||||
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) {
|
||||
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_configure(rtc_clk_xtal_freq_get(), config->source_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) {
|
||||
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) &&
|
||||
|
@ -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) {
|
||||
// On ESP32H2, FLASH_PLL (64MHz) is directly derived from the BBPLL (96MHz)
|
||||
// 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_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_set_cpu_switch_to_bbpll(SLEEP_EVENT_HW_FLASH_BBPLL_EN_STOP);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "esp_heap_caps.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_private/sleep_cpu.h"
|
||||
#include "esp_private/sleep_event.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#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();
|
||||
if ((frame->pmufunc & 0x3) == 0x1) {
|
||||
esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_CPU_TO_MEM_END, (void *)0);
|
||||
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
|
||||
/* Minus 2 * sizeof(long) is for bypass `pmufunc` and `frame_crc` field */
|
||||
update_retention_frame_crc((uint32_t*)frame, RV_SLEEP_CTX_FRMSZ - 2 * sizeof(long), (uint32_t *)(&frame->frame_crc));
|
||||
|
@ -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),
|
||||
uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp_mem_inf_fpu, bool dslp)
|
||||
{
|
||||
esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_CPU_TO_MEM_START, (void *)0);
|
||||
uint32_t mstatus = save_mstatus_and_disable_global_int();
|
||||
|
||||
cpu_domain_dev_regs_save(s_cpu_retention.retent.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.clint_frame);
|
||||
cpu_domain_dev_regs_restore(s_cpu_retention.retent.plic_frame);
|
||||
|
||||
restore_mstatus(mstatus);
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -166,9 +166,16 @@ wait_sync_done:
|
|||
|
||||
.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
|
||||
|
|
|
@ -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
|
|
@ -14,6 +14,7 @@
|
|||
#include "esp_sleep.h"
|
||||
#include "esp_private/esp_sleep_internal.h"
|
||||
#include "esp_private/esp_timer_private.h"
|
||||
#include "esp_private/sleep_event.h"
|
||||
#include "esp_private/system_internal.h"
|
||||
#include "esp_log.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_PM_CPU_RETENTION_BY_SW
|
||||
esp_sleep_execute_event_callbacks(SLEEP_EVENT_HW_GOTO_SLEEP, (void *)0);
|
||||
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);
|
||||
} else {
|
||||
#endif
|
||||
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
|
||||
result = call_rtc_sleep_start(reject_triggers, config.lslp_mem_inf_fpu, deep_sleep);
|
||||
#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);
|
||||
}
|
||||
|
||||
esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_CLK_READY, (void *)0);
|
||||
|
||||
if (cpu_freq_config.source == SOC_CPU_CLK_SRC_PLL) {
|
||||
// Turn up MSPI speed if switch to PLL
|
||||
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)
|
||||
{
|
||||
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
|
||||
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();
|
||||
#endif
|
||||
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();
|
||||
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();
|
||||
|
||||
#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
|
||||
|
||||
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);
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -152,4 +152,18 @@ menu "Power Management"
|
|||
bool
|
||||
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"
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
|
||||
#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
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "esp_private/sleep_gpio.h"
|
||||
#include "esp_private/sleep_modem.h"
|
||||
#include "esp_sleep.h"
|
||||
#include "esp_memory_utils.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)
|
||||
{
|
||||
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 sleep_time_us = MIN(wakeup_delay_us, time_until_next_alarm);
|
||||
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);
|
||||
#if CONFIG_PM_TRACE && SOC_PM_SUPPORT_RTC_PERIPH_PD
|
||||
/* to force tracing GPIOs to keep state */
|
||||
|
@ -644,7 +799,7 @@ void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
|
|||
s_light_sleep_counts++;
|
||||
#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);
|
||||
|
||||
uint32_t slept_ticks = slept_us / (portTICK_PERIOD_MS * 1000LL);
|
||||
|
@ -667,6 +822,12 @@ void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
|
|||
#endif
|
||||
}
|
||||
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);
|
||||
|
|
Ładowanie…
Reference in New Issue