From f408e1a8bc9b39dd95016a38a294b21713a720d8 Mon Sep 17 00:00:00 2001 From: armando Date: Mon, 18 May 2026 11:28:01 +0800 Subject: [PATCH] feat(sdmmc): add esp32s31 support Enable SDMMC host support on ESP32-S31 across HAL, SOC caps, tests, examples, and documentation. --- .../include/driver/sdmmc_default_configs.h | 55 +- .../esp_driver_sdmmc/src/sd_host_sdmmc.c | 107 +- .../esp_driver_sdmmc/src/sd_trans_sdmmc.c | 55 +- .../sdmmc_test_boards/Kconfig.projbuild | 5 + .../sdmmc_test_boards/sdmmc_test_board_defs.c | 30 +- .../test_apps/sdmmc/README.md | 4 +- .../sdmmc/main/test_sd_driver_resource.c | 15 +- .../test_apps/sdmmc/pytest_sdmmc.py | 2 +- .../esp32s31/include/hal/clk_tree_ll.h | 2 + .../esp_hal_sd/esp32/include/hal/sdmmc_ll.h | 82 +- .../esp_hal_sd/esp32p4/include/hal/sdmmc_ll.h | 85 +- .../esp_hal_sd/esp32s3/include/hal/sdmmc_ll.h | 82 +- .../esp32s31/include/hal/sdmmc_ll.h | 956 ++++++++++++++++++ .../esp32s31/include/soc/sdmmc_pins.h | 23 + components/esp_hal_sd/esp32s31/sdmmc_periph.c | 77 ++ components/esp_hal_sd/include/hal/sdmmc_hal.h | 9 +- components/esp_hal_sd/sdmmc_hal.c | 11 +- components/sdmmc/sdmmc_sd.c | 14 +- components/sdmmc/test_apps/README.md | 4 +- .../esp32s31/include/soc/Kconfig.soc_caps.in | 36 + .../soc/esp32s31/include/soc/clk_tree_defs.h | 15 + .../soc/esp32s31/include/soc/soc_caps.h | 13 +- .../esp32s31/register/soc/cnnt_sys_struct.h | 2 +- .../soc/esp32s31/register/soc/sdmmc_reg.h | 120 ++- .../soc/esp32s31/register/soc/sdmmc_struct.h | 165 ++- docs/conf_common.py | 1 + .../api-reference/peripherals/sdmmc_host.rst | 5 + .../peripherals/i2s/i2s_recorder/README.md | 4 +- .../i2s/i2s_recorder/pytest_i2s_record.py | 2 +- .../peripherals/jpeg/jpeg_decode/README.md | 4 +- .../peripherals/jpeg/jpeg_encode/README.md | 4 +- examples/peripherals/sdio/host/README.md | 4 +- examples/storage/.build-test-rules.yml | 2 +- examples/storage/sd_card/sdmmc/README.md | 17 +- .../storage/sd_card/sdmmc/main/CMakeLists.txt | 4 +- .../sd_card/sdmmc/main/Kconfig.projbuild | 6 +- .../sd_card/sdmmc/main/sd_card_example_main.c | 7 +- .../sdmmc/pytest_sdmmc_card_example.py | 2 +- 38 files changed, 1813 insertions(+), 218 deletions(-) create mode 100644 components/esp_hal_sd/esp32s31/include/hal/sdmmc_ll.h create mode 100644 components/esp_hal_sd/esp32s31/include/soc/sdmmc_pins.h create mode 100644 components/esp_hal_sd/esp32s31/sdmmc_periph.c diff --git a/components/esp_driver_sdmmc/legacy/include/driver/sdmmc_default_configs.h b/components/esp_driver_sdmmc/legacy/include/driver/sdmmc_default_configs.h index 9a588cf8d05..9939b759734 100644 --- a/components/esp_driver_sdmmc/legacy/include/driver/sdmmc_default_configs.h +++ b/components/esp_driver_sdmmc/legacy/include/driver/sdmmc_default_configs.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -22,6 +22,40 @@ extern "C" { * * Uses SDMMC peripheral, with 4-bit mode enabled, and max frequency set to 20MHz */ +#if CONFIG_IDF_TARGET_ESP32S31 +#define SDMMC_HOST_DEFAULT() {\ + .flags = SDMMC_HOST_FLAG_8BIT | \ + SDMMC_HOST_FLAG_4BIT | \ + SDMMC_HOST_FLAG_1BIT | \ + SDMMC_HOST_FLAG_DDR | \ + SDMMC_HOST_FLAG_DEINIT_ARG, \ + .slot = SDMMC_HOST_SLOT_0, \ + .max_freq_khz = SDMMC_FREQ_DEFAULT, \ + .io_voltage = 3.3f, \ + .driver_strength = SDMMC_DRIVER_STRENGTH_B, \ + .current_limit = SDMMC_CURRENT_LIMIT_200MA, \ + .init = &sdmmc_host_init, \ + .set_bus_width = &sdmmc_host_set_bus_width, \ + .get_bus_width = &sdmmc_host_get_slot_width, \ + .set_bus_ddr_mode = &sdmmc_host_set_bus_ddr_mode, \ + .set_card_clk = &sdmmc_host_set_card_clk, \ + .set_cclk_always_on = &sdmmc_host_set_cclk_always_on, \ + .do_transaction = &sdmmc_host_do_transaction, \ + .deinit_p = &sdmmc_host_deinit_slot, \ + .io_int_enable = sdmmc_host_io_int_enable, \ + .io_int_wait = sdmmc_host_io_int_wait, \ + .command_timeout_ms = 0, \ + .get_real_freq = &sdmmc_host_get_real_freq, \ + .input_delay_phase = SDMMC_DELAY_PHASE_0, \ + .set_input_delay = &sdmmc_host_set_input_delay, \ + .set_input_delayline = &sdmmc_host_set_input_delayline, \ + .unaligned_multi_block_rw_max_chunk_size = 16, \ + .dma_aligned_buffer = NULL, \ + .pwr_ctrl_handle = NULL, \ + .check_buffer_alignment = &sdmmc_host_check_buffer_alignment, \ + .is_slot_set_to_uhs1 = &sdmmc_host_is_slot_set_to_uhs1, \ +} +#else #define SDMMC_HOST_DEFAULT() {\ .flags = SDMMC_HOST_FLAG_8BIT | \ SDMMC_HOST_FLAG_4BIT | \ @@ -54,6 +88,7 @@ extern "C" { .check_buffer_alignment = &sdmmc_host_check_buffer_alignment, \ .is_slot_set_to_uhs1 = &sdmmc_host_is_slot_set_to_uhs1, \ } +#endif #define SDMMC_SLOT_NO_CD GPIO_NUM_NC ///< indicates that card detect line is not used #define SDMMC_SLOT_NO_WP GPIO_NUM_NC ///< indicates that write protect line is not used @@ -115,6 +150,24 @@ extern "C" { .width = SDMMC_SLOT_WIDTH_DEFAULT, \ .flags = 0, \ } + +#elif CONFIG_IDF_TARGET_ESP32S31 +#define SDMMC_SLOT_CONFIG_DEFAULT() {\ + .clk = GPIO_NUM_24, \ + .cmd = GPIO_NUM_25, \ + .d0 = GPIO_NUM_20, \ + .d1 = GPIO_NUM_21, \ + .d2 = GPIO_NUM_22, \ + .d3 = GPIO_NUM_23, \ + .d4 = GPIO_NUM_NC, \ + .d5 = GPIO_NUM_NC, \ + .d6 = GPIO_NUM_NC, \ + .d7 = GPIO_NUM_NC, \ + .cd = SDMMC_SLOT_NO_CD, \ + .wp = SDMMC_SLOT_NO_WP, \ + .width = SDMMC_SLOT_WIDTH_DEFAULT, \ + .flags = 0, \ +} #endif // GPIO Matrix chips #ifdef __cplusplus diff --git a/components/esp_driver_sdmmc/src/sd_host_sdmmc.c b/components/esp_driver_sdmmc/src/sd_host_sdmmc.c index 754bd516521..5932dae45b7 100644 --- a/components/esp_driver_sdmmc/src/sd_host_sdmmc.c +++ b/components/esp_driver_sdmmc/src/sd_host_sdmmc.c @@ -16,8 +16,9 @@ #include "esp_memory_utils.h" #include "soc/chip_revision.h" #include "soc/sdmmc_pins.h" -#include "hal/sdmmc_periph.h" #include "soc/soc_caps.h" +#include "soc/clk_tree_defs.h" +#include "hal/sdmmc_periph.h" #include "hal/efuse_hal.h" #include "hal/sd_types.h" #include "hal/sdmmc_hal.h" @@ -31,6 +32,7 @@ #include "esp_private/gpio.h" #include "esp_private/sd_host_private.h" #include "freertos/FreeRTOS.h" +#include "clk_ctrl_os.h" typedef struct sd_platform_t { _lock_t mutex; @@ -106,7 +108,14 @@ esp_err_t sd_host_create_sdmmc_controller(const sd_host_sdmmc_cfg_t *config, sd_ #endif //CONFIG_PM_ENABLE sdmmc_hal_init(&ctlr->hal); - sd_host_set_clk_div(ctlr, SDMMC_CLK_SRC_DEFAULT, 2); + PERIPH_RCC_ATOMIC() { + sdmmc_ll_pad_set_pin_dedicated_ctrl(ctlr->hal.dev, true); + } + ESP_GOTO_ON_ERROR(esp_clk_tree_enable_src(SDMMC_CLK_SRC_DEFAULT, true), err, TAG, "failed to acquire clk"); + uint32_t src_freq_hz = 0; + esp_clk_tree_src_get_freq_hz(SDMMC_CLK_SRC_DEFAULT, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &src_freq_hz); + ESP_EARLY_LOGI(TAG, "src_freq_hz: %d", src_freq_hz); + sd_host_set_clk_div(ctlr, SDMMC_CLK_SRC_DEFAULT, SDMMC_LL_DEFAULT_DIV); // Reset esp_err_t err = sd_host_reset(ctlr); @@ -120,8 +129,6 @@ esp_err_t sd_host_create_sdmmc_controller(const sd_host_sdmmc_cfg_t *config, sd_ sdmmc_ll_clear_interrupt(ctlr->hal.dev, 0xffffffff); sdmmc_ll_enable_interrupt(ctlr->hal.dev, 0xffffffff, false); sdmmc_ll_enable_global_interrupt(ctlr->hal.dev, false); - sdmmc_ll_enable_interrupt(ctlr->hal.dev, SDMMC_LL_EVENT_DEFAULT, true); - sdmmc_ll_enable_global_interrupt(ctlr->hal.dev, true); sdmmc_ll_init_dma(ctlr->hal.dev); ctlr->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; @@ -175,6 +182,14 @@ esp_err_t sd_host_sdmmc_controller_add_slot(sd_host_ctlr_handle_t ctlr, const sd ESP_GOTO_ON_ERROR(sdmmc_slot_io_config(slot, config), err, TAG, "failed to config slot io"); + portENTER_CRITICAL(&ctlr_ctx->spinlock); + if (ctlr_ctx->registered_slot_nums == 1) { + sdmmc_ll_clear_interrupt(ctlr_ctx->hal.dev, 0xffffffff); + sdmmc_ll_enable_interrupt(ctlr_ctx->hal.dev, SDMMC_LL_EVENT_DEFAULT, true); + sdmmc_ll_enable_global_interrupt(ctlr_ctx->hal.dev, true); + } + portEXIT_CRITICAL(&ctlr_ctx->spinlock); + *ret_handle = &slot->drv; return ESP_OK; @@ -210,6 +225,22 @@ static esp_err_t sd_host_slot_sdmmc_configure(sd_host_slot_handle_t slot, const ESP_LOGW(TAG, "input line delay not supported, fallback to 0 delay"); #endif +#if !SDMMC_LL_SDR104_SUPPORTED + if (config->sampling_mode == SD_SAMPLING_MODE_SDR) { + ESP_RETURN_ON_FALSE(config->freq_hz < SDMMC_FREQ_SDR104 * 1000, ESP_ERR_NOT_SUPPORTED, TAG, "UHS-I SDR104 is not supported"); + } +#endif +#if !SDMMC_LL_DDR50_SUPPORTED + if (config->sampling_mode == SD_SAMPLING_MODE_DDR) { + ESP_RETURN_ON_FALSE(config->freq_hz < SDMMC_FREQ_DDR50 * 1000, ESP_ERR_NOT_SUPPORTED, TAG, "UHS-I DDR50 is not supported"); + } +#endif +#if !SDMMC_LL_SDR50_SUPPORTED + if (config->sampling_mode == SD_SAMPLING_MODE_SDR) { + ESP_RETURN_ON_FALSE(config->freq_hz < SDMMC_FREQ_SDR50 * 1000, ESP_ERR_NOT_SUPPORTED, TAG, "UHS-I SDR50 is not supported"); + } +#endif + #if CONFIG_IDF_TARGET_ESP32P4 if (config->freq_hz == SDMMC_FREQ_SDR104 * 1000) { unsigned chip_version = efuse_hal_chip_revision(); @@ -369,6 +400,8 @@ static esp_err_t sd_host_del_sdmmc_controller(sd_host_ctlr_handle_t ctlr) ESP_RETURN_ON_ERROR(sd_host_declaim_controller(ctlr_ctx), TAG, "controller isn't in use"); ESP_RETURN_ON_ERROR(esp_intr_free(ctlr_ctx->intr_handle), TAG, "failed to delete interrupt service"); + sdmmc_hal_deinit(&ctlr_ctx->hal); + if (ctlr_ctx->event_queue) { vQueueDeleteWithCaps(ctlr_ctx->event_queue); } @@ -384,6 +417,10 @@ static esp_err_t sd_host_del_sdmmc_controller(sd_host_ctlr_handle_t ctlr) } #endif +#if SDMMC_LL_MPLL_SUPPORTED + esp_clk_tree_enable_src(SOC_MOD_CLK_MPLL, false); +#endif + free(ctlr_ctx->dma_desc); ctlr_ctx->dma_desc = NULL; free(ctlr_ctx); @@ -804,6 +841,14 @@ static void sd_host_isr(void *arg) sd_host_sdmmc_ctlr_t *ctlr = (sd_host_sdmmc_ctlr_t *)arg; sd_host_sdmmc_slot_t *slot = ctlr->slot[ctlr->cur_slot_id]; + if (slot == NULL) { + uint32_t pending = sdmmc_ll_get_intr_status(ctlr->hal.dev); + uint32_t dma_pending = sdmmc_ll_get_idsts_interrupt_raw(ctlr->hal.dev); + sdmmc_ll_clear_interrupt(ctlr->hal.dev, pending); + sdmmc_ll_clear_idsts_interrupt(ctlr->hal.dev, dma_pending); + return; + } + sd_host_sdmmc_event_t event = {}; bool need_yield = false; int higher_priority_task_awoken = pdFALSE; @@ -1013,7 +1058,7 @@ static void sd_host_slot_get_clk_dividers(sd_host_sdmmc_slot_t *slot, uint32_t f { uint32_t clk_src_freq_hz = 0; soc_periph_sdmmc_clk_src_t clk_src = 0; -#if SOC_SDMMC_UHS_I_SUPPORTED +#if SOC_SDMMC_UHS_I_SUPPORTED && SDMMC_LL_SDIO_PLL_SUPPORTED if (freq_khz > SDMMC_FREQ_HIGHSPEED) { clk_src = SDMMC_CLK_SRC_SDIO_200M; } else @@ -1038,40 +1083,56 @@ static void sd_host_slot_get_clk_dividers(sd_host_sdmmc_slot_t *slot, uint32_t f // Calculate new dividers #if SOC_SDMMC_UHS_I_SUPPORTED if (freq_khz == SDMMC_FREQ_SDR104) { - *host_div = 1; // 200 MHz / 1 = 200 MHz + *host_div = clk_src_freq_hz / 200000000; *card_div = 0; } else if (freq_khz == SDMMC_FREQ_SDR50) { - *host_div = 2; // 200 MHz / 2 = 100 MHz + *host_div = clk_src_freq_hz / 100000000; *card_div = 0; } else #endif +#if !SDMMC_LL_MPLL_SUPPORTED if (freq_khz >= SDMMC_FREQ_HIGHSPEED) { - *host_div = 4; // 160 MHz / 4 = 40 MHz + *host_div = 4; // src_freq_hz / 4 = 40 MHz *card_div = 0; } else if (freq_khz == SDMMC_FREQ_DEFAULT) { - *host_div = 8; // 160 MHz / 8 = 20 MHz + *host_div = 8; // src_freq_hz / 8 = 20 MHz *card_div = 0; } else if (freq_khz == SDMMC_FREQ_PROBING) { - *host_div = 10; // 160 MHz / 10 / (20 * 2) = 400 kHz + *host_div = 10; // src_freq_hz / 10 / (20 * 2) = 400 kHz *card_div = 20; - } else { - /* - * for custom frequencies use maximum range of host divider (1-16), find the closest <= div. combination - * if exceeded, combine with the card divider to keep reasonable precision (applies mainly to low frequencies) - * effective frequency range: 400 kHz - 32 MHz (32.1 - 39.9 MHz cannot be covered with given divider scheme) - */ - *host_div = (clk_src_freq_hz) / (freq_khz * 1000); - if (*host_div > 15) { + } else +#endif + { + uint32_t target_hz = (uint32_t)freq_khz * 1000; + // host_div: 1..16 (4-bit edge regs), card_div: 0=bypass or 1..255 (actual div = 2*card_div) + // actual_freq = clk_src / host_div when card_div == 0 + // actual_freq = clk_src / host_div / (2 * card_div) when card_div > 0 + uint32_t total_div = (clk_src_freq_hz + target_hz - 1) / target_hz; + + if (total_div <= 16) { + *host_div = (int)total_div; + *card_div = 0; + } else { + // Two-stage: smallest host_div keeps highest intermediate freq for best precision *host_div = 2; - *card_div = (clk_src_freq_hz / 2) / (2 * freq_khz * 1000); - if (((clk_src_freq_hz / 2) % (2 * freq_khz * 1000)) > 0) { - (*card_div)++; + *card_div = 255; + for (int h = 2; h <= 16; h++) { + uint32_t intermediate = clk_src_freq_hz / h; + int c = (int)((intermediate + 2 * target_hz - 1) / (2 * target_hz)); + if (c < 1) { + c = 1; + } + if (c <= 255) { + *host_div = h; + *card_div = c; + break; + } } - } else if ((clk_src_freq_hz % (freq_khz * 1000)) > 0) { - (*host_div)++; } } + ESP_LOGD(TAG, "host_div: %d, card_div: %d", *host_div, *card_div); + *src = clk_src; } diff --git a/components/esp_driver_sdmmc/src/sd_trans_sdmmc.c b/components/esp_driver_sdmmc/src/sd_trans_sdmmc.c index 7515418697a..b26e7e43c80 100644 --- a/components/esp_driver_sdmmc/src/sd_trans_sdmmc.c +++ b/components/esp_driver_sdmmc/src/sd_trans_sdmmc.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -15,6 +15,7 @@ #include "soc/soc_caps.h" #include "driver/sd_host.h" #include "esp_private/sd_host_private.h" +#include "esp_memory_utils.h" typedef enum { SDMMC_IDLE, @@ -87,11 +88,12 @@ size_t sd_host_get_free_descriptors_count(sd_host_sdmmc_slot_t *slot) int dma_desc_num = slot->ctlr->dma_desc_num; for (size_t i = 0; i < dma_desc_num; ++i) { sdmmc_desc_t *desc = slot->ctlr->dma_desc + ((next + i) % dma_desc_num); -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - esp_err_t ret = esp_cache_msync((void *)desc, sizeof(sdmmc_desc_t), ESP_CACHE_MSYNC_FLAG_DIR_M2C); - assert(ret == ESP_OK); - (void)ret; -#endif + bool needs_sync = esp_cache_get_line_size_by_addr(desc) > 0; + if (needs_sync) { + esp_err_t ret = esp_cache_msync((void *)desc, sizeof(sdmmc_desc_t), ESP_CACHE_MSYNC_FLAG_DIR_M2C); + assert(ret == ESP_OK); + (void)ret; + } if (desc->owned_by_idmac) { break; } @@ -132,11 +134,13 @@ void sd_host_fill_dma_descriptors(sd_host_sdmmc_slot_t *slot, size_t num_desc) ESP_LOGV(TAG, "fill %d desc=%d rem=%d next=%d last=%d sz=%d", num_desc, next, cur_trans->size_remaining, cur_trans->next_desc, desc->last_descriptor, desc->buffer1_size); -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - esp_err_t ret = esp_cache_msync((void *)desc, sizeof(sdmmc_desc_t), ESP_CACHE_MSYNC_FLAG_DIR_C2M); - assert(ret == ESP_OK); - (void)ret; -#endif + bool needs_sync = false; + needs_sync = esp_cache_get_line_size_by_addr(desc) > 0; + if (needs_sync) { + esp_err_t ret = esp_cache_msync((void *)desc, sizeof(sdmmc_desc_t), ESP_CACHE_MSYNC_FLAG_DIR_C2M); + assert(ret == ESP_OK); + (void)ret; + } } } @@ -507,10 +511,7 @@ esp_err_t sd_host_slot_sdmmc_do_transaction(sd_host_slot_handle_t slot, sdmmc_co ESP_GOTO_ON_ERROR(sd_host_set_delay_phase(slot_ctx), out, TAG, "failed to set delay phase"); ESP_GOTO_ON_ERROR(sd_host_set_delay_line(slot_ctx), out, TAG, "failed to set delay line"); -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - // cache sync related - size_t cache_sync_len = 0; -#endif + __attribute__((unused)) size_t cache_sync_len = 0; // dispose of any events which happened asynchronously sd_host_handle_idle_state_events(slot_ctx); @@ -538,13 +539,14 @@ esp_err_t sd_host_slot_sdmmc_do_transaction(sd_host_slot_handle_t slot, sdmmc_co goto out; } -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE - cache_sync_len = cmdinfo->buflen; - ret = esp_cache_msync((void *)cmdinfo->data, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M); - if (ret != ESP_OK) { - goto out; + bool needs_sync = esp_cache_get_line_size_by_addr(cmdinfo->data) > 0; + if (needs_sync) { + cache_sync_len = cmdinfo->buflen; + ret = esp_cache_msync((void *)cmdinfo->data, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + if (ret != ESP_OK) { + goto out; + } } -#endif // write transfer info into hardware sd_host_dma_prepare(slot_ctx, cmdinfo->data, cmdinfo->datalen, cmdinfo->blklen); } @@ -573,14 +575,15 @@ esp_err_t sd_host_slot_sdmmc_do_transaction(sd_host_slot_handle_t slot, sdmmc_co } slot_ctx->ctlr->is_app_cmd = (ret == ESP_OK && cmdinfo->opcode == MMC_APP_CMD); -#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE if (cmdinfo->data) { - ret = esp_cache_msync((void *)cmdinfo->data, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C); - if (ret != ESP_OK) { - goto out; + bool needs_sync = esp_cache_get_line_size_by_addr(cmdinfo->data) > 0; + if (needs_sync) { + ret = esp_cache_msync((void *)cmdinfo->data, cache_sync_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + if (ret != ESP_OK) { + goto out; + } } } -#endif out: #if CONFIG_PM_ENABLE diff --git a/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/Kconfig.projbuild b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/Kconfig.projbuild index 0a5038e26ce..2b4e4c66e07 100644 --- a/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/Kconfig.projbuild +++ b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/Kconfig.projbuild @@ -45,6 +45,10 @@ menu "SDMMC Test Board Configuration" bool "ESP32-C5 breakout board" depends on IDF_TARGET_ESP32C5 + config SDMMC_BOARD_ESP32S31_KORVO_BOARD + bool "ESP32-S31 KORVO Board" + depends on IDF_TARGET_ESP32S31 + config SDMMC_BOARD_CUSTOM_SD depends on SOC_SDMMC_HOST_SUPPORTED bool "Custom SD (choose pins)" @@ -147,6 +151,7 @@ menu "SDMMC Test Board Configuration" int "GPIO not routed on the board" default 34 if IDF_TARGET_ESP32P4 default 8 if IDF_TARGET_ESP32C5 + default 26 if IDF_TARGET_ESP32S31 default -1 endmenu diff --git a/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/sdmmc_test_board_defs.c b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/sdmmc_test_board_defs.c index 8f8365b6943..60e7854aacc 100644 --- a/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/sdmmc_test_board_defs.c +++ b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/sdmmc_test_board_defs.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -485,6 +485,34 @@ static const sdmmc_test_board_info_t s_board_info = { }, }; +#elif CONFIG_SDMMC_BOARD_ESP32S31_KORVO_BOARD + +static const sdmmc_test_board_info_t s_board_info = { + .name = "ESP32-S31 KORVO Board", + .slot = { + { + .slot_exists = true, + .bus_width = 4, + .clk = SDMMC_SLOT0_IOMUX_PIN_NUM_CLK, + .cmd_mosi = SDMMC_SLOT0_IOMUX_PIN_NUM_CMD, + .d0_miso = SDMMC_SLOT0_IOMUX_PIN_NUM_D0, + .d1 = SDMMC_SLOT0_IOMUX_PIN_NUM_D1, + .d2 = SDMMC_SLOT0_IOMUX_PIN_NUM_D2, + .d3_cs = SDMMC_SLOT0_IOMUX_PIN_NUM_D3, + .d4 = GPIO_NUM_NC, + .d5 = GPIO_NUM_NC, + .d6 = GPIO_NUM_NC, + .d7 = GPIO_NUM_NC, + .cd = GPIO_NUM_NC, + .wp = GPIO_NUM_NC, + .unused_pin = 26, + }, + { + .slot_exists = false, + } + }, +}; + #elif CONFIG_SDMMC_BOARD_CUSTOM_SD static const sdmmc_test_board_info_t s_board_info = { diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/README.md b/components/esp_driver_sdmmc/test_apps/sdmmc/README.md index 7fb40f3f87b..13d5f8b812e 100644 --- a/components/esp_driver_sdmmc/test_apps/sdmmc/README.md +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/README.md @@ -1,2 +1,2 @@ -| Supported Targets | ESP32 | ESP32-P4 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-P4 | ESP32-S3 | ESP32-S31 | +| ----------------- | ----- | -------- | -------- | --------- | diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/main/test_sd_driver_resource.c b/components/esp_driver_sdmmc/test_apps/sdmmc/main/test_sd_driver_resource.c index adc14fe6758..2d21baa1d4e 100644 --- a/components/esp_driver_sdmmc/test_apps/sdmmc/main/test_sd_driver_resource.c +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/main/test_sd_driver_resource.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -53,6 +53,19 @@ #define SDMMC_SLOT1_D1 13 #define SDMMC_SLOT1_D2 14 #define SDMMC_SLOT1_D3 15 +#elif CONFIG_IDF_TARGET_ESP32S31 +#define SDMMC_SLOT0_CLK 24 +#define SDMMC_SLOT0_CMD 25 +#define SDMMC_SLOT0_D0 20 +#define SDMMC_SLOT0_D1 21 +#define SDMMC_SLOT0_D2 22 +#define SDMMC_SLOT0_D3 23 +#define SDMMC_SLOT1_CLK 39 +#define SDMMC_SLOT1_CMD 40 +#define SDMMC_SLOT1_D0 35 +#define SDMMC_SLOT1_D1 36 +#define SDMMC_SLOT1_D2 37 +#define SDMMC_SLOT1_D3 38 #endif TEST_CASE("SDMMC controller exhausted allocation", "[sdmmc]") diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/pytest_sdmmc.py b/components/esp_driver_sdmmc/test_apps/sdmmc/pytest_sdmmc.py index 5adf56eb8c6..b20530251b5 100644 --- a/components/esp_driver_sdmmc/test_apps/sdmmc/pytest_sdmmc.py +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/pytest_sdmmc.py @@ -14,7 +14,7 @@ from pytest_embedded_idf.utils import idf_parametrize ], indirect=True, ) -@idf_parametrize('target', ['esp32', 'esp32s3', 'esp32p4'], indirect=['target']) +@idf_parametrize('target', ['esp32', 'esp32s3', 'esp32p4', 'esp32s31'], indirect=['target']) def test_sdmmc(dut: IdfDut) -> None: # SDMMC driver can't be reinitialized if the test fails, # so we need to reset the board between tests to avoid failing diff --git a/components/esp_hal_clock/esp32s31/include/hal/clk_tree_ll.h b/components/esp_hal_clock/esp32s31/include/hal/clk_tree_ll.h index eaa36ca3a0f..4abe91dba49 100644 --- a/components/esp_hal_clock/esp32s31/include/hal/clk_tree_ll.h +++ b/components/esp_hal_clock/esp32s31/include/hal/clk_tree_ll.h @@ -154,6 +154,7 @@ static inline __attribute__((always_inline)) void clk_ll_mpll_enable(void) SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_1_REG, PMU_TIE_HIGH_XPD_MPLL | PMU_TIE_HIGH_XPD_MPLL_I2C); SET_PERI_REG_MASK(HP_ALIVE_SYS_HP_CLK_CTRL_REG, HP_ALIVE_SYS_HP_MPLL_500M_CLK_EN); SET_PERI_REG_MASK(PMU_HP_ACTIVE_HP_CK_POWER_REG, PMU_HP_ACTIVE_XPD_MPLL_I2C | PMU_HP_ACTIVE_XPD_MPLL); + SET_PERI_REG_MASK(PMU_PSRAM_CFG_REG, PMU_PSRAM_XPD); } /** @@ -165,6 +166,7 @@ static inline __attribute__((always_inline)) void clk_ll_mpll_disable(void) SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_1_REG, PMU_TIE_LOW_XPD_MPLL | PMU_TIE_LOW_XPD_MPLL_I2C); CLEAR_PERI_REG_MASK(HP_ALIVE_SYS_HP_CLK_CTRL_REG, HP_ALIVE_SYS_HP_MPLL_500M_CLK_EN); CLEAR_PERI_REG_MASK(PMU_HP_ACTIVE_HP_CK_POWER_REG, PMU_HP_ACTIVE_XPD_MPLL_I2C | PMU_HP_ACTIVE_XPD_MPLL); + CLEAR_PERI_REG_MASK(PMU_PSRAM_CFG_REG, PMU_PSRAM_XPD); } /** diff --git a/components/esp_hal_sd/esp32/include/hal/sdmmc_ll.h b/components/esp_hal_sd/esp32/include/hal/sdmmc_ll.h index 1d58d4db5e7..495fc65c8e1 100644 --- a/components/esp_hal_sd/esp32/include/hal/sdmmc_ll.h +++ b/components/esp_hal_sd/esp32/include/hal/sdmmc_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -87,6 +87,8 @@ extern "C" { #define SDMMC_LL_IOMUX_FUNC 3 #define SDMMC_LL_HOST_CTLR_NUMS 1U +#define SDMMC_LL_DEFAULT_DIV 2 + typedef enum { SDMMC_LL_DELAY_PHASE_0, SDMMC_LL_DELAY_PHASE_1, @@ -94,6 +96,20 @@ typedef enum { SDMMC_LL_DELAY_PHASE_3, } sdmmc_ll_delay_phase_t; +/** + * SDMMC memory low power mode + */ +typedef enum { + SDMMC_LL_MEM_LP_MODE_SHUT_DOWN, +} sdmmc_ll_mem_lp_mode_t; + +/** + * @brief SDMMC IO power control source + */ +typedef enum { + SDMMC_LL_IO_POWER_CONTROL_SRC_INTERNAL, +} sdmmc_ll_io_power_control_src_t; + /*--------------------------------------------------------------- Clock & Reset ---------------------------------------------------------------*/ @@ -139,6 +155,60 @@ static inline void sdmmc_ll_reset_register(int group_id) sdmmc_ll_reset_register(__VA_ARGS__); \ } while(0) +/** + * @brief Force power on the SDMMC memory block, regardless of the outside PMU logic + * + * @param dev Peripheral instance address + */ +static inline void sdmmc_ll_mem_force_power_on(sdmmc_dev_t *dev) +{ + (void)dev; +} + +/** + * @brief Force the SDMMC memory block into low power mode, regardless of the outside PMU logic + * + * @param dev Peripheral instance address + */ +static inline void sdmmc_ll_mem_force_low_power(sdmmc_dev_t *dev) +{ + (void)dev; +} + +/** + * @brief Power control the SDMMC memory block by the outside PMU logic + * + * @param dev Peripheral instance address + */ +static inline void sdmmc_ll_mem_power_by_pmu(sdmmc_dev_t *dev) +{ + (void)dev; +} + +/** + * @brief Set low power mode for SDMMC memory block + * + * @param dev Peripheral instance address + * @param mode SDMMC memory low power mode in low power stage + */ +static inline void sdmmc_ll_mem_set_low_power_mode(sdmmc_dev_t *dev, sdmmc_ll_mem_lp_mode_t mode) +{ + (void)dev; + (void)mode; +} + +/** + * @brief Set SDMMC pad pin dedicated ctrl + * + * @param dev Peripheral instance address + * @param enable True to enable, False to disable + */ +static inline void sdmmc_ll_pad_set_pin_dedicated_ctrl(sdmmc_dev_t *dev, bool enable) +{ + (void)dev; + (void)enable; +} + /** * @brief Select SDMMC clock source * @@ -733,6 +803,16 @@ static inline void sdmmc_ll_clear_idsts_interrupt(sdmmc_dev_t *hw, uint32_t mask hw->idsts.val = mask; } +/** + * @brief Switch SDMMC IO power control source + * + * @param src SDMMC IO power control source + */ +static inline void sdmmc_ll_switch_io_power_control_src(sdmmc_ll_io_power_control_src_t src) +{ + //for compatibility +} + #ifdef __cplusplus } #endif diff --git a/components/esp_hal_sd/esp32p4/include/hal/sdmmc_ll.h b/components/esp_hal_sd/esp32p4/include/hal/sdmmc_ll.h index 009a5141247..5f59858923c 100644 --- a/components/esp_hal_sd/esp32p4/include/hal/sdmmc_ll.h +++ b/components/esp_hal_sd/esp32p4/include/hal/sdmmc_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -91,6 +91,11 @@ extern "C" { #define SDMMC_LL_DELAY_MAX_NUMS_LS 4 #define SDMMC_LL_DELAY_PHASE_SUPPORTED 1 #define SDMMC_LL_SDIO_PLL_SUPPORTED 1 +#define SDMMC_LL_SDR104_SUPPORTED 1 +#define SDMMC_LL_DDR50_SUPPORTED 1 +#define SDMMC_LL_SDR50_SUPPORTED 1 + +#define SDMMC_LL_DEFAULT_DIV 2 /** * SDMMC delay phase @@ -128,6 +133,20 @@ typedef enum { SDMMC_LL_SPEED_MODE_HS, } sdmmc_ll_speed_mode_t; +/** + * SDMMC memory low power mode + */ +typedef enum { + SDMMC_LL_MEM_LP_MODE_SHUT_DOWN, +} sdmmc_ll_mem_lp_mode_t; + +/** + * @brief SDMMC IO power control source + */ +typedef enum { + SDMMC_LL_IO_POWER_CONTROL_SRC_LDO, +} sdmmc_ll_io_power_control_src_t; + /*--------------------------------------------------------------- Clock & Reset ---------------------------------------------------------------*/ @@ -169,6 +188,60 @@ static inline void sdmmc_ll_reset_register(int group_id) sdmmc_ll_reset_register(__VA_ARGS__); \ } while(0) +/** + * @brief Force power on the SDMMC memory block, regardless of the outside PMU logic + * + * @param dev Peripheral instance address + */ +static inline void sdmmc_ll_mem_force_power_on(sdmmc_dev_t *dev) +{ + (void)dev; +} + +/** + * @brief Force the SDMMC memory block into low power mode, regardless of the outside PMU logic + * + * @param dev Peripheral instance address + */ +static inline void sdmmc_ll_mem_force_low_power(sdmmc_dev_t *dev) +{ + (void)dev; +} + +/** + * @brief Power control the SDMMC memory block by the outside PMU logic + * + * @param dev Peripheral instance address + */ +static inline void sdmmc_ll_mem_power_by_pmu(sdmmc_dev_t *dev) +{ + (void)dev; +} + +/** + * @brief Set low power mode for SDMMC memory block + * + * @param dev Peripheral instance address + * @param mode SDMMC memory low power mode in low power stage + */ +static inline void sdmmc_ll_mem_set_low_power_mode(sdmmc_dev_t *dev, sdmmc_ll_mem_lp_mode_t mode) +{ + (void)dev; + (void)mode; +} + +/** + * @brief Set SDMMC pad pin dedicated ctrl + * + * @param dev Peripheral instance address + * @param enable True to enable, False to disable + */ +static inline void sdmmc_ll_pad_set_pin_dedicated_ctrl(sdmmc_dev_t *dev, bool enable) +{ + (void)dev; + (void)enable; +} + /** * @brief Enable the bus clock for SDIO PLL * @@ -919,6 +992,16 @@ static inline void sdmmc_ll_clear_idsts_interrupt(sdmmc_dev_t *hw, uint32_t mask hw->idsts.val = mask; } +/** + * @brief Switch SDMMC IO power control source + * + * @param src SDMMC IO power control source + */ +static inline void sdmmc_ll_switch_io_power_control_src(sdmmc_ll_io_power_control_src_t src) +{ + //for compatibility +} + #ifdef __cplusplus } #endif diff --git a/components/esp_hal_sd/esp32s3/include/hal/sdmmc_ll.h b/components/esp_hal_sd/esp32s3/include/hal/sdmmc_ll.h index f3b12828868..87e63af3aa7 100644 --- a/components/esp_hal_sd/esp32s3/include/hal/sdmmc_ll.h +++ b/components/esp_hal_sd/esp32s3/include/hal/sdmmc_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -89,6 +89,8 @@ extern "C" { #define SDMMC_LL_DELAY_MAX_NUMS_LS 4 #define SDMMC_LL_DELAY_PHASE_SUPPORTED 1 +#define SDMMC_LL_DEFAULT_DIV 2 + /** * SDMMC delay phase */ @@ -107,6 +109,20 @@ typedef enum { SDMMC_LL_SPEED_MODE_HS, } sdmmc_ll_speed_mode_t; +/** + * SDMMC memory low power mode + */ +typedef enum { + SDMMC_LL_MEM_LP_MODE_SHUT_DOWN, +} sdmmc_ll_mem_lp_mode_t; + +/** + * @brief SDMMC IO power control source + */ +typedef enum { + SDMMC_LL_IO_POWER_CONTROL_SRC_INTERNAL, +} sdmmc_ll_io_power_control_src_t; + /*--------------------------------------------------------------- Clock & Reset ---------------------------------------------------------------*/ @@ -148,6 +164,60 @@ static inline void sdmmc_ll_reset_register(int group_id) sdmmc_ll_reset_register(__VA_ARGS__); \ } while(0) +/** + * @brief Force power on the SDMMC memory block, regardless of the outside PMU logic + * + * @param dev Peripheral instance address + */ +static inline void sdmmc_ll_mem_force_power_on(sdmmc_dev_t *dev) +{ + (void)dev; +} + +/** + * @brief Force the SDMMC memory block into low power mode, regardless of the outside PMU logic + * + * @param dev Peripheral instance address + */ +static inline void sdmmc_ll_mem_force_low_power(sdmmc_dev_t *dev) +{ + (void)dev; +} + +/** + * @brief Power control the SDMMC memory block by the outside PMU logic + * + * @param dev Peripheral instance address + */ +static inline void sdmmc_ll_mem_power_by_pmu(sdmmc_dev_t *dev) +{ + (void)dev; +} + +/** + * @brief Set low power mode for SDMMC memory block + * + * @param dev Peripheral instance address + * @param mode SDMMC memory low power mode in low power stage + */ +static inline void sdmmc_ll_mem_set_low_power_mode(sdmmc_dev_t *dev, sdmmc_ll_mem_lp_mode_t mode) +{ + (void)dev; + (void)mode; +} + +/** + * @brief Set SDMMC pad pin dedicated ctrl + * + * @param dev Peripheral instance address + * @param enable True to enable, False to disable + */ +static inline void sdmmc_ll_pad_set_pin_dedicated_ctrl(sdmmc_dev_t *dev, bool enable) +{ + (void)dev; + (void)enable; +} + /** * @brief Select SDMMC clock source * @@ -797,6 +867,16 @@ static inline void sdmmc_ll_clear_idsts_interrupt(sdmmc_dev_t *hw, uint32_t mask hw->idsts.val = mask; } +/** + * @brief Switch SDMMC IO power control source + * + * @param src SDMMC IO power control source + */ +static inline void sdmmc_ll_switch_io_power_control_src(sdmmc_ll_io_power_control_src_t src) +{ + //for compatibility +} + #ifdef __cplusplus } #endif diff --git a/components/esp_hal_sd/esp32s31/include/hal/sdmmc_ll.h b/components/esp_hal_sd/esp32s31/include/hal/sdmmc_ll.h new file mode 100644 index 00000000000..0f7d38a1957 --- /dev/null +++ b/components/esp_hal_sd/esp32s31/include/hal/sdmmc_ll.h @@ -0,0 +1,956 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/******************************************************************************* + * NOTICE + * The ll is not public api, don't use in application code. + * See readme.md in hal/include/hal/readme.md + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include "esp_bit_defs.h" +#include "hal/assert.h" +#include "hal/misc.h" +#include "hal/sd_types.h" +#include "soc/clk_tree_defs.h" +#include "soc/sdmmc_struct.h" +#include "soc/sdmmc_reg.h" +#include "soc/hp_sys_clkrst_struct.h" +#include "soc/lp_clkrst_struct.h" +#include "soc/pmu_reg.h" +#include "soc/cnnt_sys_struct.h" +#include "soc/cnnt_io_mux_struct.h" +#include "soc/pmu_struct.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SDMMC_LL_GET_HW(id) (((id) == 0) ? (&SDMMC) : NULL) + +#define SDMMC_LL_EVENT_IO_SLOT1 (1<<17) +#define SDMMC_LL_EVENT_IO_SLOT0 (1<<16) +#define SDMMC_LL_EVENT_EBE (1<<15) +#define SDMMC_LL_EVENT_ACD (1<<14) +#define SDMMC_LL_EVENT_SBE (1<<13) +#define SDMMC_LL_EVENT_BCI (1<<13) +#define SDMMC_LL_EVENT_HLE (1<<12) +#define SDMMC_LL_EVENT_FRUN (1<<11) +#define SDMMC_LL_EVENT_HTO (1<<10) +#define SDMMC_LL_EVENT_DTO (1<<9) +#define SDMMC_LL_EVENT_RTO (1<<8) +#define SDMMC_LL_EVENT_DCRC (1<<7) +#define SDMMC_LL_EVENT_RCRC (1<<6) +#define SDMMC_LL_EVENT_RXDR (1<<5) +#define SDMMC_LL_EVENT_TXDR (1<<4) +#define SDMMC_LL_EVENT_DATA_OVER (1<<3) +#define SDMMC_LL_EVENT_CMD_DONE (1<<2) +#define SDMMC_LL_EVENT_RESP_ERR (1<<1) +#define SDMMC_LL_EVENT_CD (1<<0) + +/* Default disabled interrupts (on init): + * SDMMC_LL_EVENT_RXDR, + * SDMMC_LL_EVENT_TXDR, + * SDMMC_LL_EVENT_BCI, + * SDMMC_LL_EVENT_ACD, + * SDMMC_LL_EVENT_IO_SLOT1, + * SDMMC_LL_EVENT_IO_SLOT0 + */ +// Default enabled interrupts (sdio is enabled only when use): +#define SDMMC_LL_EVENT_DEFAULT \ + (SDMMC_LL_EVENT_CD | SDMMC_LL_EVENT_RESP_ERR | SDMMC_LL_EVENT_CMD_DONE | SDMMC_LL_EVENT_DATA_OVER | \ + SDMMC_LL_EVENT_RCRC | SDMMC_LL_EVENT_DCRC | SDMMC_LL_EVENT_RTO | SDMMC_LL_EVENT_DTO | SDMMC_LL_EVENT_HTO | \ + SDMMC_LL_EVENT_HLE | \ + SDMMC_LL_EVENT_SBE | \ + SDMMC_LL_EVENT_EBE) + +#define SDMMC_LL_SD_EVENT_MASK \ + (SDMMC_LL_EVENT_CD | SDMMC_LL_EVENT_RESP_ERR | SDMMC_LL_EVENT_CMD_DONE | SDMMC_LL_EVENT_DATA_OVER | \ + SDMMC_LL_EVENT_TXDR | SDMMC_LL_EVENT_RXDR |\ + SDMMC_LL_EVENT_RCRC | SDMMC_LL_EVENT_DCRC | SDMMC_LL_EVENT_RTO | SDMMC_LL_EVENT_DTO | SDMMC_LL_EVENT_HTO | \ + SDMMC_LL_EVENT_FRUN | SDMMC_LL_EVENT_HLE |\ + SDMMC_LL_EVENT_SBE | SDMMC_LL_EVENT_ACD |\ + SDMMC_LL_EVENT_EBE) + +// DMA interrupts (idsts register) +#define SDMMC_LL_EVENT_DMA_TI SDMMC_IDMAC_INTMASK_TI +#define SDMMC_LL_EVENT_DMA_RI SDMMC_IDMAC_INTMASK_RI +#define SDMMC_LL_EVENT_DMA_NI SDMMC_IDMAC_INTMASK_NI +#define SDMMC_LL_EVENT_DMA_MASK 0x1f //NI and AI will be indicated by TI/RI and FBE/DU respectively + +/** + * SDMMC capabilities + */ +#define SDMMC_LL_SLOT_SUPPORT_GPIO_MATRIX(SLOT_ID) 0 +#define SDMMC_LL_IOMUX_FUNC 4 +#define SDMMC_LL_HOST_CTLR_NUMS 1U +#define SDMMC_LL_DELAY_MAX_NUMS_LS 4 +#define SDMMC_LL_DELAY_PHASE_SUPPORTED 1 +#define SDMMC_LL_MPLL_SUPPORTED 1 +#define SDMMC_LL_SDR50_SUPPORTED 1 + +#define SDMMC_LL_DEFAULT_DIV 8 + +/** + * SDMMC delay phase + */ +typedef enum { + SDMMC_LL_DELAY_PHASE_0, + SDMMC_LL_DELAY_PHASE_1, + SDMMC_LL_DELAY_PHASE_2, + SDMMC_LL_DELAY_PHASE_3, + SDMMC_LL_DELAY_PHASE_4, + SDMMC_LL_DELAY_PHASE_5, + SDMMC_LL_DELAY_PHASE_6, + SDMMC_LL_DELAY_PHASE_7, +} sdmmc_ll_delay_phase_t; + +/** + * SDMMC delayline + */ +typedef enum { + SDMMC_LL_DELAY_LINE_0, + SDMMC_LL_DELAY_LINE_1, + SDMMC_LL_DELAY_LINE_2, + SDMMC_LL_DELAY_LINE_3, + SDMMC_LL_DELAY_LINE_4, + SDMMC_LL_DELAY_LINE_5, + SDMMC_LL_DELAY_LINE_6, + SDMMC_LL_DELAY_LINE_7, +} sdmmc_ll_delay_line_t; + +/** + * SDMMC speed mode + */ +typedef enum { + SDMMC_LL_SPEED_MODE_LS, + SDMMC_LL_SPEED_MODE_HS, +} sdmmc_ll_speed_mode_t; + +/** + * SDMMC memory low power mode + */ +typedef enum { + SDMMC_LL_MEM_LP_MODE_DEEP_SLEEP, // memory will enter deep sleep during low power stage, keep memory data + SDMMC_LL_MEM_LP_MODE_LIGHT_SLEEP, // memory will enter light sleep during low power stage, keep memory data + SDMMC_LL_MEM_LP_MODE_SHUT_DOWN, // memory will be powered down during low power stage + SDMMC_LL_MEM_LP_MODE_DISABLE, // disable the low power stage +} sdmmc_ll_mem_lp_mode_t; + +/** + * @brief SDMMC IO power control source + */ +typedef enum { + SDMMC_LL_IO_POWER_CONTROL_SRC_INTERNAL, + SDMMC_LL_IO_POWER_CONTROL_SRC_LDO, +} sdmmc_ll_io_power_control_src_t; + +/*--------------------------------------------------------------- + Clock & Reset +---------------------------------------------------------------*/ +/** + * @brief Enable the bus clock for SDMMC module + * + * @param group_id Group ID + * @param en enable / disable + */ +static inline void sdmmc_ll_enable_bus_clock(int group_id, bool en) +{ + (void)group_id; + HP_SYS_CLKRST.sdio_host_ctrl0.reg_sdmmc_sys_clk_en = en; +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define sdmmc_ll_enable_bus_clock(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + sdmmc_ll_enable_bus_clock(__VA_ARGS__); \ + } while(0) + +/** + * @brief Reset the SDMMC module + * + * @param group_id Group ID + */ +static inline void sdmmc_ll_reset_register(int group_id) +{ + (void)group_id; + CNNT_SYS_REG.sys_hp_sdmmc_ctrl.sys_sdmmc_rst_en = 1; + CNNT_SYS_REG.sys_hp_sdmmc_ctrl.sys_sdmmc_rst_en = 0; +} + +/** + * @brief Force power on the SDMMC memory block, regardless of the outside PMU logic + * + * @param dev Peripheral instance address + */ +static inline void sdmmc_ll_mem_force_power_on(sdmmc_dev_t *dev) +{ + CNNT_SYS_REG.sys_sdmmc_mem_lp_ctrl.sys_sdmmc_mem_lp_force_ctrl = 1; + CNNT_SYS_REG.sys_sdmmc_mem_lp_ctrl.sys_sdmmc_mem_lp_en = 0; +} + +/** + * @brief Force the SDMMC memory block into low power mode, regardless of the outside PMU logic + * + * @param dev Peripheral instance address + */ +static inline void sdmmc_ll_mem_force_low_power(sdmmc_dev_t *dev) +{ + CNNT_SYS_REG.sys_sdmmc_mem_lp_ctrl.sys_sdmmc_mem_lp_force_ctrl = 1; + CNNT_SYS_REG.sys_sdmmc_mem_lp_ctrl.sys_sdmmc_mem_lp_en = 1; +} + +/** + * @brief Power control the SDMMC memory block by the outside PMU logic + * + * @param dev Peripheral instance address + */ +static inline void sdmmc_ll_mem_power_by_pmu(sdmmc_dev_t *dev) +{ + CNNT_SYS_REG.sys_sdmmc_mem_lp_ctrl.sys_sdmmc_mem_lp_force_ctrl = 0; + CNNT_SYS_REG.sys_sdmmc_mem_lp_ctrl.sys_sdmmc_mem_lp_en = 0; +} + +/** + * @brief Set low power mode for SDMMC memory block + * + * @param dev Peripheral instance address + * @param mode SDMMC memory low power mode in low power stage + */ +static inline void sdmmc_ll_mem_set_low_power_mode(sdmmc_dev_t *dev, sdmmc_ll_mem_lp_mode_t mode) +{ + CNNT_SYS_REG.sys_sdmmc_mem_lp_ctrl.sys_sdmmc_mem_lp_mode = mode; +} + +/** + * @brief Set SDMMC pad pin dedicated ctrl + * + * @param dev Peripheral instance address + * @param enable True to enable, False to disable + */ +static inline void sdmmc_ll_pad_set_pin_dedicated_ctrl(sdmmc_dev_t *dev, bool enable) +{ + CNNT_PAD_CTRL.ctrl.sdio_pad_pin_ctrl_ded_sel = enable; +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define sdmmc_ll_pad_set_pin_dedicated_ctrl(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + sdmmc_ll_pad_set_pin_dedicated_ctrl(__VA_ARGS__); \ + } while(0) + +/** + * @brief Select SDMMC clock source + * + * @param hw hardware instance address + * @param clk_src clock source, see valid sources in type `soc_periph_sdmmc_clk_src_t` + */ +static inline void sdmmc_ll_select_clk_source(sdmmc_dev_t *hw, soc_periph_sdmmc_clk_src_t clk_src) +{ + uint32_t clk_val = 0; + switch (clk_src) { + case SDMMC_CLK_SRC_MPLL: + clk_val = 0; + break; + default: + HAL_ASSERT(false); + break; + } + + HP_SYS_CLKRST.sdio_host_ctrl0.reg_sdio_ls_clk_src_sel = clk_val; + HP_SYS_CLKRST.sdio_host_ctrl0.reg_sdio_ls_clk_en = true; +} + +/** + * @brief Set SDMMC clock div + * + * @param hw hardware instance address + * @param div divider value + */ +static inline void sdmmc_ll_set_clock_div(sdmmc_dev_t *hw, uint32_t div) +{ + if (div > 1) { + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_clk_edge_h = div / 2 - 1; + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_clk_edge_n = div - 1; + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_clk_edge_l = div - 1; + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_clk_edge_cfg_update = 1; + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_clk_edge_cfg_update = 0; + } else { + HP_SYS_CLKRST.sdio_host_ctrl0.reg_sdio_hs_mode = 1; + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_clk_edge_h = 0; + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_clk_edge_n = 0; + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_clk_edge_l = 0; + } +} + +/** + * @brief Deinit clock + * + * @param hw hardware instance address + */ +static inline void sdmmc_ll_deinit_clk(sdmmc_dev_t *hw) +{ + hw->clk_edge_sel.val = 0; +} + +/** + * @brief Get SDMMC clock div + * + * @param hw hardware instance address + * + * @return Divider value + */ +static inline uint32_t sdmmc_ll_get_clock_div(sdmmc_dev_t *hw) +{ + uint32_t div = 0; + if (HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_clk_edge_h == 0 && + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_clk_edge_n == 0 && + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_clk_edge_l == 0) { + div = 1; + } else { + div = HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_clk_edge_l + 1; + } + + return div; +} + +/** + * @brief Initialise the din, dout, self delay phase + * + * @param hw hardware instance address + */ +static inline void sdmmc_ll_init_phase_delay(sdmmc_dev_t *hw) +{ + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_drv_clk_en = 1; + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_sam_clk_en = 1; + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_slf_clk_en = 1; + + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_drv_clk_edge_sel = 1; + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_sam_clk_edge_sel = 0; + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_slf_clk_edge_sel = 0; + + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_clk_edge_cfg_update = 1; + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_clk_edge_cfg_update = 0; +} + +/** + * @brief Set SDMMC din delay phase + * + * @param hw hardware instance address + * @param phase delay phase + * @param mode speed mode + */ +static inline void sdmmc_ll_set_din_delay_phase(sdmmc_dev_t *hw, sdmmc_ll_delay_phase_t phase, sdmmc_ll_speed_mode_t mode) +{ + if (mode == SDMMC_LL_SPEED_MODE_LS) { + switch (phase) { + case SDMMC_LL_DELAY_PHASE_1: + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_sam_clk_edge_sel = 0x1; + break; + case SDMMC_LL_DELAY_PHASE_2: + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_sam_clk_edge_sel = 0x2; + break; + case SDMMC_LL_DELAY_PHASE_3: + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_sam_clk_edge_sel = 0x3; + break; + default: + HP_SYS_CLKRST.sdio_host_func_ctrl0.reg_sdio_ls_sam_clk_edge_sel = 0x0; + break; + } + } else { + SDMMC.dll_clk_conf.dll_cclk_in_sam_phase = (phase << 3); + } +} + +/** + * @brief Set SDMMC dout delay phase + * + * @param hw hardware instance address + * @param phase delay phase + * @param mode speed mode + */ +static inline void sdmmc_ll_set_dout_delay_phase(sdmmc_dev_t *hw, sdmmc_ll_delay_phase_t phase, sdmmc_ll_speed_mode_t mode) +{ + if (mode == SDMMC_LL_SPEED_MODE_HS) { + SDMMC.dll_clk_conf.dll_cclk_in_drv_phase = (phase << 3); + } +} + +/** + * @brief Set SDMMC din delay line + * + * @param hw hardware instance address + * @param phase delay line + * @param mode speed mode + */ +static inline void sdmmc_ll_set_din_delay_line(sdmmc_dev_t *hw, sdmmc_ll_delay_line_t phase, sdmmc_ll_speed_mode_t mode) +{ + SDMMC.dll_clk_conf.dll_cclk_in_sam_phase &= ~0x7; + if (mode == SDMMC_LL_SPEED_MODE_HS) { + SDMMC.dll_clk_conf.dll_cclk_in_sam_phase |= phase; + } else { + HAL_ASSERT(false); + } +} + +/** + * @brief Set SDMMC dout delay line + * + * @param hw hardware instance address + * @param phase delay line + * @param mode speed mode + */ +static inline void sdmmc_ll_set_dout_delay_line(sdmmc_dev_t *hw, sdmmc_ll_delay_line_t phase, sdmmc_ll_speed_mode_t mode) +{ + SDMMC.dll_clk_conf.dll_cclk_in_drv_phase &= ~0x7; + if (mode == SDMMC_LL_SPEED_MODE_HS) { + SDMMC.dll_clk_conf.dll_cclk_in_drv_phase |= phase; + } else { + HAL_ASSERT(false); + } +} + +/** + * @brief Enable card clock + * + * @param hw hardware instance address + * @param slot slot + * @param en enable / disable + */ +static inline void sdmmc_ll_enable_card_clock(sdmmc_dev_t *hw, uint32_t slot, bool en) +{ + if (en) { + hw->clkena.cclk_enable |= BIT(slot); + } else { + hw->clkena.cclk_enable &= ~BIT(slot); + } +} + +/** + * @brief Set card clock div + * + * @param hw hardware instance address + * @param slot slot + * @param card_div divider value + */ +static inline void sdmmc_ll_set_card_clock_div(sdmmc_dev_t *hw, uint32_t slot, uint32_t card_div) +{ + if (slot == 0) { + hw->clksrc.card0 = 0; + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkdiv, clk_divider0, card_div); + } else if (slot == 1) { + hw->clksrc.card1 = 1; + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkdiv, clk_divider1, card_div); + } else { + HAL_ASSERT(false); + } +} + +/** + * @brief Get card clock div + * + * @param hw hardware instance address + * @param slot slot + * + * @return Divider value + */ +static inline uint32_t sdmmc_ll_get_card_clock_div(sdmmc_dev_t *hw, uint32_t slot) +{ + uint32_t card_div = 0; + + if (slot == 0) { + card_div = HAL_FORCE_READ_U32_REG_FIELD(hw->clkdiv, clk_divider0); + } else if (slot == 1) { + card_div = HAL_FORCE_READ_U32_REG_FIELD(hw->clkdiv, clk_divider1); + } else { + HAL_ASSERT(false); + } + + return card_div; +} + +/** + * @brief Disable clock when the card is in IDLE state + * + * @param hw hardware instance address + * @param slot slot + * @param en enable / disable + */ +static inline void sdmmc_ll_enable_card_clock_low_power(sdmmc_dev_t *hw, uint32_t slot, bool en) +{ + if (en) { + hw->clkena.lp_enable |= BIT(slot); + } else { + hw->clkena.lp_enable &= ~BIT(slot); + } +} + +/** + * @brief Reset controller + * + * @note Self clear after two AHB clock cycles, needs wait done + * + * @param hw hardware instance address + */ +static inline void sdmmc_ll_reset_controller(sdmmc_dev_t *hw) +{ + hw->ctrl.controller_reset = 1; +} + +/** + * @brief Get if controller reset is done + * + * @param hw hardware instance address + * + * @return true: done; false: not done + */ +static inline bool sdmmc_ll_is_controller_reset_done(sdmmc_dev_t *hw) +{ + return hw->ctrl.controller_reset == 0; +} + +/** + * @brief Reset DMA + * + * @note Self clear after two AHB clock cycles, needs wait done + * + * @param hw hardware instance address + */ +static inline void sdmmc_ll_reset_dma(sdmmc_dev_t *hw) +{ + hw->ctrl.dma_reset = 1; +} + +/** + * @brief Get if dma reset is done + * + * @param hw hardware instance address + * + * @return true: done; false: not done + */ +static inline bool sdmmc_ll_is_dma_reset_done(sdmmc_dev_t *hw) +{ + return hw->ctrl.dma_reset == 0; +} + +/** + * @brief Reset fifo + * + * @note Self clear after reset done, needs wait done + * + * @param hw hardware instance address + */ +static inline void sdmmc_ll_reset_fifo(sdmmc_dev_t *hw) +{ + hw->ctrl.fifo_reset = 1; +} + +/** + * @brief Get if fifo reset is done + * + * @param hw hardware instance address + * + * @return true: done; false: not done + */ +static inline bool sdmmc_ll_is_fifo_reset_done(sdmmc_dev_t *hw) +{ + return hw->ctrl.fifo_reset == 0; +} + +/*--------------------------------------------------------------- + MISC +---------------------------------------------------------------*/ +/** + * @brief Set card data read timeout cycles + * + * @param hw hardware instance address + * @param timeout_cycles timeout cycles + */ +static inline void sdmmc_ll_set_data_timeout(sdmmc_dev_t *hw, uint32_t timeout_cycles) +{ + if (timeout_cycles > 0xffffff) { + timeout_cycles = 0xffffff; + } + + hw->tmout.data_timeout = timeout_cycles; +} + +/** + * @brief Set response timeout cycles (in card output clocks) + * + * @param hw hardware instance address + * @param timeout_cycles timeout cycles + */ +static inline void sdmmc_ll_set_response_timeout(sdmmc_dev_t *hw, uint32_t timeout_cycles) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tmout, response_timeout, timeout_cycles); +} + +/** + * @brief Check if card is detected + * + * @param hw hardware instance address + * @param slot slot + * + * @return True for detected + */ +static inline bool sdmmc_ll_is_card_detected(sdmmc_dev_t *hw, uint32_t slot) +{ + return ((hw->cdetect.card_detect_n & BIT(slot)) == 0); +} + +/** + * @brief Check if card is write protected + * + * @param hw hardware instance address + * @param slot slot + * + * @return True for write protected + */ +static inline bool sdmmc_ll_is_card_write_protected(sdmmc_dev_t *hw, uint32_t slot) +{ + bool is_protected = hw->wrtprt.write_protect & BIT(slot); + return is_protected; +} + +/** + * @brief Switch between 3.3V and 1.8V mode + * + * @param hw hardware instance address + * @param slot slot + * @param en enable / disable 1.8V (3.3V on disable) + */ +static inline void sdmmc_ll_enable_1v8_mode(sdmmc_dev_t *hw, uint32_t slot, bool en) +{ + if (en) { + hw->uhs.volt |= BIT(slot); + } else { + hw->uhs.volt &= ~BIT(slot); + } +} + +/** + * @brief Enable DDR mode + * + * @param hw hardware instance address + * @param slot slot + * @param en enable / disable + */ +static inline void sdmmc_ll_enable_ddr_mode(sdmmc_dev_t *hw, uint32_t slot, bool en) +{ + if (en) { + hw->uhs.ddr |= BIT(slot); + hw->emmcddr.halfstartbit_reg |= BIT(slot); + } else { + hw->uhs.ddr &= ~BIT(slot); + hw->emmcddr.halfstartbit_reg &= ~BIT(slot); + } +} + +/** + * @brief Set data transfer length + * + * @param hw hardware instance address + * @param len length + */ +static inline void sdmmc_ll_set_data_transfer_len(sdmmc_dev_t *hw, uint32_t len) +{ + hw->bytcnt.byte_count = len; +} + +/** + * @brief Set block size + * + * @param hw hardware instance address + * @param block_size block size + */ +static inline void sdmmc_ll_set_block_size(sdmmc_dev_t *hw, uint32_t block_size) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->blksiz, block_size, block_size); +} + +/** + * @brief Set descriptor addr + * + * @param hw hardware instance address + * @param block_size block size + */ +static inline void sdmmc_ll_set_desc_addr(sdmmc_dev_t *hw, uint32_t desc_addr) +{ + hw->dbaddr.dbaddr_reg = desc_addr; +} + +/** + * @brief Poll demand + * + * @param hw hardware instance address + */ +static inline void sdmmc_ll_poll_demand(sdmmc_dev_t *hw) +{ + hw->pldmnd.pldmnd_pd = 1; +} + +/** + * @brief Set command + * + * @param hw hardware instance address + */ +static inline void sdmmc_ll_set_command(sdmmc_dev_t *hw, sdmmc_hw_cmd_t cmd) +{ + memcpy((void *)&hw->cmd, &cmd, sizeof(sdmmc_hw_cmd_t)); +} + +/** + * @brief Get if command is taken by CIU + * + * @param hw hardware instance address + * + * @return 1: is taken; 0: not taken, should not write to any command regs + */ +static inline bool sdmmc_ll_is_command_taken(sdmmc_dev_t *hw) +{ + return hw->cmd.start_command == 0; +} + +/** + * @brief Set command argument + * + * @param hw hardware instance address + * @param arg value indicates command argument to be passed to card + */ +static inline void sdmmc_ll_set_command_arg(sdmmc_dev_t *hw, uint32_t arg) +{ + hw->cmdarg = arg; +} + +/** + * @brief Get version ID + * + * @param hw hardware instance address + * + * @return version ID + */ +static inline uint32_t sdmmc_ll_get_version_id(sdmmc_dev_t *hw) +{ + return hw->verid; +} + +/** + * @brief Get hardware configuration info + * + * @param hw hardware instance address + * + * @return hardware configurations + */ +static inline uint32_t sdmmc_ll_get_hw_config_info(sdmmc_dev_t *hw) +{ + return hw->hcon.val; +} + +/** + * @brief Set card width + * + * @param hw hardware instance address + * @param slot slot ID + * @param width card width + */ +static inline void sdmmc_ll_set_card_width(sdmmc_dev_t *hw, uint32_t slot, sd_bus_width_t width) +{ + uint16_t mask = 1 << slot; + + switch (width) { + case SD_BUS_WIDTH_1_BIT: + hw->ctype.card_width8 &= ~mask; + hw->ctype.card_width4 &= ~mask; + break; + case SD_BUS_WIDTH_4_BIT: + hw->ctype.card_width8 &= ~mask; + hw->ctype.card_width4 |= mask; + break; + case SD_BUS_WIDTH_8_BIT: + hw->ctype.card_width8 |= mask; + break; + default: + HAL_ASSERT(false); + } +} + +/** + * @brief Is card data busy + * + * @param hw hardware instance address + * + * @return 1: busy; 0: idle + */ +static inline bool sdmmc_ll_is_card_data_busy(sdmmc_dev_t *hw) +{ + return hw->status.data_busy == 1; +} + +/*--------------------------------------------------------------- + DMA +---------------------------------------------------------------*/ +/** + * @brief Init DMA + * - enable dma + * - clear bus mode reg and reset all dmac internal regs + * - enable internal dmac interrupt + * + * @param hw hardware instance address + */ +static inline void sdmmc_ll_init_dma(sdmmc_dev_t *hw) +{ + hw->ctrl.dma_enable = 1; + hw->bmod.val = 0; + hw->bmod.bmod_swr = 1; + hw->idinten.idinten_ni = 1; + hw->idinten.idinten_ri = 1; + hw->idinten.idinten_ti = 1; + hw->fifoth.dma_multiple_transaction_size = 0x5; + hw->bmod.bmod_fb = 1; + hw->bmod.bmod_pbl = 0x7; +} + +/** + * @brief Enable DMA + * + * @param hw hardware instance address + * @param en enable / disable + */ +static inline void sdmmc_ll_enable_dma(sdmmc_dev_t *hw, bool en) +{ + hw->ctrl.dma_enable = en; + hw->ctrl.use_internal_dma = en; + hw->bmod.bmod_de = en; + hw->bmod.bmod_fb = en; +} + +/** + * @brief Stop DMA + * + * @param hw hardware instance address + */ +static inline void sdmmc_ll_stop_dma(sdmmc_dev_t *hw) +{ + hw->ctrl.use_internal_dma = 0; + hw->ctrl.dma_reset = 1; //here might be an issue as we don't wait the `dma_reset` to be self-cleared, check in next steps + hw->bmod.bmod_fb = 0; + hw->bmod.bmod_de = 0; +} + +/*--------------------------------------------------------------- + INTR +---------------------------------------------------------------*/ +/** + * @brief Get masked interrupt-status register value + * + * @param hw hardware instance address + */ +static inline uint32_t sdmmc_ll_get_intr_status(sdmmc_dev_t *hw) +{ + return hw->mintsts.val; +} + +/** + * @brief Enable interrupt + * + * @param hw hardware instance address + * @param mask interrupt mask + * @param en enable / disable + */ +static inline void sdmmc_ll_enable_interrupt(sdmmc_dev_t *hw, uint32_t mask, bool en) +{ + if (en) { + hw->intmask.val |= mask; + } else { + hw->intmask.val &= ~mask; + } +} + +/** + * @brief Get RAW interrupt-status register value + */ +static inline uint32_t sdmmc_ll_get_interrupt_raw(sdmmc_dev_t *hw) +{ + return hw->rintsts.val; +} + +/** + * @brief Clear interrupt + * + * @param hw hardware instance address + * @param mask interrupt mask + */ +static inline void sdmmc_ll_clear_interrupt(sdmmc_dev_t *hw, uint32_t mask) +{ + hw->rintsts.val = mask; +} + +/** + * @brief Enable / disable interrupts globally + * + * @param hw hardware instance address + * @param en enable / disable + */ +static inline void sdmmc_ll_enable_global_interrupt(sdmmc_dev_t *hw, bool en) +{ + hw->ctrl.int_enable = (uint32_t)en; +} + +/** + * @brief Enable / disable busy clear interrupt + * + * @param hw hardware instance address + * @param en enable / disable + */ +static inline void sdmmc_ll_enable_busy_clear_interrupt(sdmmc_dev_t *hw, bool en) +{ + hw->cardthrctl.cardclrinten_reg = en; +} + +/** + * @brief Get internal dmac status register val + */ +static inline uint32_t sdmmc_ll_get_idsts_interrupt_raw(sdmmc_dev_t *hw) +{ + return hw->idsts.val; +} + +/** + * @brief Clear internal dmac status register events + * + * @param hw hardware instance address + * @param mask interrupt mask + */ +static inline void sdmmc_ll_clear_idsts_interrupt(sdmmc_dev_t *hw, uint32_t mask) +{ + hw->idsts.val = mask; +} + +/** + * @brief Switch SDMMC IO power control source + * + * @param src SDMMC IO power control source + */ +static inline void sdmmc_ll_switch_io_power_control_src(sdmmc_ll_io_power_control_src_t src) +{ + if (src == SDMMC_LL_IO_POWER_CONTROL_SRC_INTERNAL) { + PMU.vdd_sdio_cfg.sdio_sw_power_sel = 0; + } else { + PMU.vdd_sdio_cfg.sdio_sw_power_sel = 1; + PMU.vdd_sdio_cfg.sdio_power_sel_sw_ctrl = 0; + } +} + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hal_sd/esp32s31/include/soc/sdmmc_pins.h b/components/esp_hal_sd/esp32s31/include/soc/sdmmc_pins.h new file mode 100644 index 00000000000..731ac527911 --- /dev/null +++ b/components/esp_hal_sd/esp32s31/include/soc/sdmmc_pins.h @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#define SDMMC_SLOT0_IOMUX_PIN_NUM_CLK 24 +#define SDMMC_SLOT0_IOMUX_PIN_NUM_CMD 25 +#define SDMMC_SLOT0_IOMUX_PIN_NUM_D0 20 +#define SDMMC_SLOT0_IOMUX_PIN_NUM_D1 21 +#define SDMMC_SLOT0_IOMUX_PIN_NUM_D2 22 +#define SDMMC_SLOT0_IOMUX_PIN_NUM_D3 23 +#define SDMMC_SLOT0_FUNC 0 + +#define SDMMC_SLOT1_IOMUX_PIN_NUM_CLK 39 +#define SDMMC_SLOT1_IOMUX_PIN_NUM_CMD 40 +#define SDMMC_SLOT1_IOMUX_PIN_NUM_D0 35 +#define SDMMC_SLOT1_IOMUX_PIN_NUM_D1 36 +#define SDMMC_SLOT1_IOMUX_PIN_NUM_D2 37 +#define SDMMC_SLOT1_IOMUX_PIN_NUM_D3 38 +#define SDMMC_SLOT1_FUNC 4 diff --git a/components/esp_hal_sd/esp32s31/sdmmc_periph.c b/components/esp_hal_sd/esp32s31/sdmmc_periph.c new file mode 100644 index 00000000000..7dd43282a92 --- /dev/null +++ b/components/esp_hal_sd/esp32s31/sdmmc_periph.c @@ -0,0 +1,77 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "hal/sdmmc_periph.h" +#include "soc/sdmmc_pins.h" + +const sdmmc_slot_info_t sdmmc_slot_info[SOC_SDMMC_NUM_SLOTS] = { + { + .width = 4, + .card_detect = SD_CARD_DETECT_N_1_PAD_IN_IDX, + .write_protect = SD_CARD_WRITE_PRT_1_PAD_IN_IDX, + .card_int = SD_CARD_INT_N_1_PAD_IN_IDX, + }, + { + .width = 4, + .card_detect = SD_CARD_DETECT_N_2_PAD_IN_IDX, + .write_protect = SD_CARD_WRITE_PRT_2_PAD_IN_IDX, + .card_int = SD_CARD_INT_N_2_PAD_IN_IDX, + } +}; + +const sdmmc_slot_io_info_t sdmmc_slot_gpio_num[SOC_SDMMC_NUM_SLOTS] = { + { + .clk = SDMMC_SLOT0_IOMUX_PIN_NUM_CLK, + .cmd = SDMMC_SLOT0_IOMUX_PIN_NUM_CMD, + .d0 = SDMMC_SLOT0_IOMUX_PIN_NUM_D0, + .d1 = SDMMC_SLOT0_IOMUX_PIN_NUM_D1, + .d2 = SDMMC_SLOT0_IOMUX_PIN_NUM_D2, + .d3 = SDMMC_SLOT0_IOMUX_PIN_NUM_D3, + .d4 = -1, + .d5 = -1, + .d6 = -1, + .d7 = -1, + }, + { + .clk = SDMMC_SLOT1_IOMUX_PIN_NUM_CLK, + .cmd = SDMMC_SLOT1_IOMUX_PIN_NUM_CMD, + .d0 = SDMMC_SLOT1_IOMUX_PIN_NUM_D0, + .d1 = SDMMC_SLOT1_IOMUX_PIN_NUM_D1, + .d2 = SDMMC_SLOT1_IOMUX_PIN_NUM_D2, + .d3 = SDMMC_SLOT1_IOMUX_PIN_NUM_D3, + .d4 = -1, + .d5 = -1, + .d6 = -1, + .d7 = -1, + } +}; + +const sdmmc_slot_io_info_t sdmmc_slot_gpio_sig[SOC_SDMMC_NUM_SLOTS] = { + { + .clk = -1, + .cmd = -1, + .d0 = -1, + .d1 = -1, + .d2 = -1, + .d3 = -1, + .d4 = -1, + .d5 = -1, + .d6 = -1, + .d7 = -1, + }, + { + .clk = -1, + .cmd = -1, + .d0 = -1, + .d1 = -1, + .d2 = -1, + .d3 = -1, + .d4 = -1, + .d5 = -1, + .d6 = -1, + .d7 = -1, + }, +}; diff --git a/components/esp_hal_sd/include/hal/sdmmc_hal.h b/components/esp_hal_sd/include/hal/sdmmc_hal.h index 4f3c36c8cf2..4700dcad483 100644 --- a/components/esp_hal_sd/include/hal/sdmmc_hal.h +++ b/components/esp_hal_sd/include/hal/sdmmc_hal.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -34,6 +34,13 @@ typedef struct { */ void sdmmc_hal_init(sdmmc_hal_context_t *hal); +/** + * @brief Deinit the sdmmc hal context. + * + * @param hal Context of the HAL + */ +void sdmmc_hal_deinit(sdmmc_hal_context_t *hal); + #ifdef __cplusplus } #endif diff --git a/components/esp_hal_sd/sdmmc_hal.c b/components/esp_hal_sd/sdmmc_hal.c index 0eb76f82d1c..87747d990f2 100644 --- a/components/esp_hal_sd/sdmmc_hal.c +++ b/components/esp_hal_sd/sdmmc_hal.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,4 +12,13 @@ void sdmmc_hal_init(sdmmc_hal_context_t *hal) { hal->dev = SDMMC_LL_GET_HW(0); + + sdmmc_ll_mem_power_by_pmu(hal->dev); + sdmmc_ll_mem_set_low_power_mode(hal->dev, SDMMC_LL_MEM_LP_MODE_SHUT_DOWN); // power down memory during low power stage +} + +void sdmmc_hal_deinit(sdmmc_hal_context_t *hal) +{ + sdmmc_ll_mem_force_low_power(hal->dev); // power off SDMMC power domain forcefully + hal->dev = NULL; } diff --git a/components/sdmmc/sdmmc_sd.c b/components/sdmmc/sdmmc_sd.c index 156e396643f..50062d6d1d0 100644 --- a/components/sdmmc/sdmmc_sd.c +++ b/components/sdmmc/sdmmc_sd.c @@ -21,6 +21,10 @@ #include "esp_cache.h" #include "esp_private/sdmmc_common.h" #include "freertos/FreeRTOS.h" +#include "soc/soc_caps.h" +#if SOC_SDMMC_HOST_SUPPORTED +#include "hal/sdmmc_ll.h" +#endif #define SDMMC_DELAY_NUMS_MAX 10 @@ -441,7 +445,7 @@ esp_err_t sdmmc_do_timing_tuning(sdmmc_card_t *card, sdmmc_delay_mode_t delay_mo int results[SDMMC_DELAY_NUMS_MAX] = {}; int start_delay_item = (delay_mode == SDMMC_DELAY_MODE_PHASE) ? SDMMC_DELAY_PHASE_0 : SDMMC_DELAY_LINE_0; int slot = card->host.slot; - int delay_total_nums = 4; + int delay_total_nums = 5; if (delay_mode == SDMMC_DELAY_MODE_PHASE) { if (card->host.max_freq_khz == SDMMC_FREQ_SDR104) { delay_total_nums = SDMMC_DELAY_PHASE_AUTO; @@ -567,9 +571,15 @@ esp_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card) static esp_err_t sdmmc_init_sd_uhs1_volt_sw_cb(void* arg, int voltage_mv) { - sdmmc_card_t* card = (sdmmc_card_t*)arg; ESP_LOGV(TAG, "%s: Voltage switch callback (%umv)", __func__, voltage_mv); + +#if SOC_SDMMC_IO_UHS_POWER_EXTERNAL + sdmmc_ll_switch_io_power_control_src(SDMMC_LL_IO_POWER_CONTROL_SRC_LDO); + return ESP_OK; +#else + sdmmc_card_t* card = (sdmmc_card_t*)arg; return sd_pwr_ctrl_set_io_voltage(card->host.pwr_ctrl_handle, voltage_mv); +#endif } esp_err_t sdmmc_init_sd_uhs1(sdmmc_card_t* card) diff --git a/components/sdmmc/test_apps/README.md b/components/sdmmc/test_apps/README.md index c3772ae63d4..348f511d13d 100644 --- a/components/sdmmc/test_apps/README.md +++ b/components/sdmmc/test_apps/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-P4 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-P4 | ESP32-S3 | ESP32-S31 | +| ----------------- | ----- | -------- | -------- | --------- | This is a test app for sdmmc component. This app is for internal use. diff --git a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in index 9e0b8ea5479..a69e7315b1d 100644 --- a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in @@ -243,6 +243,10 @@ config SOC_PSRAM_DMA_CAPABLE bool default y +config SOC_SDMMC_HOST_SUPPORTED + bool + default y + config SOC_CLK_TREE_SUPPORTED bool default y @@ -275,6 +279,10 @@ config SOC_TOUCH_SENSOR_SUPPORTED bool default y +config SOC_GP_LDO_SUPPORTED + bool + default y + config SOC_RNG_SUPPORTED bool default y @@ -783,6 +791,34 @@ config SOC_MMU_PER_EXT_MEM_TARGET bool default y +config SOC_SDMMC_USE_IOMUX + bool + default y + +config SOC_SDMMC_NUM_SLOTS + int + default 2 + +config SOC_SDMMC_DATA_WIDTH_MAX + int + default 4 + +config SOC_SDMMC_DELAY_PHASE_NUM + int + default 8 + +config SOC_SDMMC_IO_UHS_POWER_EXTERNAL + bool + default y + +config SOC_SDMMC_PSRAM_DMA_CAPABLE + bool + default y + +config SOC_SDMMC_UHS_I_SUPPORTED + bool + default y + config SOC_SPI_PERIPH_NUM int default 3 diff --git a/components/soc/esp32s31/include/soc/clk_tree_defs.h b/components/soc/esp32s31/include/soc/clk_tree_defs.h index 1ab59aa7cb4..55e54163c72 100644 --- a/components/soc/esp32s31/include/soc/clk_tree_defs.h +++ b/components/soc/esp32s31/include/soc/clk_tree_defs.h @@ -340,6 +340,21 @@ typedef enum { LP_I2C_SCLK_DEFAULT = SOC_MOD_CLK_RTC_FAST, /*!< LP_I2C source clock default choice is RTC_FAST */ } soc_periph_lp_i2c_clk_src_t; +//////////////////////////////////////////////////SDMMC/////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of SDMMC + */ +#define SOC_SDMMC_CLKS {SOC_MOD_CLK_MPLL} + +/** + * @brief Type of SDMMC clock source + */ +typedef enum { + SDMMC_CLK_SRC_DEFAULT = SOC_MOD_CLK_MPLL, /*!< Select MPLL as the default choice */ + SDMMC_CLK_SRC_MPLL = SOC_MOD_CLK_MPLL, /*!< Select MPLL as the source clock */ +} soc_periph_sdmmc_clk_src_t; + /////////////////////////////////////////////////SPI//////////////////////////////////////////////////////////////////// /** diff --git a/components/soc/esp32s31/include/soc/soc_caps.h b/components/soc/esp32s31/include/soc/soc_caps.h index 87fec4c1b63..1126f4b8071 100644 --- a/components/soc/esp32s31/include/soc/soc_caps.h +++ b/components/soc/esp32s31/include/soc/soc_caps.h @@ -89,7 +89,7 @@ #define SOC_LP_SPI_SUPPORTED 1 #define SOC_SPIRAM_SUPPORTED 1 #define SOC_PSRAM_DMA_CAPABLE 1 -// #define SOC_SDMMC_HOST_SUPPORTED 1 // TODO: [ESP32S31] IDF-14705 +#define SOC_SDMMC_HOST_SUPPORTED 1 #define SOC_CLK_TREE_SUPPORTED 1 #define SOC_ASSIST_DEBUG_SUPPORTED 1 #define SOC_CPU_LOCKUP_DEBUG_SUPPORTED 1 @@ -99,6 +99,7 @@ #define SOC_SPI_FLASH_SUPPORTED 1 // TODO: [ESP32S31] IDF-14777 #define SOC_SPI_FLASH_HAS_DEDICATED_LDO 1 #define SOC_TOUCH_SENSOR_SUPPORTED 1 +#define SOC_GP_LDO_SUPPORTED 1 // General purpose LDO #define SOC_RNG_SUPPORTED 1 #define SOC_PPA_SUPPORTED 1 #define SOC_LIGHT_SLEEP_SUPPORTED 1 @@ -301,6 +302,16 @@ #define SOC_MMU_DI_VADDR_SHARED (1) /*!< D/I vaddr are shared */ #define SOC_MMU_PER_EXT_MEM_TARGET (1) /*!< MMU is per physical external memory target (flash, psram) */ +/*-------------------------- SDMMC CAPS -----------------------------------------*/ +#define SOC_SDMMC_USE_IOMUX 1 +#define SOC_SDMMC_NUM_SLOTS 2 +#define SOC_SDMMC_DATA_WIDTH_MAX 4 +/* Supported host clock delay phase number */ +#define SOC_SDMMC_DELAY_PHASE_NUM 8 +#define SOC_SDMMC_IO_UHS_POWER_EXTERNAL 1 ///< SDMMC IO power controlled by external power supply +#define SOC_SDMMC_PSRAM_DMA_CAPABLE 1 ///< SDMMC peripheral can do DMA transfer to/from PSRAMsts +#define SOC_SDMMC_UHS_I_SUPPORTED 1 + /*-------------------------- SPI CAPS ----------------------------------------*/ #define SOC_SPI_PERIPH_NUM 3 #define SOC_SPI_MAXIMUM_BUFFER_SIZE 64 diff --git a/components/soc/esp32s31/register/soc/cnnt_sys_struct.h b/components/soc/esp32s31/register/soc/cnnt_sys_struct.h index caf012f8c57..0e755471ae6 100644 --- a/components/soc/esp32s31/register/soc/cnnt_sys_struct.h +++ b/components/soc/esp32s31/register/soc/cnnt_sys_struct.h @@ -579,7 +579,7 @@ typedef union { } cnnt_sys_gmac_ctrl2_reg_t; -typedef struct { +typedef struct cnnt_dev_t { volatile cnnt_sys_ver_date_reg_t sys_ver_date; volatile cnnt_clk_en_reg_t clk_en; uint32_t reserved_008[2]; diff --git a/components/soc/esp32s31/register/soc/sdmmc_reg.h b/components/soc/esp32s31/register/soc/sdmmc_reg.h index 075c6e38bc0..ebbd4073bf5 100644 --- a/components/soc/esp32s31/register/soc/sdmmc_reg.h +++ b/components/soc/esp32s31/register/soc/sdmmc_reg.h @@ -1,5 +1,5 @@ /** - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 OR MIT */ @@ -162,7 +162,7 @@ extern "C" { * Clock source selection register */ #define SDHOST_CLKSRC_REG (DR_REG_SDHOST_BASE + 0xc) -/** SDHOST_CLKSRC_REG : R/W; bitpos: [3:0]; default: 0; +/** SDHOST_CLKSRC : R/W; bitpos: [3:0]; default: 0; * Clock divider source for two SD cards is supported. Each card has two bits assigned * to it. For example, bit[1:0] are assigned for card 0, bit[3:2] are assigned for * card 1. Card 0 maps and internally routes clock divider[0:3] outputs to @@ -172,10 +172,10 @@ extern "C" { * 10 : Clock divider 2; * 11 : Clock divider 3. */ -#define SDHOST_CLKSRC_REG 0x0000000FU -#define SDHOST_CLKSRC_REG_M (SDHOST_CLKSRC_REG_V << SDHOST_CLKSRC_REG_S) -#define SDHOST_CLKSRC_REG_V 0x0000000FU -#define SDHOST_CLKSRC_REG_S 0 +#define SDHOST_CLKSRC 0x0000000FU +#define SDHOST_CLKSRC_M (SDHOST_CLKSRC_V << SDHOST_CLKSRC_S) +#define SDHOST_CLKSRC_V 0x0000000FU +#define SDHOST_CLKSRC_S 0 /** SDHOST_CLKENA_REG register * Clock enable register @@ -320,13 +320,13 @@ extern "C" { * Command argument data register */ #define SDHOST_CMDARG_REG (DR_REG_SDHOST_BASE + 0x28) -/** SDHOST_CMDARG_REG : R/W; bitpos: [31:0]; default: 0; +/** SDHOST_CMDARG : R/W; bitpos: [31:0]; default: 0; * Value indicates command argument to be passed to the card. */ -#define SDHOST_CMDARG_REG 0xFFFFFFFFU -#define SDHOST_CMDARG_REG_M (SDHOST_CMDARG_REG_V << SDHOST_CMDARG_REG_S) -#define SDHOST_CMDARG_REG_V 0xFFFFFFFFU -#define SDHOST_CMDARG_REG_S 0 +#define SDHOST_CMDARG 0xFFFFFFFFU +#define SDHOST_CMDARG_M (SDHOST_CMDARG_V << SDHOST_CMDARG_S) +#define SDHOST_CMDARG_V 0xFFFFFFFFU +#define SDHOST_CMDARG_S 0 /** SDHOST_CMD_REG register * Command and boot configuration register @@ -807,25 +807,25 @@ extern "C" { * Transferred byte count register */ #define SDHOST_TCBCNT_REG (DR_REG_SDHOST_BASE + 0x5c) -/** SDHOST_TCBCNT_REG : RO; bitpos: [31:0]; default: 0; +/** SDHOST_TCBCNT : RO; bitpos: [31:0]; default: 0; * Number of bytes transferred by CIU unit to card. */ -#define SDHOST_TCBCNT_REG 0xFFFFFFFFU -#define SDHOST_TCBCNT_REG_M (SDHOST_TCBCNT_REG_V << SDHOST_TCBCNT_REG_S) -#define SDHOST_TCBCNT_REG_V 0xFFFFFFFFU -#define SDHOST_TCBCNT_REG_S 0 +#define SDHOST_TCBCNT 0xFFFFFFFFU +#define SDHOST_TCBCNT_M (SDHOST_TCBCNT_V << SDHOST_TCBCNT_S) +#define SDHOST_TCBCNT_V 0xFFFFFFFFU +#define SDHOST_TCBCNT_S 0 /** SDHOST_TBBCNT_REG register * Transferred byte count register */ #define SDHOST_TBBCNT_REG (DR_REG_SDHOST_BASE + 0x60) -/** SDHOST_TBBCNT_REG : RO; bitpos: [31:0]; default: 0; +/** SDHOST_TBBCNT : RO; bitpos: [31:0]; default: 0; * Number of bytes transferred between Host/DMA memory and BIU FIFO. */ -#define SDHOST_TBBCNT_REG 0xFFFFFFFFU -#define SDHOST_TBBCNT_REG_M (SDHOST_TBBCNT_REG_V << SDHOST_TBBCNT_REG_S) -#define SDHOST_TBBCNT_REG_V 0xFFFFFFFFU -#define SDHOST_TBBCNT_REG_S 0 +#define SDHOST_TBBCNT 0xFFFFFFFFU +#define SDHOST_TBBCNT_M (SDHOST_TBBCNT_V << SDHOST_TBBCNT_S) +#define SDHOST_TBBCNT_V 0xFFFFFFFFU +#define SDHOST_TBBCNT_S 0 /** SDHOST_DEBNCE_REG register * Debounce filter time configuration register @@ -845,14 +845,14 @@ extern "C" { * User ID (scratchpad) register */ #define SDHOST_USRID_REG (DR_REG_SDHOST_BASE + 0x68) -/** SDHOST_USRID_REG : R/W; bitpos: [31:0]; default: 0; +/** SDHOST_USRID : R/W; bitpos: [31:0]; default: 0; * User identification register, value set by user. Can also be used as a scratchpad * register by user. */ -#define SDHOST_USRID_REG 0xFFFFFFFFU -#define SDHOST_USRID_REG_M (SDHOST_USRID_REG_V << SDHOST_USRID_REG_S) -#define SDHOST_USRID_REG_V 0xFFFFFFFFU -#define SDHOST_USRID_REG_S 0 +#define SDHOST_USRID 0xFFFFFFFFU +#define SDHOST_USRID_M (SDHOST_USRID_V << SDHOST_USRID_S) +#define SDHOST_USRID_V 0xFFFFFFFFU +#define SDHOST_USRID_S 0 /** SDHOST_VERID_REG register * Version ID (scratchpad) register @@ -1035,15 +1035,15 @@ extern "C" { * Descriptor base address register */ #define SDHOST_DBADDR_REG (DR_REG_SDHOST_BASE + 0x88) -/** SDHOST_DBADDR_REG : R/W; bitpos: [31:0]; default: 0; +/** SDHOST_DBADDR : R/W; bitpos: [31:0]; default: 0; * Start of Descriptor List. Contains the base address of the First Descriptor. The * LSB bits [1:0] are ignored and taken as all-zero by the IDMAC internally. Hence * these LSB bits may be treated as read-only. */ -#define SDHOST_DBADDR_REG 0xFFFFFFFFU -#define SDHOST_DBADDR_REG_M (SDHOST_DBADDR_REG_V << SDHOST_DBADDR_REG_S) -#define SDHOST_DBADDR_REG_V 0xFFFFFFFFU -#define SDHOST_DBADDR_REG_S 0 +#define SDHOST_DBADDR 0xFFFFFFFFU +#define SDHOST_DBADDR_M (SDHOST_DBADDR_V << SDHOST_DBADDR_S) +#define SDHOST_DBADDR_V 0xFFFFFFFFU +#define SDHOST_DBADDR_S 0 /** SDHOST_IDSTS_REG register * IDMAC status register @@ -1219,29 +1219,29 @@ extern "C" { * Host descriptor address pointer */ #define SDHOST_DSCADDR_REG (DR_REG_SDHOST_BASE + 0x94) -/** SDHOST_DSCADDR_REG : RO; bitpos: [31:0]; default: 0; +/** SDHOST_DSCADDR : RO; bitpos: [31:0]; default: 0; * Host Descriptor Address Pointer, updated by IDMAC during operation and cleared on * reset. This register points to the start address of the current descriptor read by * the IDMAC. */ -#define SDHOST_DSCADDR_REG 0xFFFFFFFFU -#define SDHOST_DSCADDR_REG_M (SDHOST_DSCADDR_REG_V << SDHOST_DSCADDR_REG_S) -#define SDHOST_DSCADDR_REG_V 0xFFFFFFFFU -#define SDHOST_DSCADDR_REG_S 0 +#define SDHOST_DSCADDR 0xFFFFFFFFU +#define SDHOST_DSCADDR_M (SDHOST_DSCADDR_V << SDHOST_DSCADDR_S) +#define SDHOST_DSCADDR_V 0xFFFFFFFFU +#define SDHOST_DSCADDR_S 0 /** SDHOST_BUFADDR_REG register * Host buffer address pointer register */ #define SDHOST_BUFADDR_REG (DR_REG_SDHOST_BASE + 0x98) -/** SDHOST_BUFADDR_REG : RO; bitpos: [31:0]; default: 0; +/** SDHOST_BUFADDR : RO; bitpos: [31:0]; default: 0; * Host Buffer Address Pointer, updated by IDMAC during operation and cleared on * reset. This register points to the current Data Buffer Address being accessed by * the IDMAC. */ -#define SDHOST_BUFADDR_REG 0xFFFFFFFFU -#define SDHOST_BUFADDR_REG_M (SDHOST_BUFADDR_REG_V << SDHOST_BUFADDR_REG_S) -#define SDHOST_BUFADDR_REG_V 0xFFFFFFFFU -#define SDHOST_BUFADDR_REG_S 0 +#define SDHOST_BUFADDR 0xFFFFFFFFU +#define SDHOST_BUFADDR_M (SDHOST_BUFADDR_V << SDHOST_BUFADDR_S) +#define SDHOST_BUFADDR_V 0xFFFFFFFFU +#define SDHOST_BUFADDR_S 0 /** SDHOST_CARDTHRCTL_REG register * Card Threshold Control register @@ -1327,14 +1327,14 @@ extern "C" { * CPU write and read transmit data by FIFO */ #define SDHOST_BUFFIFO_REG (DR_REG_SDHOST_BASE + 0x200) -/** SDHOST_BUFFIFO_REG : R/W; bitpos: [31:0]; default: 0; +/** SDHOST_BUFFIFO : R/W; bitpos: [31:0]; default: 0; * CPU write and read transmit data by FIFO. This register points to the current Data * FIFO . */ -#define SDHOST_BUFFIFO_REG 0xFFFFFFFFU -#define SDHOST_BUFFIFO_REG_M (SDHOST_BUFFIFO_REG_V << SDHOST_BUFFIFO_REG_S) -#define SDHOST_BUFFIFO_REG_V 0xFFFFFFFFU -#define SDHOST_BUFFIFO_REG_S 0 +#define SDHOST_BUFFIFO 0xFFFFFFFFU +#define SDHOST_BUFFIFO_M (SDHOST_BUFFIFO_V << SDHOST_BUFFIFO_S) +#define SDHOST_BUFFIFO_V 0xFFFFFFFFU +#define SDHOST_BUFFIFO_S 0 /** SDHOST_CLK_EDGE_SEL_REG register * SDIO control register. @@ -1492,6 +1492,34 @@ extern "C" { #define SDHOST_DLL_CAL_END_V 0x00000001U #define SDHOST_DLL_CAL_END_S 1 +#define SDMMC_INTMASK_IO_SLOT1 BIT(17) +#define SDMMC_INTMASK_IO_SLOT0 BIT(16) +#define SDMMC_INTMASK_EBE BIT(15) +#define SDMMC_INTMASK_ACD BIT(14) +#define SDMMC_INTMASK_SBE BIT(13) +#define SDMMC_INTMASK_HLE BIT(12) +#define SDMMC_INTMASK_FRUN BIT(11) +#define SDMMC_INTMASK_HTO BIT(10) +#define SDMMC_INTMASK_VOLT_SW SDMMC_INTMASK_HTO +#define SDMMC_INTMASK_DTO BIT(9) +#define SDMMC_INTMASK_RTO BIT(8) +#define SDMMC_INTMASK_DCRC BIT(7) +#define SDMMC_INTMASK_RCRC BIT(6) +#define SDMMC_INTMASK_RXDR BIT(5) +#define SDMMC_INTMASK_TXDR BIT(4) +#define SDMMC_INTMASK_DATA_OVER BIT(3) +#define SDMMC_INTMASK_CMD_DONE BIT(2) +#define SDMMC_INTMASK_RESP_ERR BIT(1) +#define SDMMC_INTMASK_CD BIT(0) + +#define SDMMC_IDMAC_INTMASK_AI BIT(9) +#define SDMMC_IDMAC_INTMASK_NI BIT(8) +#define SDMMC_IDMAC_INTMASK_CES BIT(5) +#define SDMMC_IDMAC_INTMASK_DU BIT(4) +#define SDMMC_IDMAC_INTMASK_FBE BIT(2) +#define SDMMC_IDMAC_INTMASK_RI BIT(1) +#define SDMMC_IDMAC_INTMASK_TI BIT(0) + #ifdef __cplusplus } #endif diff --git a/components/soc/esp32s31/register/soc/sdmmc_struct.h b/components/soc/esp32s31/register/soc/sdmmc_struct.h index 1a20d4772a6..e076f3134c4 100644 --- a/components/soc/esp32s31/register/soc/sdmmc_struct.h +++ b/components/soc/esp32s31/register/soc/sdmmc_struct.h @@ -1,5 +1,5 @@ /** - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 OR MIT */ @@ -10,6 +10,36 @@ extern "C" { #endif +typedef struct sdmmc_desc_s { + struct { + uint32_t reserved1: 1; + uint32_t disable_int_on_completion: 1; + uint32_t last_descriptor: 1; + uint32_t first_descriptor: 1; + uint32_t second_address_chained: 1; + uint32_t end_of_ring: 1; + uint32_t reserved2: 24; + uint32_t card_error_summary: 1; + uint32_t owned_by_idmac: 1; + }; + struct { + uint32_t buffer1_size: 13; + uint32_t buffer2_size: 13; + uint32_t reserved3: 6; + }; + void* buffer1_ptr; + union { + void* buffer2_ptr; + void* next_desc_ptr; + }; +} sdmmc_desc_t; + +#define SDMMC_DMA_MAX_BUF_LEN 4096 + +#ifndef __cplusplus +_Static_assert(sizeof(sdmmc_desc_t) == 16, "invalid size of sdmmc_desc_t structure"); +#endif + /** Group: Control register */ /** Type of ctrl register * Control register @@ -39,7 +69,7 @@ typedef union { * Global interrupt enable/disable bit. 0: Disable; 1: Enable. */ uint32_t int_enable:1; - uint32_t reserved_5:1; + uint32_t dma_enable:1; /** read_wait : R/W; bitpos: [6]; default: 0; * For sending read-wait to SDIO cards. */ @@ -90,7 +120,12 @@ typedef union { * then software should set this bit. */ uint32_t ceata_device_interrupt_status:1; - uint32_t reserved_12:20; + uint32_t reserved2:4; + uint32_t card_voltage_a:4; + uint32_t card_voltage_b:4; + uint32_t enable_od_pullup:1; + uint32_t use_internal_dma:1; + uint32_t reserved3:6; }; uint32_t val; } sdhost_ctrl_reg_t; @@ -147,7 +182,8 @@ typedef union { * 10 : Clock divider 2; * 11 : Clock divider 3. */ - uint32_t clksrc_reg:4; + uint32_t card0:2; + uint32_t card1:2; uint32_t reserved_4:28; }; uint32_t val; @@ -334,10 +370,10 @@ typedef union { * 0: No response expected from card; 1: Response expected from card. */ uint32_t response_expect:1; - /** response_length : R/W; bitpos: [7]; default: 0; + /** response_long : R/W; bitpos: [7]; default: 0; * 0: Short response expected from card; 1: Long response expected from card. */ - uint32_t response_length:1; + uint32_t response_long:1; /** check_response_crc : R/W; bitpos: [8]; default: 0; * 0: Do not check; 1: Check response CRC. * Some of command responses do not return valid CRC bits. Software should disable CRC @@ -348,11 +384,11 @@ typedef union { * 0: No data transfer expected; 1: Data transfer expected. */ uint32_t data_expected:1; - /** read_write : R/W; bitpos: [10]; default: 0; + /** rw : R/W; bitpos: [10]; default: 0; * 0: Read from card; 1: Write to card. * Don't care if no data is expected from card. */ - uint32_t read_write:1; + uint32_t rw:1; /** transfer_mode : R/W; bitpos: [11]; default: 0; * 0: Block data transfer command; 1: Stream data transfer command. * Don't care if no data expected. @@ -363,14 +399,14 @@ typedef union { * end of data transfer. */ uint32_t send_auto_stop:1; - /** wait_prvdata_complete : R/W; bitpos: [13]; default: 0; + /** wait_complete : R/W; bitpos: [13]; default: 0; * 0: Send command at once, even if previous data transfer has not completed; 1: Wait * for previous data transfer to complete before sending Command. - * The SDHOST_WAIT_PRVDATA_COMPLETE] = 0 option is typically used to query status of + * The SDHOST_WAIT_COMPLETE] = 0 option is typically used to query status of * card during data transfer or to stop current data transfer. SDHOST_CARD_NUMBERr * should be same as in previous command. */ - uint32_t wait_prvdata_complete:1; + uint32_t wait_complete:1; /** stop_abort_cmd : R/W; bitpos: [14]; default: 0; * 0: Neither stop nor abort command can stop current data transfer. If abort is sent * to function-number currently selected or not in data-transfer mode, then bit should @@ -381,20 +417,20 @@ typedef union { * state-machines of CIU can return correctly to idle state. */ uint32_t stop_abort_cmd:1; - /** send_initialization : R/W; bitpos: [15]; default: 0; + /** send_init : R/W; bitpos: [15]; default: 0; * 0: Do not send initialization sequence (80 clocks of 1) before sending this * command; 1: Send initialization sequence before sending this command. * After powered on, 80 clocks must be sent to card for initialization before sending * any commands to card. Bit should be set while sending first command to card so that * controller will initialize clocks before sending command to card. */ - uint32_t send_initialization:1; - /** card_number : R/W; bitpos: [20:16]; default: 0; + uint32_t send_init:1; + /** card_num : R/W; bitpos: [20:16]; default: 0; * Card number in use. Represents physical slot number of card being accessed. In * SD-only mode, up to two cards are supported. */ - uint32_t card_number:5; - /** update_clock_registers_only : R/W; bitpos: [21]; default: 0; + uint32_t card_num:5; + /** update_clk_reg : R/W; bitpos: [21]; default: 0; * 0: Normal command sequence; 1: Do not send commands, just update clock register * value into card clock domain. * Following register values are transferred into card clock domain: CLKDIV, CLRSRC, @@ -407,7 +443,7 @@ typedef union { * register values for new command sequence to card(s). When bit is set, there are no * Command Done interrupts because no command is sent to SD_MMC_CEATA cards. */ - uint32_t update_clock_registers_only:1; + uint32_t update_clk_reg:1; /** read_ceata_device : R/W; bitpos: [22]; default: 0; * Read access flag. * 0: Host is not performing read access (RW_REG or RW_BLK)towards CE-ATA device; @@ -431,83 +467,33 @@ typedef union { * not masked. */ uint32_t ccs_expected:1; - uint32_t reserved_24:5; + uint32_t reserved_24:4; + /** volt_switch : R/W; bitpos: [28]; default: 0; + * Voltage switch bit. + * 0: No voltage switching. + * 1: Voltage switching enabled; must be set for CMD11 only. + */ + uint32_t volt_switch:1; /** use_hole_reg : R/W; bitpos: [29]; default: 1; * Use Hold Register. * 0: CMD and DATA sent to card bypassing HOLD Register; * 1: CMD and DATA sent to card through the HOLD Register. */ - uint32_t use_hole_reg:1; + uint32_t use_hold_reg:1; uint32_t reserved_30:1; - /** start_cmd : R/W; bitpos: [31]; default: 0; + /** start_command : R/W; bitpos: [31]; default: 0; * Start command. Once command is served by the CIU, this bit is automatically * cleared. When this bit is set, host should not attempt to write to any command * registers. If a write is attempted, hardware lock error is set in raw interrupt * register. Once command is sent and a response is received from SD_MMC_CEATA cards, * Command Done bit is set in the raw interrupt Register. */ - uint32_t start_cmd:1; + uint32_t start_command:1; }; uint32_t val; } sdhost_cmd_reg_t; -/** Group: Response data register */ -/** Type of resp0 register - * Response data register - */ -typedef union { - struct { - /** response0_reg : RO; bitpos: [31:0]; default: 0; - * Bit[31:0] of response. - */ - uint32_t response0_reg:32; - }; - uint32_t val; -} sdhost_resp0_reg_t; - - -/** Group: Long response data register */ -/** Type of resp1 register - * Long response data register - */ -typedef union { - struct { - /** response1_reg : RO; bitpos: [31:0]; default: 0; - * Bit[63:32] of long response. - */ - uint32_t response1_reg:32; - }; - uint32_t val; -} sdhost_resp1_reg_t; - -/** Type of resp2 register - * Long response data register - */ -typedef union { - struct { - /** response2_reg : RO; bitpos: [31:0]; default: 0; - * Bit[95:64] of long response. - */ - uint32_t response2_reg:32; - }; - uint32_t val; -} sdhost_resp2_reg_t; - -/** Type of resp3 register - * Long response data register - */ -typedef union { - struct { - /** response3_reg : RO; bitpos: [31:0]; default: 0; - * Bit[127:96] of long response. - */ - uint32_t response3_reg:32; - }; - uint32_t val; -} sdhost_resp3_reg_t; - - /** Group: Masked interrupt status register */ /** Type of mintsts register * Masked interrupt status register @@ -880,13 +866,18 @@ typedef union { */ typedef union { struct { - uint32_t reserved_0:16; - /** ddr_reg : R/W; bitpos: [17:16]; default: 0; + /** volt: R/W; bitpos: [1:0]; default: 0; + * 0: 3.3V mode. + * 1: 1.8V mode. + */ + uint32_t volt:2; + uint32_t reserved_0:14; + /** ddr : R/W; bitpos: [17:16]; default: 0; * DDR mode selection,1 bit for each card. * 0-Non-DDR mode. * 1-DDR mode. */ - uint32_t ddr_reg:2; + uint32_t ddr:2; uint32_t reserved_18:14; }; uint32_t val; @@ -1392,7 +1383,7 @@ typedef union { } sdhost_dll_conf_reg_t; -typedef struct { +typedef struct sdmmc_dev_t { volatile sdhost_ctrl_reg_t ctrl; uint32_t reserved_004; volatile sdhost_clkdiv_reg_t clkdiv; @@ -1403,12 +1394,9 @@ typedef struct { volatile sdhost_blksiz_reg_t blksiz; volatile sdhost_bytcnt_reg_t bytcnt; volatile sdhost_intmask_reg_t intmask; - volatile sdhost_cmdarg_reg_t cmdarg; + volatile uint32_t cmdarg; volatile sdhost_cmd_reg_t cmd; - volatile sdhost_resp0_reg_t resp0; - volatile sdhost_resp1_reg_t resp1; - volatile sdhost_resp2_reg_t resp2; - volatile sdhost_resp3_reg_t resp3; + volatile uint32_t resp[4]; volatile sdhost_mintsts_reg_t mintsts; volatile sdhost_rintsts_reg_t rintsts; volatile sdhost_status_reg_t status; @@ -1420,7 +1408,7 @@ typedef struct { volatile sdhost_tbbcnt_reg_t tbbcnt; volatile sdhost_debnce_reg_t debnce; volatile sdhost_usrid_reg_t usrid; - volatile sdhost_verid_reg_t verid; + volatile uint32_t verid; volatile sdhost_hcon_reg_t hcon; volatile sdhost_uhs_reg_t uhs; volatile sdhost_rst_n_reg_t rst_n; @@ -1446,6 +1434,9 @@ typedef struct { volatile sdhost_dll_conf_reg_t dll_conf; } sdmmc_dev_t; +extern sdmmc_dev_t SDMMC; + +typedef sdhost_cmd_reg_t sdmmc_hw_cmd_t; #ifndef __cplusplus _Static_assert(sizeof(sdmmc_dev_t) == 0x810, "Invalid size of sdmmc_dev_t structure"); diff --git a/docs/conf_common.py b/docs/conf_common.py index 05b6838aba9..21c519ebb66 100644 --- a/docs/conf_common.py +++ b/docs/conf_common.py @@ -340,6 +340,7 @@ ESP32H4_DOCS = [ ESP32S31_DOCS = [ 'api-reference/system/ipc.rst', + 'api-reference/peripherals/sd_pullup_requirements.rst', 'api-guides/RF_calibration.rst', 'api-guides/phy.rst', ] diff --git a/docs/en/api-reference/peripherals/sdmmc_host.rst b/docs/en/api-reference/peripherals/sdmmc_host.rst index 9561f0d4973..19b40c24cff 100644 --- a/docs/en/api-reference/peripherals/sdmmc_host.rst +++ b/docs/en/api-reference/peripherals/sdmmc_host.rst @@ -78,6 +78,11 @@ Overview On {IDF_TARGET_NAME}, SDMMC host requires an external power supply for the IO voltage. Please refer to :ref:`pwr-ctrl` for details. +.. only:: esp32s31 + + - Both slots :c:macro:`SDMMC_HOST_SLOT_0` and :c:macro:`SDMMC_HOST_SLOT_1` are dedicated IOs. + - :c:macro:`SDMMC_HOST_SLOT_0` supports UHS-I mode. + Supported Speed Modes --------------------- diff --git a/examples/peripherals/i2s/i2s_recorder/README.md b/examples/peripherals/i2s/i2s_recorder/README.md index 3addb4baaaa..0b5c6e4ae40 100644 --- a/examples/peripherals/i2s/i2s_recorder/README.md +++ b/examples/peripherals/i2s/i2s_recorder/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-P4 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-P4 | ESP32-S3 | ESP32-S31 | +| ----------------- | ----- | -------- | -------- | --------- | # I2S Digital Microphone Recording Example diff --git a/examples/peripherals/i2s/i2s_recorder/pytest_i2s_record.py b/examples/peripherals/i2s/i2s_recorder/pytest_i2s_record.py index be6b9f241b3..874a9427c25 100644 --- a/examples/peripherals/i2s/i2s_recorder/pytest_i2s_record.py +++ b/examples/peripherals/i2s/i2s_recorder/pytest_i2s_record.py @@ -6,7 +6,7 @@ from pytest_embedded_idf.utils import idf_parametrize @pytest.mark.generic -@idf_parametrize('target', ['esp32', 'esp32s3', 'esp32p4'], indirect=['target']) +@idf_parametrize('target', ['esp32', 'esp32s3', 'esp32p4', 'esp32s31'], indirect=['target']) def test_i2s_recorder_generic(dut: Dut) -> None: dut.expect('PDM microphone recording example start') dut.expect('--------------------------------------') diff --git a/examples/peripherals/jpeg/jpeg_decode/README.md b/examples/peripherals/jpeg/jpeg_decode/README.md index ff99171c3e3..dd94d672b81 100644 --- a/examples/peripherals/jpeg/jpeg_decode/README.md +++ b/examples/peripherals/jpeg/jpeg_decode/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32-P4 | -| ----------------- | -------- | +| Supported Targets | ESP32-P4 | ESP32-S31 | +| ----------------- | -------- | --------- | # JPEG decode example diff --git a/examples/peripherals/jpeg/jpeg_encode/README.md b/examples/peripherals/jpeg/jpeg_encode/README.md index 82aa2704b4a..d8a22456b33 100644 --- a/examples/peripherals/jpeg/jpeg_encode/README.md +++ b/examples/peripherals/jpeg/jpeg_encode/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32-P4 | -| ----------------- | -------- | +| Supported Targets | ESP32-P4 | ESP32-S31 | +| ----------------- | -------- | --------- | # JPEG encode example diff --git a/examples/peripherals/sdio/host/README.md b/examples/peripherals/sdio/host/README.md index 213ba44961d..d64856b44d6 100644 --- a/examples/peripherals/sdio/host/README.md +++ b/examples/peripherals/sdio/host/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-S3 | -| ----------------- | ----- | -------- | +| Supported Targets | ESP32 | ESP32-S3 | ESP32-S31 | +| ----------------- | ----- | -------- | --------- | ## Troubleshooting diff --git a/examples/storage/.build-test-rules.yml b/examples/storage/.build-test-rules.yml index 44add14168c..46d14b814dc 100644 --- a/examples/storage/.build-test-rules.yml +++ b/examples/storage/.build-test-rules.yml @@ -73,7 +73,7 @@ examples/storage/sd_card/sdmmc: disable: - if: SOC_SDMMC_HOST_SUPPORTED != 1 disable_test: - - if: IDF_TARGET not in ["esp32", "esp32p4"] + - if: IDF_TARGET not in ["esp32", "esp32p4", "esp32s31"] temporary: true reason: lack of runners diff --git a/examples/storage/sd_card/sdmmc/README.md b/examples/storage/sd_card/sdmmc/README.md index 43943d00e8b..cbc8961e5ed 100644 --- a/examples/storage/sd_card/sdmmc/README.md +++ b/examples/storage/sd_card/sdmmc/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-P4 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-P4 | ESP32-S3 | ESP32-S31 | +| ----------------- | ----- | -------- | -------- | --------- | # SD Card example (SDMMC) @@ -88,6 +88,19 @@ Default dedicated pins on ESP32-P4 are able to connect to an ultra high-speed SD When using different GPIO pins this is not required and `SD power supply comes from internal LDO IO` setting can be disabled. +### Pin assignments for ESP32-S31 + +On ESP32-S31, SDMMC peripheral is connected to specific GPIO pins using the IO MUX. GPIO pins cannot be customized. Please see the table below for the pin connections. + +ESP32-S31 pin | SD card pin | Notes +--------------|-------------|------------ +GPIO24 | CLK | 10k pullup +GPIO25 | CMD | 10k pullup +GPIO20 | D0 | 10k pullup +GPIO21 | D1 | not used in 1-line SD mode; 10k pullup in 4-line mode +GPIO22 | D2 | not used in 1-line SD mode; 10k pullup in 4-line mode +GPIO23 | D3 | not used in 1-line SD mode, but card's D3 pin must have a 10k pullup + ### 4-line and 1-line SD modes By default, this example uses 4 line SD mode, utilizing 6 pins: CLK, CMD, D0 - D3. It is possible to use 1-line mode (CLK, CMD, D0) by changing "SD/MMC bus width" in the example configuration menu (see `CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_1`). diff --git a/examples/storage/sd_card/sdmmc/main/CMakeLists.txt b/examples/storage/sd_card/sdmmc/main/CMakeLists.txt index cae1a04464f..4e8b445c730 100644 --- a/examples/storage/sd_card/sdmmc/main/CMakeLists.txt +++ b/examples/storage/sd_card/sdmmc/main/CMakeLists.txt @@ -6,7 +6,5 @@ idf_component_register(SRCS ${srcs} WHOLE_ARCHIVE) if(NOT CONFIG_SOC_SDMMC_HOST_SUPPORTED) - fail_at_build_time(sdmmc "" - "Only ESP32 and ESP32-S3 targets are supported." - "Please refer README.md for more details") + fail_at_build_time(sdmmc "No SDMMC Host supported. Please refer README.md for more details") endif() diff --git a/examples/storage/sd_card/sdmmc/main/Kconfig.projbuild b/examples/storage/sd_card/sdmmc/main/Kconfig.projbuild index ca3edccbaf6..b2fb06bfcb2 100644 --- a/examples/storage/sd_card/sdmmc/main/Kconfig.projbuild +++ b/examples/storage/sd_card/sdmmc/main/Kconfig.projbuild @@ -158,7 +158,7 @@ menu "SD/MMC Example Configuration" endif # EXAMPLE_SDMMC_BUS_WIDTH_4 config EXAMPLE_SD_PWR_CTRL_LDO_INTERNAL_IO - depends on SOC_SDMMC_IO_POWER_EXTERNAL + depends on SOC_SDMMC_IO_POWER_EXTERNAL || SOC_SDMMC_IO_UHS_POWER_EXTERNAL bool "SD power supply comes from internal LDO IO (READ HELP!)" default y help @@ -167,9 +167,11 @@ menu "SD/MMC Example Configuration" Unselect this option if the SD card is powered by an external power supply. config EXAMPLE_SD_PWR_CTRL_LDO_IO_ID - depends on SOC_SDMMC_IO_POWER_EXTERNAL && EXAMPLE_SD_PWR_CTRL_LDO_INTERNAL_IO + depends on SOC_SDMMC_IO_POWER_EXTERNAL || SOC_SDMMC_IO_UHS_POWER_EXTERNAL + depends on EXAMPLE_SD_PWR_CTRL_LDO_INTERNAL_IO int "LDO ID" default 4 if IDF_TARGET_ESP32P4 + default 1 if IDF_TARGET_ESP32S31 help Please read the schematic first and input your LDO ID. endmenu diff --git a/examples/storage/sd_card/sdmmc/main/sd_card_example_main.c b/examples/storage/sd_card/sdmmc/main/sd_card_example_main.c index bc700b13500..d027ad94899 100644 --- a/examples/storage/sd_card/sdmmc/main/sd_card_example_main.c +++ b/examples/storage/sd_card/sdmmc/main/sd_card_example_main.c @@ -16,7 +16,7 @@ #include "driver/sdmmc_host.h" #include "driver/gpio.h" #include "sd_test_io.h" -#if SOC_SDMMC_IO_POWER_EXTERNAL +#if SOC_SDMMC_IO_POWER_EXTERNAL || SOC_SDMMC_IO_UHS_POWER_EXTERNAL #include "sd_pwr_ctrl_by_on_chip_ldo.h" #endif @@ -178,6 +178,11 @@ void app_main(void) host.flags &= ~SDMMC_HOST_FLAG_DDR; #endif +#if SOC_SDMMC_IO_UHS_POWER_EXTERNAL + //for uhs-i power + host.io_voltage = 1.8f; +#endif + // For SoCs where the SD power can be supplied both via an internal or external (e.g. on-board LDO) power supply. // When using specific IO pins (which can be used for ultra high-speed SDMMC) to connect to the SD card // and the internal LDO power supply, we need to initialize the power supply first. diff --git a/examples/storage/sd_card/sdmmc/pytest_sdmmc_card_example.py b/examples/storage/sd_card/sdmmc/pytest_sdmmc_card_example.py index 97e058eeb46..d6056bdb4d6 100644 --- a/examples/storage/sd_card/sdmmc/pytest_sdmmc_card_example.py +++ b/examples/storage/sd_card/sdmmc/pytest_sdmmc_card_example.py @@ -9,7 +9,7 @@ from pytest_embedded_idf.utils import idf_parametrize @pytest.mark.sdcard_sdmode -@idf_parametrize('target', ['esp32', 'esp32p4'], indirect=['target']) +@idf_parametrize('target', ['esp32', 'esp32p4', 'esp32s31'], indirect=['target']) def test_examples_sd_card_sdmmc(dut: Dut) -> None: dut.expect('example: Initializing SD card', timeout=20) dut.expect('example: Using SDMMC peripheral', timeout=10)