forked from espressif/esp-idf
fix(sdio_slave): prevents peripheral power domain being powered off when SDIO slave in use
This commit is contained in:
@@ -76,20 +76,24 @@ The driver of FIFOs works as below:
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "driver/sdio_slave.h"
|
||||
#include "soc/sdio_slave_periph.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
|
||||
#include "soc/soc_memory_layout.h"
|
||||
#include "soc/gpio_periph.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/sdio_slave_periph.h"
|
||||
#include "esp_cpu.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_log.h"
|
||||
#include "hal/sdio_slave_hal.h"
|
||||
#include "hal/gpio_hal.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
|
||||
#include "esp_private/sleep_retention.h"
|
||||
#endif
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/sdio_slave.h"
|
||||
|
||||
#define SDIO_SLAVE_CHECK(res, str, ret_val) do { if(!(res)){\
|
||||
SDIO_SLAVE_LOGE("%s", str);\
|
||||
@@ -365,6 +369,13 @@ esp_err_t sdio_slave_initialize(sdio_slave_config_t *config)
|
||||
}
|
||||
context.intr_handle = intr_handle;
|
||||
|
||||
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
|
||||
r = sleep_retention_power_lock_acquire();
|
||||
if (r != ESP_OK) {
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
|
||||
r = sdio_slave_hw_init(config);
|
||||
if (r != ESP_OK) {
|
||||
return r;
|
||||
@@ -378,6 +389,11 @@ void sdio_slave_deinit(void)
|
||||
{
|
||||
sdio_slave_hw_deinit();
|
||||
|
||||
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
|
||||
esp_err_t r = sleep_retention_power_lock_release();
|
||||
assert(r == ESP_OK);
|
||||
#endif
|
||||
|
||||
//unregister all buffers registered but returned (not loaded)
|
||||
recv_desc_t *temp_desc;
|
||||
recv_desc_t *desc;
|
||||
|
@@ -1,7 +1,6 @@
|
||||
components/esp_driver_sdio/test_apps/sdio/sdio_common_tests/host_sdmmc:
|
||||
enable:
|
||||
- if: IDF_TARGET == "esp32"
|
||||
temporary: false
|
||||
reason: always use ESP32 SDMMC as host
|
||||
depends_components:
|
||||
- sdmmc
|
||||
@@ -9,6 +8,10 @@ components/esp_driver_sdio/test_apps/sdio/sdio_common_tests/host_sdmmc:
|
||||
- esp_driver_sdio
|
||||
|
||||
components/esp_driver_sdio/test_apps/sdio/sdio_common_tests/sdio:
|
||||
enable:
|
||||
# There is no retention support for SDIO slave, just build and test if driver can forbid from auto light sleep.
|
||||
- if: CONFIG_NAME == "sleep_retention" and SOC_PAU_SUPPORTED == 1
|
||||
- if: CONFIG_NAME != "sleep_retention"
|
||||
disable:
|
||||
- if: SOC_SDIO_SLAVE_SUPPORTED != 1
|
||||
depends_components:
|
||||
|
@@ -418,6 +418,20 @@ TEST_CASE("SDIO_SDMMC: test to host", "[sdio]")
|
||||
test_to_host(true);
|
||||
}
|
||||
|
||||
TEST_CASE("SDIO_SDMMC: test sleep retention", "[sdio_retention]")
|
||||
{
|
||||
essl_handle_t handle = NULL;
|
||||
test_sdio_param_t test_param = {
|
||||
.host_flags = SDMMC_HOST_FLAG_4BIT | SDMMC_HOST_FLAG_ALLOC_ALIGNED_BUF,
|
||||
.max_freq_khz = SDMMC_FREQ_HIGHSPEED,
|
||||
};
|
||||
//essl init and sdmmc init
|
||||
s_master_init(&test_param, &handle, NULL);
|
||||
|
||||
s_send_finish_test(handle);
|
||||
s_master_deinit();
|
||||
}
|
||||
|
||||
TEST_CASE("SDIO_SDMMC: test to host (Performance)", "[sdio_speed]")
|
||||
{
|
||||
test_to_host(false);
|
||||
|
@@ -1,13 +1,30 @@
|
||||
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
import os.path
|
||||
from typing import List
|
||||
from typing import Tuple
|
||||
|
||||
import pytest
|
||||
from pytest_embedded_idf import IdfDut
|
||||
|
||||
|
||||
def parameter_expand(existing_parameters: List[List[str]], value_list: List[str]) -> List[List[str]]:
|
||||
ret = []
|
||||
for param in existing_parameters:
|
||||
ret.extend([param + [value] for value in value_list])
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
esp32_32_param = [[f'{os.path.join(os.path.dirname(__file__), "host_sdmmc")}|{os.path.join(os.path.dirname(__file__), "sdio")}', 'esp32|esp32']]
|
||||
esp32_c6_param = [[f'{os.path.join(os.path.dirname(__file__), "host_sdmmc")}|{os.path.join(os.path.dirname(__file__), "sdio")}', 'esp32|esp32c6']]
|
||||
|
||||
esp32_param_default = [pytest.param(*param) for param in parameter_expand(esp32_32_param, ['default|default'])]
|
||||
c6_param_default = [pytest.param(*param) for param in parameter_expand(esp32_c6_param, ['default|default'])]
|
||||
|
||||
c6_param_retention = [pytest.param(*param) for param in parameter_expand(esp32_c6_param, ['default|sleep_retention'])]
|
||||
|
||||
|
||||
# Normal tests
|
||||
def test_sdio_flow(dut:Tuple[IdfDut, IdfDut]) -> None:
|
||||
dut[1].expect('Press ENTER to see the list of tests')
|
||||
@@ -24,11 +41,7 @@ def test_sdio_flow(dut:Tuple[IdfDut, IdfDut]) -> None:
|
||||
@pytest.mark.esp32c6
|
||||
@pytest.mark.sdio_multidev_32_c6
|
||||
@pytest.mark.parametrize('count', [2,], indirect=True)
|
||||
@pytest.mark.parametrize('app_path, target', [
|
||||
pytest.param(
|
||||
f'{os.path.join(os.path.dirname(__file__), "host_sdmmc")}|{os.path.join(os.path.dirname(__file__), "sdio")}',
|
||||
'esp32|esp32c6'),
|
||||
], indirect=True)
|
||||
@pytest.mark.parametrize('app_path, target, config', c6_param_default, indirect=True)
|
||||
def test_sdio_esp32_esp32c6(dut:Tuple[IdfDut, IdfDut]) -> None:
|
||||
test_sdio_flow(dut)
|
||||
|
||||
@@ -36,11 +49,7 @@ def test_sdio_esp32_esp32c6(dut:Tuple[IdfDut, IdfDut]) -> None:
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.sdio_master_slave
|
||||
@pytest.mark.parametrize('count', [2,], indirect=True)
|
||||
@pytest.mark.parametrize('app_path, target', [
|
||||
pytest.param(
|
||||
f'{os.path.join(os.path.dirname(__file__), "host_sdmmc")}|{os.path.join(os.path.dirname(__file__), "sdio")}',
|
||||
'esp32|esp32'),
|
||||
], indirect=True)
|
||||
@pytest.mark.parametrize('app_path, target, config', esp32_param_default, indirect=True)
|
||||
def test_sdio_esp32_esp32(dut:Tuple[IdfDut, IdfDut]) -> None:
|
||||
test_sdio_flow(dut)
|
||||
|
||||
@@ -68,11 +77,7 @@ def test_sdio_speed_frhost_flow(dut:Tuple[IdfDut, IdfDut], expected_4b_speed:int
|
||||
@pytest.mark.esp32c6
|
||||
@pytest.mark.sdio_multidev_32_c6
|
||||
@pytest.mark.parametrize('count', [2,], indirect=True)
|
||||
@pytest.mark.parametrize('app_path, target', [
|
||||
pytest.param(
|
||||
f'{os.path.join(os.path.dirname(__file__), "host_sdmmc")}|{os.path.join(os.path.dirname(__file__), "sdio")}',
|
||||
'esp32|esp32c6'),
|
||||
], indirect=True)
|
||||
@pytest.mark.parametrize('app_path, target, config', c6_param_default, indirect=True)
|
||||
def test_sdio_speed_frhost_esp32_esp32c6(dut:Tuple[IdfDut, IdfDut]) -> None:
|
||||
test_sdio_speed_frhost_flow(dut, 10000, 4000)
|
||||
|
||||
@@ -80,11 +85,7 @@ def test_sdio_speed_frhost_esp32_esp32c6(dut:Tuple[IdfDut, IdfDut]) -> None:
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.sdio_master_slave
|
||||
@pytest.mark.parametrize('count', [2,], indirect=True)
|
||||
@pytest.mark.parametrize('app_path, target', [
|
||||
pytest.param(
|
||||
f'{os.path.join(os.path.dirname(__file__), "host_sdmmc")}|{os.path.join(os.path.dirname(__file__), "sdio")}',
|
||||
'esp32|esp32'),
|
||||
], indirect=True)
|
||||
@pytest.mark.parametrize('app_path, target, config', esp32_param_default, indirect=True)
|
||||
def test_sdio_speed_frhost_esp32_esp32(dut:Tuple[IdfDut, IdfDut]) -> None:
|
||||
test_sdio_speed_frhost_flow(dut, 12200, 4000)
|
||||
|
||||
@@ -112,11 +113,7 @@ def test_sdio_speed_tohost_flow(dut:Tuple[IdfDut, IdfDut], expected_4b_speed:int
|
||||
@pytest.mark.esp32c6
|
||||
@pytest.mark.sdio_multidev_32_c6
|
||||
@pytest.mark.parametrize('count', [2,], indirect=True)
|
||||
@pytest.mark.parametrize('app_path, target', [
|
||||
pytest.param(
|
||||
f'{os.path.join(os.path.dirname(__file__), "host_sdmmc")}|{os.path.join(os.path.dirname(__file__), "sdio")}',
|
||||
'esp32|esp32c6'),
|
||||
], indirect=True)
|
||||
@pytest.mark.parametrize('app_path, target, config', c6_param_default, indirect=True)
|
||||
def test_sdio_speed_tohost_esp32_esp32c6(dut:Tuple[IdfDut, IdfDut]) -> None:
|
||||
test_sdio_speed_tohost_flow(dut, 9000, 4000)
|
||||
|
||||
@@ -124,10 +121,27 @@ def test_sdio_speed_tohost_esp32_esp32c6(dut:Tuple[IdfDut, IdfDut]) -> None:
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.sdio_master_slave
|
||||
@pytest.mark.parametrize('count', [2,], indirect=True)
|
||||
@pytest.mark.parametrize('app_path, target', [
|
||||
pytest.param(
|
||||
f'{os.path.join(os.path.dirname(__file__), "host_sdmmc")}|{os.path.join(os.path.dirname(__file__), "sdio")}',
|
||||
'esp32|esp32'),
|
||||
], indirect=True)
|
||||
@pytest.mark.parametrize('app_path, target, config', esp32_param_default, indirect=True)
|
||||
def test_sdio_speed_tohost_esp32_esp32(dut:Tuple[IdfDut, IdfDut]) -> None:
|
||||
test_sdio_speed_tohost_flow(dut, 12200, 4000)
|
||||
|
||||
|
||||
# Retention tests
|
||||
def test_sdio_retention(dut:Tuple[IdfDut, IdfDut]) -> None:
|
||||
dut[1].expect('Press ENTER to see the list of tests')
|
||||
dut[1].write('[sdio_retention]')
|
||||
dut[1].expect('test_sdio: slave ready')
|
||||
|
||||
dut[0].expect('Press ENTER to see the list of tests')
|
||||
dut[0].write('[sdio_retention]')
|
||||
|
||||
dut[1].expect_unity_test_output()
|
||||
dut[0].expect_unity_test_output()
|
||||
|
||||
|
||||
@pytest.mark.esp32c6
|
||||
@pytest.mark.sdio_multidev_32_c6
|
||||
@pytest.mark.parametrize('count', [2,], indirect=True)
|
||||
@pytest.mark.parametrize('app_path, target, config', c6_param_retention, indirect=True)
|
||||
def test_sdio_retention_esp32_esp32c6(dut:Tuple[IdfDut, IdfDut]) -> None:
|
||||
test_sdio_retention(dut)
|
||||
|
@@ -2,5 +2,5 @@ set(srcs "test_app_main.c"
|
||||
"test_sdio_slave.c")
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
PRIV_REQUIRES test_driver_utils driver
|
||||
PRIV_REQUIRES test_driver_utils driver esp_hw_support
|
||||
WHOLE_ARCHIVE)
|
||||
|
@@ -305,6 +305,31 @@ TEST_CASE("SDIO_Slave: test to host", "[sdio]")
|
||||
test_to_host();
|
||||
}
|
||||
|
||||
#if SOC_PAU_SUPPORTED
|
||||
#include "esp_private/sleep_sys_periph.h"
|
||||
#include "esp_private/sleep_retention.h"
|
||||
|
||||
TEST_CASE("SDIO_Slave: test sleep retention", "[sdio_retention]")
|
||||
{
|
||||
TEST_ASSERT_EQUAL_INT32(true, peripheral_domain_pd_allowed());
|
||||
sleep_retention_dump_modules(stdout);
|
||||
|
||||
s_slave_init(SDIO_SLAVE_SEND_STREAM);
|
||||
TEST_ESP_OK(sdio_slave_start());
|
||||
ESP_LOGI(TAG, "slave ready");
|
||||
|
||||
TEST_ASSERT_EQUAL_INT32(false, peripheral_domain_pd_allowed());
|
||||
sleep_retention_dump_modules(stdout);
|
||||
|
||||
wait_for_finish(&s_test_slv_ctx);
|
||||
|
||||
sdio_slave_stop();
|
||||
sdio_slave_deinit();
|
||||
|
||||
TEST_ASSERT_EQUAL_INT32(true, peripheral_domain_pd_allowed());
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("SDIO_Slave: test to host (Performance)", "[sdio_speed]")
|
||||
{
|
||||
test_to_host();
|
||||
|
@@ -0,0 +1,4 @@
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP=y
|
||||
CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
@@ -141,7 +141,7 @@ The following peripheral drivers are not aware of DFS yet. Applications need to
|
||||
|
||||
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.
|
||||
|
||||
Currently ESP-IDF supports Light-sleep context retention for the following peripherals:
|
||||
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.
|
||||
|
||||
.. list::
|
||||
|
||||
@@ -162,7 +162,13 @@ The following peripheral drivers are not aware of DFS yet. Applications need to
|
||||
:SOC_PARLIO_SUPPORT_SLEEP_RETENTION: - PARL_IO
|
||||
:SOC_SPI_SUPPORT_SLEEP_RETENTION: - All GPSPIs
|
||||
|
||||
The following peripherals are not yet supported:
|
||||
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.
|
||||
|
||||
.. list::
|
||||
|
||||
:SOC_SDIO_SLAVE_SUPPORTED: - SDIO Slave
|
||||
|
||||
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.
|
||||
|
||||
.. list::
|
||||
|
||||
@@ -173,9 +179,6 @@ The following peripheral drivers are not aware of DFS yet. Applications need to
|
||||
- USB-Serial-JTAG
|
||||
- MCPWM
|
||||
- SARADC
|
||||
- SDIO
|
||||
|
||||
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.
|
||||
|
||||
.. note::
|
||||
|
||||
|
@@ -141,7 +141,7 @@ ESP-IDF 中集成的电源管理算法可以根据应用程序组件的需求,
|
||||
|
||||
如果在 menuconfig 中启用了 :ref:`CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP`,在初始化外设时,驱动会将外设工作的寄存器上下文注册到休眠备份链表中,在进入休眠前,``REG_DMA`` 外设会读取休眠备份链表中的配置,根据链表中的配置将外设的寄存器上下文备份至内存,``REG_DMA`` 也会在唤醒时将上下文从内存恢复到外设寄存中。
|
||||
|
||||
目前 IDF 支持以下外设的 Light-sleep 上下文备份:
|
||||
目前 IDF 支持以下外设的 Light-sleep 上下文备份,它们的上下文会自动恢复,或者提供了相关的选项允许用户进入外设下电模式:
|
||||
|
||||
.. list::
|
||||
|
||||
@@ -162,7 +162,13 @@ ESP-IDF 中集成的电源管理算法可以根据应用程序组件的需求,
|
||||
:SOC_PARLIO_SUPPORT_SLEEP_RETENTION: - PARL_IO
|
||||
:SOC_SPI_SUPPORT_SLEEP_RETENTION: - All GPSPIs
|
||||
|
||||
以下外设尚未支持:
|
||||
一些外设尚未支持睡眠上下文恢复,或者寄存器丢失后根本无法恢复。即使外设下电功能被启用,它们也会阻止外设下电的发生:
|
||||
|
||||
.. list::
|
||||
|
||||
:SOC_SDIO_SLAVE_SUPPORTED: - SDIO Slave
|
||||
|
||||
以下外设(以及一些未在本章节任意一组中列出的外设)尚未支持外设下电功能。如果您的应用使用了这些外设,它们可能无法在从睡眠中醒来后仍然正常工作:
|
||||
|
||||
.. list::
|
||||
|
||||
@@ -173,9 +179,6 @@ ESP-IDF 中集成的电源管理算法可以根据应用程序组件的需求,
|
||||
- USB-Serial-JTAG
|
||||
- MCPWM
|
||||
- SARADC
|
||||
- SDIO
|
||||
|
||||
对于未支持 Light-sleep 上下文备份的外设,若启用了电源管理功能,应在外设工作时持有 ``ESP_PM_NO_LIGHT_SLEEP`` 锁以避免进入休眠导致外设工作上下文丢失。
|
||||
|
||||
.. note::
|
||||
|
||||
|
Reference in New Issue
Block a user