Skip to content

Commit 802e7b2

Browse files
committed
feat(sleep_retention): allow drivers taking TOP power lock
Also add a dump function
1 parent a2ffd9e commit 802e7b2

File tree

17 files changed

+241
-0
lines changed

17 files changed

+241
-0
lines changed

components/esp_hw_support/include/esp_private/sleep_retention.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ typedef enum {
5959
*/
6060
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);
6161

62+
/**
63+
* @brief Dump the initialization status of all modules.
64+
*/
65+
void sleep_retention_dump_modules(FILE *out);
66+
6267
/**
6368
* @brief Dump all runtime sleep retention linked lists
6469
*/
@@ -139,6 +144,23 @@ esp_err_t sleep_retention_module_allocate(sleep_retention_module_t module);
139144
*/
140145
esp_err_t sleep_retention_module_free(sleep_retention_module_t module);
141146

147+
/**
148+
* @brief Force take the power lock so that during sleep the power domain won't be powered off.
149+
*
150+
* @return
151+
* - ESP_OK if success
152+
* - other value when the internal `sleep_retention_module_init` fails.
153+
*/
154+
esp_err_t sleep_retention_power_lock_acquire(void);
155+
156+
/**
157+
* @brief Release the power lock so that the peripherals' power domain can be powered off.
158+
* Please note that there is an internal reference counter and the power domain will be kept on until same number
159+
* of `sleep_retention_power_lock_release` is called as `sleep_retention_power_lock_acquire`.
160+
* @return always ESP_OK
161+
*/
162+
esp_err_t sleep_retention_power_lock_release(void);
163+
142164
/**
143165
* @brief Get all initialized modules that require sleep retention
144166
*

components/esp_hw_support/sleep_retention.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
#endif
3030

3131
static __attribute__((unused)) const char *TAG = "sleep";
32+
static int acquire_cnt; //for the force acquire lock
33+
3234

3335
struct sleep_retention_module_object {
3436
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)
319321
_lock_release_recursive(&s_retention.lock);
320322
}
321323

324+
void sleep_retention_dump_modules(FILE *out)
325+
{
326+
uint32_t inited_modules = sleep_retention_get_inited_modules();
327+
uint32_t created_modules = sleep_retention_get_created_modules();
328+
for (int i = SLEEP_RETENTION_MODULE_MIN; i <= SLEEP_RETENTION_MODULE_MAX; i++) {
329+
bool inited = (inited_modules & BIT(i)) != 0;
330+
bool created = (created_modules & BIT(i)) != 0;
331+
bool is_top = (TOP_DOMAIN_PERIPHERALS_BM & BIT(i)) != 0;
332+
333+
const char* status = !inited? "-":
334+
created? "CREATED":
335+
"INITED";
336+
const char* domain = is_top? "TOP": "-";
337+
fprintf(out, "%2d: %4s %8s\n", i, domain, status);
338+
}
339+
}
340+
322341
void sleep_retention_dump_entries(FILE *out)
323342
{
324343
_lock_acquire_recursive(&s_retention.lock);
@@ -820,6 +839,42 @@ esp_err_t sleep_retention_module_free(sleep_retention_module_t module)
820839
return err;
821840
}
822841

842+
static esp_err_t empty_create(void *args)
843+
{
844+
return ESP_OK;
845+
}
846+
847+
esp_err_t sleep_retention_power_lock_acquire(void)
848+
{
849+
_lock_acquire_recursive(&s_retention.lock);
850+
if (acquire_cnt == 0) {
851+
sleep_retention_module_init_param_t init_param = {
852+
.cbs = { .create = {.handle = empty_create},},
853+
};
854+
esp_err_t ret = sleep_retention_module_init(SLEEP_RETENTION_MODULE_NULL, &init_param);
855+
if (ret != ESP_OK) {
856+
_lock_release_recursive(&s_retention.lock);
857+
return ret;
858+
}
859+
}
860+
acquire_cnt++;
861+
_lock_release_recursive(&s_retention.lock);
862+
return ESP_OK;
863+
}
864+
865+
esp_err_t sleep_retention_power_lock_release(void)
866+
{
867+
esp_err_t ret = ESP_OK;
868+
_lock_acquire_recursive(&s_retention.lock);
869+
acquire_cnt--;
870+
assert(acquire_cnt >= 0);
871+
if (acquire_cnt == 0) {
872+
ret = sleep_retention_module_deinit(SLEEP_RETENTION_MODULE_NULL);
873+
}
874+
_lock_release_recursive(&s_retention.lock);
875+
return ret;
876+
}
877+
823878
void IRAM_ATTR sleep_retention_do_extra_retention(bool backup_or_restore)
824879
{
825880
if (s_retention.highpri < SLEEP_RETENTION_REGDMA_LINK_HIGHEST_PRIORITY ||

components/esp_hw_support/test_apps/.build-test-rules.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ components/esp_hw_support/test_apps/rtc_power_modes:
4242
temporary: true
4343
reason: the other targets are not tested yet
4444

45+
components/esp_hw_support/test_apps/sleep_retention:
46+
enable:
47+
- if: SOC_PAU_SUPPORTED == 1 and CONFIG_NAME != "xip_psram"
48+
- if: SOC_PAU_SUPPORTED == 1 and (SOC_SPIRAM_XIP_SUPPORTED == 1 and CONFIG_NAME == "xip_psram")
49+
4550
components/esp_hw_support/test_apps/vad_wakeup:
4651
disable:
4752
- if: SOC_LP_VAD_SUPPORTED != 1
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# This is the project CMakeLists.txt file for the test subproject
2+
cmake_minimum_required(VERSION 3.16)
3+
4+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
5+
6+
# "Trim" the build. Include the minimal set of components, main, and anything it depends on. We also depend on esp_psram
7+
# as we set CONFIG_SPIRAM_... options.
8+
set(COMPONENTS main esp_psram)
9+
10+
project(test_retention)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 |
2+
| ----------------- | -------- | -------- | --------- | -------- | -------- |
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
set(srcs "test_app_main.c" "test_retention.c")
2+
3+
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
4+
# the component can be registered as WHOLE_ARCHIVE
5+
idf_component_register(SRCS ${srcs}
6+
PRIV_REQUIRES unity esp_mm esp_psram
7+
WHOLE_ARCHIVE)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include "unity.h"
8+
#include "unity_test_runner.h"
9+
#include "esp_heap_caps.h"
10+
11+
#define TEST_MEMORY_LEAK_THRESHOLD (-300)
12+
13+
static size_t before_free_8bit;
14+
static size_t before_free_32bit;
15+
16+
static void check_leak(size_t before_free, size_t after_free, const char *type)
17+
{
18+
ssize_t delta = after_free - before_free;
19+
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
20+
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
21+
}
22+
23+
void setUp(void)
24+
{
25+
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
26+
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
27+
}
28+
29+
void tearDown(void)
30+
{
31+
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
32+
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
33+
check_leak(before_free_8bit, after_free_8bit, "8BIT");
34+
check_leak(before_free_32bit, after_free_32bit, "32BIT");
35+
}
36+
37+
void app_main(void)
38+
{
39+
unity_run_menu();
40+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include "freertos/FreeRTOS.h"
8+
#include "freertos/task.h"
9+
#include "esp_log.h"
10+
#include "sdkconfig.h"
11+
#include "unity.h"
12+
#include "esp_private/sleep_sys_periph.h"
13+
#include "esp_private/sleep_retention.h"
14+
#include "esp_sleep.h"
15+
#include "esp_private/sleep_cpu.h"
16+
17+
const char TAG[] = "retention";
18+
19+
TEST_CASE("retention: can go to retention", "[retention]")
20+
{
21+
// Prepare a TOP PD sleep
22+
TEST_ESP_OK(esp_sleep_enable_timer_wakeup(1 * 1000 * 1000));
23+
sleep_cpu_configure(true);
24+
25+
TEST_ASSERT_EQUAL_INT32(true, peripheral_domain_pd_allowed());
26+
sleep_retention_dump_modules(stdout);
27+
vTaskDelay(1000/portTICK_PERIOD_MS);
28+
29+
ESP_LOGI(TAG, "Going to sleep...");
30+
esp_light_sleep_start();
31+
32+
ESP_LOGI(TAG, "After wakeup");
33+
TEST_ASSERT_EQUAL_INT32(true, peripheral_domain_pd_allowed());
34+
sleep_retention_dump_modules(stdout);
35+
36+
sleep_cpu_configure(false);
37+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
2+
# SPDX-License-Identifier: CC0-1.0
3+
import functools
4+
from typing import Callable
5+
from typing import Dict
6+
from typing import List
7+
8+
import pytest
9+
from pytest_embedded import Dut
10+
11+
12+
def target_list(targets: List[str]) -> Callable:
13+
14+
def decorator(func: Callable) -> Callable:
15+
16+
@functools.wraps(func)
17+
def wrapper(*args: List, **kwargs: Dict) -> Callable:
18+
return func(*args, **kwargs) # type: ignore
19+
20+
for target in targets:
21+
wrapper = pytest.mark.__getattr__(target)(wrapper)
22+
23+
return wrapper
24+
25+
return decorator
26+
27+
28+
# SOC_PAU_SUPPORTED == 1
29+
retention_targets = ['esp32c6', 'esp32h2', 'esp32p4', 'esp32c5', 'esp32c61']
30+
31+
32+
@target_list(retention_targets)
33+
@pytest.mark.generic
34+
def test_sleep_retention(dut: Dut) -> None:
35+
dut.run_all_single_board_cases()

components/esp_hw_support/test_apps/sleep_retention/sdkconfig.ci.defaults

Whitespace-only changes.

0 commit comments

Comments
 (0)