Skip to content

Commit

Permalink
Merge branch 'bringup/support_callback_mechanism_in_lightsleep_flow_v…
Browse files Browse the repository at this point in the history
…5.1' into 'release/v5.1'

feat(pm): support callback mechanism in lightsleep flow(backport v5.1)

See merge request espressif/esp-idf!26365
  • Loading branch information
jack0c committed Nov 14, 2023
2 parents 0172c33 + 42ec7a5 commit cee24a6
Show file tree
Hide file tree
Showing 12 changed files with 493 additions and 5 deletions.
1 change: 1 addition & 0 deletions components/esp_hw_support/CMakeLists.txt
Expand Up @@ -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"
Expand Down
14 changes: 14 additions & 0 deletions components/esp_hw_support/Kconfig
Expand Up @@ -161,6 +161,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"
Expand Down
120 changes: 120 additions & 0 deletions components/esp_hw_support/include/esp_private/sleep_event.h
@@ -0,0 +1,120 @@
/*
* 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 esp_err_t (*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 None
*/
void esp_sleep_execute_event_callbacks(esp_sleep_event_cb_index_t event_id, void *ext_arg);

#ifdef __cplusplus
}
#endif
7 changes: 7 additions & 0 deletions components/esp_hw_support/port/esp32c6/rtc_clk.c
Expand Up @@ -19,6 +19,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"
Expand Down Expand Up @@ -252,6 +253,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();
Expand All @@ -263,10 +268,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) {
Expand Down
9 changes: 9 additions & 0 deletions components/esp_hw_support/port/esp32h2/rtc_clk.c
Expand Up @@ -20,6 +20,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"
Expand Down Expand Up @@ -297,6 +298,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();
Expand All @@ -308,10 +313,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) &&
Expand All @@ -322,10 +329,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);
}
}

Expand Down
4 changes: 3 additions & 1 deletion components/esp_hw_support/sleep_cpu.c
Expand Up @@ -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
Expand Down Expand Up @@ -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));
Expand All @@ -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);
Expand All @@ -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;
}
Expand Down
7 changes: 7 additions & 0 deletions components/esp_hw_support/sleep_cpu_asm.S
Expand Up @@ -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
Expand Down
92 changes: 92 additions & 0 deletions components/esp_hw_support/sleep_event.c
@@ -0,0 +1,92 @@
/*
* 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"

static __attribute__((unused)) const char *TAG = "sleep_event";

#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

void IRAM_ATTR esp_sleep_execute_event_callbacks(esp_sleep_event_cb_index_t event_id, void *ext_arg)
{
#if CONFIG_ESP_SLEEP_EVENT_CALLBACKS
if (event_id >= SLEEP_EVENT_CB_INDEX_NUM) {
ESP_EARLY_LOGW(TAG, "event_id out of range");
return;
}
esp_sleep_event_cb_config_t *current = g_sleep_event_cbs_config.sleep_event_cb_config[event_id];
while (current != NULL) {
if (current->cb != NULL) {
if (ESP_OK != (*current->cb)(current->user_arg, ext_arg)) {
ESP_EARLY_LOGW(TAG, "esp_sleep_execute_event_callbacks has an err, current->cb = %p", current->cb);
}
}
current = current->next;
}
#endif
}

0 comments on commit cee24a6

Please sign in to comment.