feat(sleep_retention): allow drivers taking TOP power lock

Also add a dump function
This commit is contained in:
Xiao Xufeng 2024-09-26 19:58:20 +08:00
parent 116c3d6ddb
commit df2d09e3e0
17 changed files with 241 additions and 0 deletions

View File

@ -59,6 +59,11 @@ typedef enum {
*/
esp_err_t sleep_retention_entries_create(const sleep_retention_entries_config_t retent[], int num, regdma_link_priority_t priority, sleep_retention_module_t module);
/**
* @brief Dump the initialization status of all modules.
*/
void sleep_retention_dump_modules(FILE *out);
/**
* @brief Dump all runtime sleep retention linked lists
*/
@ -139,6 +144,23 @@ esp_err_t sleep_retention_module_allocate(sleep_retention_module_t module);
*/
esp_err_t sleep_retention_module_free(sleep_retention_module_t module);
/**
* @brief Force take the power lock so that during sleep the power domain won't be powered off.
*
* @return
* - ESP_OK if success
* - other value when the internal `sleep_retention_module_init` fails.
*/
esp_err_t sleep_retention_power_lock_acquire(void);
/**
* @brief Release the power lock so that the peripherals' power domain can be powered off.
* Please note that there is an internal reference counter and the power domain will be kept on until same number
* of `sleep_retention_power_lock_release` is called as `sleep_retention_power_lock_acquire`.
* @return always ESP_OK
*/
esp_err_t sleep_retention_power_lock_release(void);
/**
* @brief Get all initialized modules that require sleep retention
*

View File

@ -29,6 +29,8 @@
#endif
static __attribute__((unused)) const char *TAG = "sleep";
static int acquire_cnt; //for the force acquire lock
struct sleep_retention_module_object {
sleep_retention_module_callbacks_t cbs; /* A callback list that can extend more sleep retention event callbacks */
@ -319,6 +321,23 @@ static void sleep_retention_entries_stats(void)
_lock_release_recursive(&s_retention.lock);
}
void sleep_retention_dump_modules(FILE *out)
{
uint32_t inited_modules = sleep_retention_get_inited_modules();
uint32_t created_modules = sleep_retention_get_created_modules();
for (int i = SLEEP_RETENTION_MODULE_MIN; i <= SLEEP_RETENTION_MODULE_MAX; i++) {
bool inited = (inited_modules & BIT(i)) != 0;
bool created = (created_modules & BIT(i)) != 0;
bool is_top = (TOP_DOMAIN_PERIPHERALS_BM & BIT(i)) != 0;
const char* status = !inited? "-":
created? "CREATED":
"INITED";
const char* domain = is_top? "TOP": "-";
fprintf(out, "%2d: %4s %8s\n", i, domain, status);
}
}
void sleep_retention_dump_entries(FILE *out)
{
_lock_acquire_recursive(&s_retention.lock);
@ -820,6 +839,42 @@ esp_err_t sleep_retention_module_free(sleep_retention_module_t module)
return err;
}
static esp_err_t empty_create(void *args)
{
return ESP_OK;
}
esp_err_t sleep_retention_power_lock_acquire(void)
{
_lock_acquire_recursive(&s_retention.lock);
if (acquire_cnt == 0) {
sleep_retention_module_init_param_t init_param = {
.cbs = { .create = {.handle = empty_create},},
};
esp_err_t ret = sleep_retention_module_init(SLEEP_RETENTION_MODULE_NULL, &init_param);
if (ret != ESP_OK) {
_lock_release_recursive(&s_retention.lock);
return ret;
}
}
acquire_cnt++;
_lock_release_recursive(&s_retention.lock);
return ESP_OK;
}
esp_err_t sleep_retention_power_lock_release(void)
{
esp_err_t ret = ESP_OK;
_lock_acquire_recursive(&s_retention.lock);
acquire_cnt--;
assert(acquire_cnt >= 0);
if (acquire_cnt == 0) {
ret = sleep_retention_module_deinit(SLEEP_RETENTION_MODULE_NULL);
}
_lock_release_recursive(&s_retention.lock);
return ret;
}
void IRAM_ATTR sleep_retention_do_extra_retention(bool backup_or_restore)
{
if (s_retention.highpri < SLEEP_RETENTION_REGDMA_LINK_HIGHEST_PRIORITY ||

View File

@ -42,6 +42,11 @@ components/esp_hw_support/test_apps/rtc_power_modes:
temporary: true
reason: the other targets are not tested yet
components/esp_hw_support/test_apps/sleep_retention:
enable:
- if: SOC_PAU_SUPPORTED == 1 and CONFIG_NAME != "xip_psram"
- if: SOC_PAU_SUPPORTED == 1 and (SOC_SPIRAM_XIP_SUPPORTED == 1 and CONFIG_NAME == "xip_psram")
components/esp_hw_support/test_apps/vad_wakeup:
disable:
- if: SOC_LP_VAD_SUPPORTED != 1

View File

@ -0,0 +1,10 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on. We also depend on esp_psram
# as we set CONFIG_SPIRAM_... options.
set(COMPONENTS main esp_psram)
project(test_retention)

View File

@ -0,0 +1,2 @@
| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 |
| ----------------- | -------- | -------- | --------- | -------- | -------- |

View File

@ -0,0 +1,7 @@
set(srcs "test_app_main.c" "test_retention.c")
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
# the component can be registered as WHOLE_ARCHIVE
idf_component_register(SRCS ${srcs}
PRIV_REQUIRES unity esp_mm esp_psram
WHOLE_ARCHIVE)

View File

@ -0,0 +1,40 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "unity.h"
#include "unity_test_runner.h"
#include "esp_heap_caps.h"
#define TEST_MEMORY_LEAK_THRESHOLD (-300)
static size_t before_free_8bit;
static size_t before_free_32bit;
static void check_leak(size_t before_free, size_t after_free, const char *type)
{
ssize_t delta = after_free - before_free;
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
}
void setUp(void)
{
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
}
void tearDown(void)
{
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
check_leak(before_free_8bit, after_free_8bit, "8BIT");
check_leak(before_free_32bit, after_free_32bit, "32BIT");
}
void app_main(void)
{
unity_run_menu();
}

View File

@ -0,0 +1,37 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "unity.h"
#include "esp_private/sleep_sys_periph.h"
#include "esp_private/sleep_retention.h"
#include "esp_sleep.h"
#include "esp_private/sleep_cpu.h"
const char TAG[] = "retention";
TEST_CASE("retention: can go to retention", "[retention]")
{
// Prepare a TOP PD sleep
TEST_ESP_OK(esp_sleep_enable_timer_wakeup(1 * 1000 * 1000));
sleep_cpu_configure(true);
TEST_ASSERT_EQUAL_INT32(true, peripheral_domain_pd_allowed());
sleep_retention_dump_modules(stdout);
vTaskDelay(1000/portTICK_PERIOD_MS);
ESP_LOGI(TAG, "Going to sleep...");
esp_light_sleep_start();
ESP_LOGI(TAG, "After wakeup");
TEST_ASSERT_EQUAL_INT32(true, peripheral_domain_pd_allowed());
sleep_retention_dump_modules(stdout);
sleep_cpu_configure(false);
}

View File

@ -0,0 +1,35 @@
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import functools
from typing import Callable
from typing import Dict
from typing import List
import pytest
from pytest_embedded import Dut
def target_list(targets: List[str]) -> Callable:
def decorator(func: Callable) -> Callable:
@functools.wraps(func)
def wrapper(*args: List, **kwargs: Dict) -> Callable:
return func(*args, **kwargs) # type: ignore
for target in targets:
wrapper = pytest.mark.__getattr__(target)(wrapper)
return wrapper
return decorator
# SOC_PAU_SUPPORTED == 1
retention_targets = ['esp32c6', 'esp32h2', 'esp32p4', 'esp32c5', 'esp32c61']
@target_list(retention_targets)
@pytest.mark.generic
def test_sleep_retention(dut: Dut) -> None:
dut.run_all_single_board_cases()

View File

@ -0,0 +1,2 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_XIP_FROM_PSRAM=y

View File

@ -0,0 +1,6 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT=n
CONFIG_PM_ENABLE=y
CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP=y
CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y

View File

@ -15,6 +15,7 @@ extern "C" {
typedef enum periph_retention_module {
SLEEP_RETENTION_MODULE_MIN = 0,
SLEEP_RETENTION_MODULE_NULL = SLEEP_RETENTION_MODULE_MIN, /* This module is for all peripherals that can't survive from PD_TOP to call init only. Shouldn't have any dependency. */
/* clock module, which includes system and modem */
SLEEP_RETENTION_MODULE_CLOCK_SYSTEM = 1,
SLEEP_RETENTION_MODULE_CLOCK_MODEM = 2,
@ -55,6 +56,8 @@ typedef enum periph_retention_module {
} periph_retention_module_t;
typedef enum periph_retention_module_bitmap {
SLEEP_RETENTION_MODULE_BM_NULL = BIT(SLEEP_RETENTION_MODULE_NULL),
/* clock module, which includes system and modem */
SLEEP_RETENTION_MODULE_BM_CLOCK_SYSTEM = BIT(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM),
SLEEP_RETENTION_MODULE_BM_CLOCK_MODEM = BIT(SLEEP_RETENTION_MODULE_CLOCK_MODEM),
@ -115,6 +118,7 @@ typedef enum periph_retention_module_bitmap {
| SLEEP_RETENTION_MODULE_BM_GPSPI2 \
| SLEEP_RETENTION_MODULE_BM_LEDC \
| SLEEP_RETENTION_MODULE_BM_PCNT0 \
| SLEEP_RETENTION_MODULE_BM_NULL \
)
#ifdef __cplusplus
}

View File

@ -15,6 +15,7 @@ extern "C" {
typedef enum periph_retention_module {
SLEEP_RETENTION_MODULE_MIN = 0,
SLEEP_RETENTION_MODULE_NULL = SLEEP_RETENTION_MODULE_MIN, /* This module is for all peripherals that can't survive from PD_TOP to call init only. Shouldn't have any dependency. */
/* clock module, which includes system and modem */
SLEEP_RETENTION_MODULE_CLOCK_SYSTEM = 1,
SLEEP_RETENTION_MODULE_CLOCK_MODEM = 2,
@ -56,6 +57,8 @@ typedef enum periph_retention_module {
} periph_retention_module_t;
typedef enum periph_retention_module_bitmap {
SLEEP_RETENTION_MODULE_BM_NULL = BIT(SLEEP_RETENTION_MODULE_NULL),
/* clock module, which includes system and modem */
SLEEP_RETENTION_MODULE_BM_CLOCK_SYSTEM = BIT(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM),
SLEEP_RETENTION_MODULE_BM_CLOCK_MODEM = BIT(SLEEP_RETENTION_MODULE_CLOCK_MODEM),
@ -117,6 +120,7 @@ typedef enum periph_retention_module_bitmap {
| SLEEP_RETENTION_MODULE_BM_GPSPI2 \
| SLEEP_RETENTION_MODULE_BM_LEDC \
| SLEEP_RETENTION_MODULE_BM_PCNT0 \
| SLEEP_RETENTION_MODULE_BM_NULL \
)
#ifdef __cplusplus

View File

@ -15,6 +15,7 @@ extern "C" {
typedef enum periph_retention_module {
SLEEP_RETENTION_MODULE_MIN = 0,
SLEEP_RETENTION_MODULE_NULL = SLEEP_RETENTION_MODULE_MIN, /* This module is for all peripherals that can't survive from PD_TOP to call init only. Shouldn't have any dependency. */
/* clock module, which includes system and modem */
SLEEP_RETENTION_MODULE_CLOCK_SYSTEM = 1,
SLEEP_RETENTION_MODULE_CLOCK_MODEM = 2,
@ -48,6 +49,8 @@ typedef enum periph_retention_module {
} periph_retention_module_t;
typedef enum periph_retention_module_bitmap {
SLEEP_RETENTION_MODULE_BM_NULL = BIT(SLEEP_RETENTION_MODULE_NULL),
/* clock module, which includes system and modem */
SLEEP_RETENTION_MODULE_BM_CLOCK_SYSTEM = BIT(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM),
SLEEP_RETENTION_MODULE_BM_CLOCK_MODEM = BIT(SLEEP_RETENTION_MODULE_CLOCK_MODEM),
@ -93,6 +96,7 @@ typedef enum periph_retention_module_bitmap {
| SLEEP_RETENTION_MODULE_BM_GPSPI2 \
| SLEEP_RETENTION_MODULE_BM_LEDC \
| SLEEP_RETENTION_MODULE_BM_I2S0 \
| SLEEP_RETENTION_MODULE_BM_NULL \
)
#ifdef __cplusplus

View File

@ -15,6 +15,7 @@ extern "C" {
typedef enum periph_retention_module {
SLEEP_RETENTION_MODULE_MIN = 0,
SLEEP_RETENTION_MODULE_NULL = SLEEP_RETENTION_MODULE_MIN, /* This module is for all peripherals that can't survive from PD_TOP to call init only. Shouldn't have any dependency. */
/* clock module, which includes system and modem */
SLEEP_RETENTION_MODULE_CLOCK_SYSTEM = 1,
SLEEP_RETENTION_MODULE_CLOCK_MODEM = 2,
@ -54,6 +55,8 @@ typedef enum periph_retention_module {
} periph_retention_module_t;
typedef enum periph_retention_module_bitmap {
SLEEP_RETENTION_MODULE_BM_NULL = BIT(SLEEP_RETENTION_MODULE_NULL),
/* clock module, which includes system and modem */
SLEEP_RETENTION_MODULE_BM_CLOCK_SYSTEM = BIT(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM),
SLEEP_RETENTION_MODULE_BM_CLOCK_MODEM = BIT(SLEEP_RETENTION_MODULE_CLOCK_MODEM),
@ -113,6 +116,7 @@ typedef enum periph_retention_module_bitmap {
| SLEEP_RETENTION_MODULE_BM_GPSPI2 \
| SLEEP_RETENTION_MODULE_BM_LEDC \
| SLEEP_RETENTION_MODULE_BM_PCNT0 \
| SLEEP_RETENTION_MODULE_BM_NULL \
)
#ifdef __cplusplus

View File

@ -15,6 +15,7 @@ extern "C" {
typedef enum periph_retention_module {
SLEEP_RETENTION_MODULE_MIN = 0,
SLEEP_RETENTION_MODULE_NULL = SLEEP_RETENTION_MODULE_MIN, /* This module is for all peripherals that can't survive from PD_TOP to call init only. Shouldn't have any dependency. */
/* clock module, which includes system and modem */
SLEEP_RETENTION_MODULE_CLOCK_SYSTEM = 1,
/* digital peripheral module, which includes Interrupt Matrix, HP_SYSTEM,
@ -58,6 +59,8 @@ typedef enum periph_retention_module {
} periph_retention_module_t;
typedef enum periph_retention_module_bitmap {
SLEEP_RETENTION_MODULE_BM_NULL = BIT(SLEEP_RETENTION_MODULE_NULL),
/* clock module, which includes system and modem */
SLEEP_RETENTION_MODULE_BM_CLOCK_SYSTEM = BIT(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM),
/* digital peripheral module, which includes Interrupt Matrix, HP_SYSTEM,
@ -130,6 +133,7 @@ typedef enum periph_retention_module_bitmap {
| SLEEP_RETENTION_MODULE_BM_GPSPI2 \
| SLEEP_RETENTION_MODULE_BM_GPSPI3 \
| SLEEP_RETENTION_MODULE_BM_LEDC \
| SLEEP_RETENTION_MODULE_BM_NULL \
)
#ifdef __cplusplus