mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-03 03:31:41 +03:00
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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user