Skip to content

Commit 3e06117

Browse files
committed
fix(sdio_slave): prevents peripheral power domain being powered off when SDIO slave in use
1 parent 802e7b2 commit 3e06117

File tree

9 files changed

+133
-51
lines changed

9 files changed

+133
-51
lines changed

components/esp_driver_sdio/src/sdio_slave.c

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,20 +76,24 @@ The driver of FIFOs works as below:
7676
*/
7777

7878
#include <string.h>
79-
#include "driver/sdio_slave.h"
80-
#include "soc/sdio_slave_periph.h"
81-
#include "esp_log.h"
82-
#include "esp_intr_alloc.h"
83-
#include "freertos/FreeRTOS.h"
79+
8480
#include "soc/soc_memory_layout.h"
8581
#include "soc/gpio_periph.h"
8682
#include "soc/soc_caps.h"
83+
#include "soc/sdio_slave_periph.h"
8784
#include "esp_cpu.h"
85+
#include "esp_intr_alloc.h"
86+
#include "esp_log.h"
87+
#include "hal/sdio_slave_hal.h"
88+
#include "hal/gpio_hal.h"
89+
#include "freertos/FreeRTOS.h"
8890
#include "freertos/semphr.h"
8991
#include "esp_private/periph_ctrl.h"
92+
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
93+
#include "esp_private/sleep_retention.h"
94+
#endif
9095
#include "driver/gpio.h"
91-
#include "hal/sdio_slave_hal.h"
92-
#include "hal/gpio_hal.h"
96+
#include "driver/sdio_slave.h"
9397

9498
#define SDIO_SLAVE_CHECK(res, str, ret_val) do { if(!(res)){\
9599
SDIO_SLAVE_LOGE("%s", str);\
@@ -365,6 +369,13 @@ esp_err_t sdio_slave_initialize(sdio_slave_config_t *config)
365369
}
366370
context.intr_handle = intr_handle;
367371

372+
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
373+
r = sleep_retention_power_lock_acquire();
374+
if (r != ESP_OK) {
375+
return r;
376+
}
377+
#endif
378+
368379
r = sdio_slave_hw_init(config);
369380
if (r != ESP_OK) {
370381
return r;
@@ -378,6 +389,11 @@ void sdio_slave_deinit(void)
378389
{
379390
sdio_slave_hw_deinit();
380391

392+
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
393+
esp_err_t r = sleep_retention_power_lock_release();
394+
assert(r == ESP_OK);
395+
#endif
396+
381397
//unregister all buffers registered but returned (not loaded)
382398
recv_desc_t *temp_desc;
383399
recv_desc_t *desc;

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
components/esp_driver_sdio/test_apps/sdio/sdio_common_tests/host_sdmmc:
22
enable:
33
- if: IDF_TARGET == "esp32"
4-
temporary: false
54
reason: always use ESP32 SDMMC as host
65
depends_components:
76
- sdmmc
87
- esp_driver_sdmmc
98
- esp_driver_sdio
109

1110
components/esp_driver_sdio/test_apps/sdio/sdio_common_tests/sdio:
11+
enable:
12+
# There is no retention support for SDIO slave, just build and test if driver can forbid from auto light sleep.
13+
- if: CONFIG_NAME == "sleep_retention" and SOC_PAU_SUPPORTED == 1
14+
- if: CONFIG_NAME != "sleep_retention"
1215
disable:
1316
- if: SOC_SDIO_SLAVE_SUPPORTED != 1
1417
depends_components:

components/esp_driver_sdio/test_apps/sdio/sdio_common_tests/host_sdmmc/main/test_sdio_sdhost.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,20 @@ TEST_CASE("SDIO_SDMMC: test to host", "[sdio]")
418418
test_to_host(true);
419419
}
420420

421+
TEST_CASE("SDIO_SDMMC: test sleep retention", "[sdio_retention]")
422+
{
423+
essl_handle_t handle = NULL;
424+
test_sdio_param_t test_param = {
425+
.host_flags = SDMMC_HOST_FLAG_4BIT | SDMMC_HOST_FLAG_ALLOC_ALIGNED_BUF,
426+
.max_freq_khz = SDMMC_FREQ_HIGHSPEED,
427+
};
428+
//essl init and sdmmc init
429+
s_master_init(&test_param, &handle, NULL);
430+
431+
s_send_finish_test(handle);
432+
s_master_deinit();
433+
}
434+
421435
TEST_CASE("SDIO_SDMMC: test to host (Performance)", "[sdio_speed]")
422436
{
423437
test_to_host(false);

components/esp_driver_sdio/test_apps/sdio/sdio_common_tests/pytest_sdio.py

Lines changed: 46 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,30 @@
1-
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
1+
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
22
# SPDX-License-Identifier: CC0-1.0
3-
43
import os.path
4+
from typing import List
55
from typing import Tuple
66

77
import pytest
88
from pytest_embedded_idf import IdfDut
99

1010

11+
def parameter_expand(existing_parameters: List[List[str]], value_list: List[str]) -> List[List[str]]:
12+
ret = []
13+
for param in existing_parameters:
14+
ret.extend([param + [value] for value in value_list])
15+
16+
return ret
17+
18+
19+
esp32_32_param = [[f'{os.path.join(os.path.dirname(__file__), "host_sdmmc")}|{os.path.join(os.path.dirname(__file__), "sdio")}', 'esp32|esp32']]
20+
esp32_c6_param = [[f'{os.path.join(os.path.dirname(__file__), "host_sdmmc")}|{os.path.join(os.path.dirname(__file__), "sdio")}', 'esp32|esp32c6']]
21+
22+
esp32_param_default = [pytest.param(*param) for param in parameter_expand(esp32_32_param, ['default|default'])]
23+
c6_param_default = [pytest.param(*param) for param in parameter_expand(esp32_c6_param, ['default|default'])]
24+
25+
c6_param_retention = [pytest.param(*param) for param in parameter_expand(esp32_c6_param, ['default|sleep_retention'])]
26+
27+
1128
# Normal tests
1229
def test_sdio_flow(dut:Tuple[IdfDut, IdfDut]) -> None:
1330
dut[1].expect('Press ENTER to see the list of tests')
@@ -24,23 +41,15 @@ def test_sdio_flow(dut:Tuple[IdfDut, IdfDut]) -> None:
2441
@pytest.mark.esp32c6
2542
@pytest.mark.sdio_multidev_32_c6
2643
@pytest.mark.parametrize('count', [2,], indirect=True)
27-
@pytest.mark.parametrize('app_path, target', [
28-
pytest.param(
29-
f'{os.path.join(os.path.dirname(__file__), "host_sdmmc")}|{os.path.join(os.path.dirname(__file__), "sdio")}',
30-
'esp32|esp32c6'),
31-
], indirect=True)
44+
@pytest.mark.parametrize('app_path, target, config', c6_param_default, indirect=True)
3245
def test_sdio_esp32_esp32c6(dut:Tuple[IdfDut, IdfDut]) -> None:
3346
test_sdio_flow(dut)
3447

3548

3649
@pytest.mark.esp32
3750
@pytest.mark.sdio_master_slave
3851
@pytest.mark.parametrize('count', [2,], indirect=True)
39-
@pytest.mark.parametrize('app_path, target', [
40-
pytest.param(
41-
f'{os.path.join(os.path.dirname(__file__), "host_sdmmc")}|{os.path.join(os.path.dirname(__file__), "sdio")}',
42-
'esp32|esp32'),
43-
], indirect=True)
52+
@pytest.mark.parametrize('app_path, target, config', esp32_param_default, indirect=True)
4453
def test_sdio_esp32_esp32(dut:Tuple[IdfDut, IdfDut]) -> None:
4554
test_sdio_flow(dut)
4655

@@ -68,23 +77,15 @@ def test_sdio_speed_frhost_flow(dut:Tuple[IdfDut, IdfDut], expected_4b_speed:int
6877
@pytest.mark.esp32c6
6978
@pytest.mark.sdio_multidev_32_c6
7079
@pytest.mark.parametrize('count', [2,], indirect=True)
71-
@pytest.mark.parametrize('app_path, target', [
72-
pytest.param(
73-
f'{os.path.join(os.path.dirname(__file__), "host_sdmmc")}|{os.path.join(os.path.dirname(__file__), "sdio")}',
74-
'esp32|esp32c6'),
75-
], indirect=True)
80+
@pytest.mark.parametrize('app_path, target, config', c6_param_default, indirect=True)
7681
def test_sdio_speed_frhost_esp32_esp32c6(dut:Tuple[IdfDut, IdfDut]) -> None:
7782
test_sdio_speed_frhost_flow(dut, 10000, 4000)
7883

7984

8085
@pytest.mark.esp32
8186
@pytest.mark.sdio_master_slave
8287
@pytest.mark.parametrize('count', [2,], indirect=True)
83-
@pytest.mark.parametrize('app_path, target', [
84-
pytest.param(
85-
f'{os.path.join(os.path.dirname(__file__), "host_sdmmc")}|{os.path.join(os.path.dirname(__file__), "sdio")}',
86-
'esp32|esp32'),
87-
], indirect=True)
88+
@pytest.mark.parametrize('app_path, target, config', esp32_param_default, indirect=True)
8889
def test_sdio_speed_frhost_esp32_esp32(dut:Tuple[IdfDut, IdfDut]) -> None:
8990
test_sdio_speed_frhost_flow(dut, 12200, 4000)
9091

@@ -112,22 +113,35 @@ def test_sdio_speed_tohost_flow(dut:Tuple[IdfDut, IdfDut], expected_4b_speed:int
112113
@pytest.mark.esp32c6
113114
@pytest.mark.sdio_multidev_32_c6
114115
@pytest.mark.parametrize('count', [2,], indirect=True)
115-
@pytest.mark.parametrize('app_path, target', [
116-
pytest.param(
117-
f'{os.path.join(os.path.dirname(__file__), "host_sdmmc")}|{os.path.join(os.path.dirname(__file__), "sdio")}',
118-
'esp32|esp32c6'),
119-
], indirect=True)
116+
@pytest.mark.parametrize('app_path, target, config', c6_param_default, indirect=True)
120117
def test_sdio_speed_tohost_esp32_esp32c6(dut:Tuple[IdfDut, IdfDut]) -> None:
121118
test_sdio_speed_tohost_flow(dut, 9000, 4000)
122119

123120

124121
@pytest.mark.esp32
125122
@pytest.mark.sdio_master_slave
126123
@pytest.mark.parametrize('count', [2,], indirect=True)
127-
@pytest.mark.parametrize('app_path, target', [
128-
pytest.param(
129-
f'{os.path.join(os.path.dirname(__file__), "host_sdmmc")}|{os.path.join(os.path.dirname(__file__), "sdio")}',
130-
'esp32|esp32'),
131-
], indirect=True)
124+
@pytest.mark.parametrize('app_path, target, config', esp32_param_default, indirect=True)
132125
def test_sdio_speed_tohost_esp32_esp32(dut:Tuple[IdfDut, IdfDut]) -> None:
133126
test_sdio_speed_tohost_flow(dut, 12200, 4000)
127+
128+
129+
# Retention tests
130+
def test_sdio_retention(dut:Tuple[IdfDut, IdfDut]) -> None:
131+
dut[1].expect('Press ENTER to see the list of tests')
132+
dut[1].write('[sdio_retention]')
133+
dut[1].expect('test_sdio: slave ready')
134+
135+
dut[0].expect('Press ENTER to see the list of tests')
136+
dut[0].write('[sdio_retention]')
137+
138+
dut[1].expect_unity_test_output()
139+
dut[0].expect_unity_test_output()
140+
141+
142+
@pytest.mark.esp32c6
143+
@pytest.mark.sdio_multidev_32_c6
144+
@pytest.mark.parametrize('count', [2,], indirect=True)
145+
@pytest.mark.parametrize('app_path, target, config', c6_param_retention, indirect=True)
146+
def test_sdio_retention_esp32_esp32c6(dut:Tuple[IdfDut, IdfDut]) -> None:
147+
test_sdio_retention(dut)

components/esp_driver_sdio/test_apps/sdio/sdio_common_tests/sdio/main/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ set(srcs "test_app_main.c"
22
"test_sdio_slave.c")
33

44
idf_component_register(SRCS ${srcs}
5-
PRIV_REQUIRES test_driver_utils driver
5+
PRIV_REQUIRES test_driver_utils driver esp_hw_support
66
WHOLE_ARCHIVE)

components/esp_driver_sdio/test_apps/sdio/sdio_common_tests/sdio/main/test_sdio_slave.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,31 @@ TEST_CASE("SDIO_Slave: test to host", "[sdio]")
305305
test_to_host();
306306
}
307307

308+
#if SOC_PAU_SUPPORTED
309+
#include "esp_private/sleep_sys_periph.h"
310+
#include "esp_private/sleep_retention.h"
311+
312+
TEST_CASE("SDIO_Slave: test sleep retention", "[sdio_retention]")
313+
{
314+
TEST_ASSERT_EQUAL_INT32(true, peripheral_domain_pd_allowed());
315+
sleep_retention_dump_modules(stdout);
316+
317+
s_slave_init(SDIO_SLAVE_SEND_STREAM);
318+
TEST_ESP_OK(sdio_slave_start());
319+
ESP_LOGI(TAG, "slave ready");
320+
321+
TEST_ASSERT_EQUAL_INT32(false, peripheral_domain_pd_allowed());
322+
sleep_retention_dump_modules(stdout);
323+
324+
wait_for_finish(&s_test_slv_ctx);
325+
326+
sdio_slave_stop();
327+
sdio_slave_deinit();
328+
329+
TEST_ASSERT_EQUAL_INT32(true, peripheral_domain_pd_allowed());
330+
}
331+
#endif
332+
308333
TEST_CASE("SDIO_Slave: test to host (Performance)", "[sdio_speed]")
309334
{
310335
test_to_host();
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
CONFIG_PM_ENABLE=y
2+
CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP=y
3+
CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y
4+
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y

docs/en/api-reference/system/power_management.rst

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ The following peripheral drivers are not aware of DFS yet. Applications need to
141141

142142
If :ref:`CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP` is enabled, when the driver initializes the peripheral, the driver will register the working register context of the peripheral to the sleep retention link. Before entering sleep, the ``REG_DMA`` peripheral reads the configuration in the sleep retention link, and back up the register context to memory according to the configuration. ``REG_DMA`` also restores context from memory to peripheral registers on wakeup.
143143

144-
Currently ESP-IDF supports Light-sleep context retention for the following peripherals:
144+
Currently ESP-IDF supports Light-sleep context retention for the following peripherals. Their context is automatically restored, or they provide some option for the user to enable this feature and goes into peripheral power down mode.
145145

146146
.. list::
147147

@@ -162,7 +162,13 @@ The following peripheral drivers are not aware of DFS yet. Applications need to
162162
:SOC_PARLIO_SUPPORT_SLEEP_RETENTION: - PARL_IO
163163
:SOC_SPI_SUPPORT_SLEEP_RETENTION: - All GPSPIs
164164

165-
The following peripherals are not yet supported:
165+
Some peripherals haven't support Light-sleep context retention, or it cannot survive from the register lose. They will prevent the power-down of peripherals even when the feature is enabled.
166+
167+
.. list::
168+
169+
:SOC_SDIO_SLAVE_SUPPORTED: - SDIO Slave
170+
171+
The following peripherals (and those not listed in any group of this section) are not yet supported. If your application uses these peripherals, they may not work well after waking up from sleep.
166172

167173
.. list::
168174

@@ -173,9 +179,6 @@ The following peripheral drivers are not aware of DFS yet. Applications need to
173179
- USB-Serial-JTAG
174180
- MCPWM
175181
- SARADC
176-
- SDIO
177-
178-
For peripherals that do not support Light-sleep context retention, if the Power management is enabled, the ``ESP_PM_NO_LIGHT_SLEEP`` lock should be held when the peripheral is working to avoid losing the working context of the peripheral when entering sleep.
179182

180183
.. note::
181184

docs/zh_CN/api-reference/system/power_management.rst

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ ESP-IDF 中集成的电源管理算法可以根据应用程序组件的需求,
141141

142142
如果在 menuconfig 中启用了 :ref:`CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP`,在初始化外设时,驱动会将外设工作的寄存器上下文注册到休眠备份链表中,在进入休眠前,``REG_DMA`` 外设会读取休眠备份链表中的配置,根据链表中的配置将外设的寄存器上下文备份至内存,``REG_DMA`` 也会在唤醒时将上下文从内存恢复到外设寄存中。
143143

144-
目前 IDF 支持以下外设的 Light-sleep 上下文备份:
144+
目前 IDF 支持以下外设的 Light-sleep 上下文备份,它们的上下文会自动恢复,或者提供了相关的选项允许用户进入外设下电模式
145145

146146
.. list::
147147

@@ -162,7 +162,13 @@ ESP-IDF 中集成的电源管理算法可以根据应用程序组件的需求,
162162
:SOC_PARLIO_SUPPORT_SLEEP_RETENTION: - PARL_IO
163163
:SOC_SPI_SUPPORT_SLEEP_RETENTION: - All GPSPIs
164164

165-
以下外设尚未支持:
165+
一些外设尚未支持睡眠上下文恢复,或者寄存器丢失后根本无法恢复。即使外设下电功能被启用,它们也会阻止外设下电的发生:
166+
167+
.. list::
168+
169+
:SOC_SDIO_SLAVE_SUPPORTED: - SDIO Slave
170+
171+
以下外设(以及一些未在本章节任意一组中列出的外设)尚未支持外设下电功能。如果您的应用使用了这些外设,它们可能无法在从睡眠中醒来后仍然正常工作:
166172

167173
.. list::
168174

@@ -173,9 +179,6 @@ ESP-IDF 中集成的电源管理算法可以根据应用程序组件的需求,
173179
- USB-Serial-JTAG
174180
- MCPWM
175181
- SARADC
176-
- SDIO
177-
178-
对于未支持 Light-sleep 上下文备份的外设,若启用了电源管理功能,应在外设工作时持有 ``ESP_PM_NO_LIGHT_SLEEP`` 锁以避免进入休眠导致外设工作上下文丢失。
179182

180183
.. note::
181184

0 commit comments

Comments
 (0)