feat(sdmmc): add esp32s31 support

Enable SDMMC host support on ESP32-S31 across HAL, SOC caps, tests, examples, and documentation.
This commit is contained in:
armando
2026-05-21 09:09:28 +08:00
parent 19a716086f
commit f408e1a8bc
38 changed files with 1813 additions and 218 deletions
+84 -23
View File
@@ -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;
}
@@ -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