Merge branch 'feat/sdmmc_uhs_tuning_v5.4' into 'release/v5.4'

sdmmc: UHS-I support for SDR50 (100MHz, 50MB/s), DDR50 (50MHz, 50MB/s) (v5.4)

See merge request espressif/esp-idf!34867
This commit is contained in:
morris
2024-11-14 11:13:45 +08:00
34 changed files with 924 additions and 186 deletions

View File

@@ -31,6 +31,8 @@ extern "C" {
.slot = SDMMC_HOST_SLOT_1, \ .slot = SDMMC_HOST_SLOT_1, \
.max_freq_khz = SDMMC_FREQ_DEFAULT, \ .max_freq_khz = SDMMC_FREQ_DEFAULT, \
.io_voltage = 3.3f, \ .io_voltage = 3.3f, \
.driver_strength = SDMMC_DRIVER_STRENGTH_B, \
.current_limit = SDMMC_CURRENT_LIMIT_200MA, \
.init = &sdmmc_host_init, \ .init = &sdmmc_host_init, \
.set_bus_width = &sdmmc_host_set_bus_width, \ .set_bus_width = &sdmmc_host_set_bus_width, \
.get_bus_width = &sdmmc_host_get_slot_width, \ .get_bus_width = &sdmmc_host_get_slot_width, \
@@ -48,29 +50,35 @@ extern "C" {
.dma_aligned_buffer = NULL, \ .dma_aligned_buffer = NULL, \
.pwr_ctrl_handle = NULL, \ .pwr_ctrl_handle = NULL, \
.get_dma_info = &sdmmc_host_get_dma_info, \ .get_dma_info = &sdmmc_host_get_dma_info, \
.is_slot_set_to_uhs1 = &sdmmc_host_is_slot_set_to_uhs1, \
} }
#define SDMMC_SLOT_NO_CD GPIO_NUM_NC ///< indicates that card detect line is not used #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 #define SDMMC_SLOT_NO_WP GPIO_NUM_NC ///< indicates that write protect line is not used
#define SDMMC_SLOT_WIDTH_DEFAULT 0 ///< use the maximum possible width for the slot #define SDMMC_SLOT_WIDTH_DEFAULT 0 ///< use the maximum possible width for the slot
#if SOC_SDMMC_USE_IOMUX && !SOC_SDMMC_USE_GPIO_MATRIX
/** /**
* Macro defining default configuration of SDMMC host slot * Macro defining default configuration of SDMMC host slot
*/ */
#if CONFIG_IDF_TARGET_ESP32
#define SDMMC_SLOT_CONFIG_DEFAULT() {\ #define SDMMC_SLOT_CONFIG_DEFAULT() {\
.clk = GPIO_NUM_6, \
.cmd = GPIO_NUM_11, \
.d0 = GPIO_NUM_7, \
.d1 = GPIO_NUM_8, \
.d2 = GPIO_NUM_9, \
.d3 = GPIO_NUM_10, \
.d4 = GPIO_NUM_16, \
.d5 = GPIO_NUM_17, \
.d6 = GPIO_NUM_5, \
.d7 = GPIO_NUM_18, \
.cd = SDMMC_SLOT_NO_CD, \ .cd = SDMMC_SLOT_NO_CD, \
.wp = SDMMC_SLOT_NO_WP, \ .wp = SDMMC_SLOT_NO_WP, \
.width = SDMMC_SLOT_WIDTH_DEFAULT, \ .width = SDMMC_SLOT_WIDTH_DEFAULT, \
.flags = 0, \ .flags = 0, \
} }
#else #elif CONFIG_IDF_TARGET_ESP32P4
/**
* Macro defining default configuration of SDMMC host slot
*/
#if CONFIG_IDF_TARGET_ESP32P4
#define SDMMC_SLOT_CONFIG_DEFAULT() {\ #define SDMMC_SLOT_CONFIG_DEFAULT() {\
.clk = GPIO_NUM_43, \ .clk = GPIO_NUM_43, \
.cmd = GPIO_NUM_44, \ .cmd = GPIO_NUM_44, \
@@ -107,8 +115,6 @@ extern "C" {
} }
#endif // GPIO Matrix chips #endif // GPIO Matrix chips
#endif
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -24,7 +24,6 @@ extern "C" {
* Extra configuration for SDMMC peripheral slot * Extra configuration for SDMMC peripheral slot
*/ */
typedef struct { typedef struct {
#ifdef SOC_SDMMC_USE_GPIO_MATRIX
gpio_num_t clk; ///< GPIO number of CLK signal. gpio_num_t clk; ///< GPIO number of CLK signal.
gpio_num_t cmd; ///< GPIO number of CMD signal. gpio_num_t cmd; ///< GPIO number of CMD signal.
gpio_num_t d0; ///< GPIO number of D0 signal. gpio_num_t d0; ///< GPIO number of D0 signal.
@@ -35,7 +34,6 @@ typedef struct {
gpio_num_t d5; ///< GPIO number of D5 signal. Ignored in 1- or 4- line mode. gpio_num_t d5; ///< GPIO number of D5 signal. Ignored in 1- or 4- line mode.
gpio_num_t d6; ///< GPIO number of D6 signal. Ignored in 1- or 4- line mode. gpio_num_t d6; ///< GPIO number of D6 signal. Ignored in 1- or 4- line mode.
gpio_num_t d7; ///< GPIO number of D7 signal. Ignored in 1- or 4- line mode. gpio_num_t d7; ///< GPIO number of D7 signal. Ignored in 1- or 4- line mode.
#endif // SOC_SDMMC_USE_GPIO_MATRIX
union { union {
gpio_num_t gpio_cd; ///< GPIO number of card detect signal gpio_num_t gpio_cd; ///< GPIO number of card detect signal
gpio_num_t cd; ///< GPIO number of card detect signal; shorter name. gpio_num_t cd; ///< GPIO number of card detect signal; shorter name.
@@ -56,6 +54,8 @@ typedef struct {
* 0 means "active low", i.e. card is protected when the GPIO is low; * 0 means "active low", i.e. card is protected when the GPIO is low;
* 1 means "active high", i.e. card is protected when GPIO is high. * 1 means "active high", i.e. card is protected when GPIO is high.
*/ */
#define SDMMC_SLOT_FLAG_UHS1 BIT(2)
/**< Enable UHS-I mode for this slot */
} sdmmc_slot_config_t; } sdmmc_slot_config_t;
/** /**
@@ -284,6 +284,18 @@ esp_err_t sdmmc_host_set_input_delay(int slot, sdmmc_delay_phase_t delay_phase);
*/ */
esp_err_t sdmmc_host_get_dma_info(int slot, esp_dma_mem_info_t *dma_mem_info); esp_err_t sdmmc_host_get_dma_info(int slot, esp_dma_mem_info_t *dma_mem_info);
/**
* @brief Check if the slot is set to uhs1 or not
*
* @param[in] slot Slot id
* @param[out] is_uhs1 Is uhs1 or not
*
* @return
* - ESP_OK: on success
* - ESP_ERR_INVALID_STATE: driver not in correct state
*/
esp_err_t sdmmc_host_is_slot_set_to_uhs1(int slot, bool *is_uhs1);
/** /**
* @brief Get the state of SDMMC host * @brief Get the state of SDMMC host
* *

View File

@@ -20,7 +20,7 @@
#include "driver/sdmmc_host.h" #include "driver/sdmmc_host.h"
#include "esp_private/esp_clk_tree_common.h" #include "esp_private/esp_clk_tree_common.h"
#include "esp_private/periph_ctrl.h" #include "esp_private/periph_ctrl.h"
#include "sdmmc_private.h" #include "sdmmc_internal.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/semphr.h" #include "freertos/semphr.h"
#include "esp_clk_tree.h" #include "esp_clk_tree.h"
@@ -33,6 +33,8 @@
#define SDMMC_EVENT_QUEUE_LENGTH 32 #define SDMMC_EVENT_QUEUE_LENGTH 32
#define SDMMC_FREQ_SDR104 208000 /*!< MMC 208MHz speed */
#if !SOC_RCC_IS_INDEPENDENT #if !SOC_RCC_IS_INDEPENDENT
// Reset and Clock Control registers are mixing with other peripherals, so we need to use a critical section // Reset and Clock Control registers are mixing with other peripherals, so we need to use a critical section
#define SDMMC_RCC_ATOMIC() PERIPH_RCC_ATOMIC() #define SDMMC_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
@@ -65,9 +67,11 @@ if (!GPIO_IS_VALID_GPIO(_gpio_num)) { \
* Slot contexts * Slot contexts
*/ */
typedef struct slot_ctx_t { typedef struct slot_ctx_t {
int slot_id;
size_t slot_width; size_t slot_width;
sdmmc_slot_io_info_t slot_gpio_num; sdmmc_slot_io_info_t slot_gpio_num;
bool use_gpio_matrix; bool use_gpio_matrix;
bool is_uhs1;
#if SOC_SDMMC_NUM_SLOTS >= 2 #if SOC_SDMMC_NUM_SLOTS >= 2
int slot_host_div; int slot_host_div;
uint32_t slot_freq_khz; uint32_t slot_freq_khz;
@@ -79,14 +83,15 @@ typedef struct slot_ctx_t {
* Host contexts * Host contexts
*/ */
typedef struct host_ctx_t { typedef struct host_ctx_t {
intr_handle_t intr_handle; intr_handle_t intr_handle;
QueueHandle_t event_queue; QueueHandle_t event_queue;
SemaphoreHandle_t io_intr_event; SemaphoreHandle_t io_intr_event;
sdmmc_hal_context_t hal; sdmmc_hal_context_t hal;
slot_ctx_t slot_ctx[SOC_SDMMC_NUM_SLOTS]; soc_periph_sdmmc_clk_src_t clk_src;
slot_ctx_t slot_ctx[SOC_SDMMC_NUM_SLOTS];
#if SOC_SDMMC_NUM_SLOTS >= 2 #if SOC_SDMMC_NUM_SLOTS >= 2
uint8_t num_of_init_slots; uint8_t num_of_init_slots;
int8_t active_slot_num; int8_t active_slot_num;
#endif #endif
} host_ctx_t; } host_ctx_t;
@@ -163,20 +168,25 @@ esp_err_t sdmmc_host_reset(void)
* Of the second stage dividers, div0 is used for card 0, and div1 is used * Of the second stage dividers, div0 is used for card 0, and div1 is used
* for card 1. * for card 1.
*/ */
static void sdmmc_host_set_clk_div(int div) static void sdmmc_host_set_clk_div(soc_periph_sdmmc_clk_src_t src, int div)
{ {
esp_clk_tree_enable_src((soc_module_clk_t)SDMMC_CLK_SRC_DEFAULT, true); esp_clk_tree_enable_src((soc_module_clk_t)src, true);
SDMMC_CLK_SRC_ATOMIC() { SDMMC_CLK_SRC_ATOMIC() {
sdmmc_ll_set_clock_div(s_host_ctx.hal.dev, div); sdmmc_ll_set_clock_div(s_host_ctx.hal.dev, div);
sdmmc_ll_select_clk_source(s_host_ctx.hal.dev, SDMMC_CLK_SRC_DEFAULT); sdmmc_ll_select_clk_source(s_host_ctx.hal.dev, src);
sdmmc_ll_init_phase_delay(s_host_ctx.hal.dev); sdmmc_ll_init_phase_delay(s_host_ctx.hal.dev);
#if SOC_CLK_SDIO_PLL_SUPPORTED
if (src == SDMMC_CLK_SRC_SDIO_200M) {
sdmmc_ll_enable_sdio_pll(s_host_ctx.hal.dev, true);
}
#endif
} }
// Wait for the clock to propagate // Wait for the clock to propagate
esp_rom_delay_us(10); esp_rom_delay_us(10);
} }
static esp_err_t sdmmc_host_clock_update_command(int slot) static esp_err_t sdmmc_host_clock_update_command(int slot, bool is_cmd11)
{ {
// Clock update command (not a real command; just updates CIU registers) // Clock update command (not a real command; just updates CIU registers)
sdmmc_hw_cmd_t cmd_val = { sdmmc_hw_cmd_t cmd_val = {
@@ -184,47 +194,31 @@ static esp_err_t sdmmc_host_clock_update_command(int slot)
.update_clk_reg = 1, .update_clk_reg = 1,
.wait_complete = 1 .wait_complete = 1
}; };
bool repeat = true; if (is_cmd11) {
while (repeat) { cmd_val.volt_switch = 1;
ESP_RETURN_ON_ERROR(sdmmc_host_start_command(slot, cmd_val, 0), TAG, "sdmmc_host_start_command returned 0x%x", err_rc_);
int64_t yield_delay_us = 100 * 1000; // initially 100ms
int64_t t0 = esp_timer_get_time();
int64_t t1 = 0;
while (true) {
t1 = esp_timer_get_time();
if (t1 - t0 > SDMMC_HOST_CLOCK_UPDATE_CMD_TIMEOUT_US) {
return ESP_ERR_TIMEOUT;
}
// Sending clock update command to the CIU can generate HLE error.
// According to the manual, this is okay and we must retry the command.
if (sdmmc_ll_get_interrupt_raw(s_host_ctx.hal.dev) & SDMMC_LL_EVENT_HLE) {
sdmmc_ll_clear_interrupt(s_host_ctx.hal.dev, SDMMC_LL_EVENT_HLE);
repeat = true;
break;
}
// When the command is accepted by CIU, start_command bit will be
// cleared in SDMMC.cmd register.
if (sdmmc_ll_is_command_taken(s_host_ctx.hal.dev)) {
repeat = false;
break;
}
if (t1 - t0 > yield_delay_us) {
yield_delay_us *= 2;
vTaskDelay(1);
}
}
} }
ESP_RETURN_ON_ERROR(sdmmc_host_start_command(slot, cmd_val, 0), TAG, "sdmmc_host_start_command returned 0x%x", err_rc_);
return ESP_OK; return ESP_OK;
} }
void sdmmc_host_get_clk_dividers(uint32_t freq_khz, int *host_div, int *card_div) void sdmmc_host_get_clk_dividers(uint32_t freq_khz, int *host_div, int *card_div, soc_periph_sdmmc_clk_src_t *src)
{ {
uint32_t clk_src_freq_hz = 0; uint32_t clk_src_freq_hz = 0;
esp_clk_tree_src_get_freq_hz(SDMMC_CLK_SRC_DEFAULT, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz); soc_periph_sdmmc_clk_src_t clk_src = 0;
assert(clk_src_freq_hz == (160 * 1000 * 1000)); #if SOC_SDMMC_UHS_I_SUPPORTED
if (freq_khz > SDMMC_FREQ_HIGHSPEED) {
clk_src = SDMMC_CLK_SRC_SDIO_200M;
} else
#endif
{
clk_src = SDMMC_CLK_SRC_DEFAULT;
}
s_host_ctx.clk_src = clk_src;
esp_err_t ret = esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz);
assert(ret == ESP_OK);
ESP_LOGD(TAG, "clk_src_freq_hz: %"PRId32" hz", clk_src_freq_hz);
#if SDMMC_LL_MAX_FREQ_KHZ_FPGA #if SDMMC_LL_MAX_FREQ_KHZ_FPGA
if (freq_khz >= SDMMC_LL_MAX_FREQ_KHZ_FPGA) { if (freq_khz >= SDMMC_LL_MAX_FREQ_KHZ_FPGA) {
@@ -232,40 +226,53 @@ void sdmmc_host_get_clk_dividers(uint32_t freq_khz, int *host_div, int *card_div
freq_khz = SDMMC_LL_MAX_FREQ_KHZ_FPGA; freq_khz = SDMMC_LL_MAX_FREQ_KHZ_FPGA;
} }
#endif #endif
// Calculate new dividers // Calculate new dividers
if (freq_khz >= SDMMC_FREQ_HIGHSPEED) { #if SOC_SDMMC_UHS_I_SUPPORTED
*host_div = 4; // 160 MHz / 4 = 40 MHz if (freq_khz == SDMMC_FREQ_SDR104) {
*host_div = 1; // 200 MHz / 1 = 200 MHz
*card_div = 0; *card_div = 0;
} else if (freq_khz == SDMMC_FREQ_DEFAULT) { } else if (freq_khz == SDMMC_FREQ_SDR50) {
*host_div = 8; // 160 MHz / 8 = 20 MHz *host_div = 2; // 200 MHz / 2 = 100 MHz
*card_div = 0; *card_div = 0;
} else if (freq_khz == SDMMC_FREQ_PROBING) { } else
*host_div = 10; // 160 MHz / 10 / (20 * 2) = 400 kHz #endif
*card_div = 20; if (freq_khz >= SDMMC_FREQ_HIGHSPEED) {
} else { *host_div = 4; // 160 MHz / 4 = 40 MHz
/* *card_div = 0;
* for custom frequencies use maximum range of host divider (1-16), find the closest <= div. combination } else if (freq_khz == SDMMC_FREQ_DEFAULT) {
* if exceeded, combine with the card divider to keep reasonable precision (applies mainly to low frequencies) *host_div = 8; // 160 MHz / 8 = 20 MHz
* effective frequency range: 400 kHz - 32 MHz (32.1 - 39.9 MHz cannot be covered with given divider scheme) *card_div = 0;
*/ } else if (freq_khz == SDMMC_FREQ_PROBING) {
*host_div = (clk_src_freq_hz) / (freq_khz * 1000); *host_div = 10; // 160 MHz / 10 / (20 * 2) = 400 kHz
if (*host_div > 15) { *card_div = 20;
*host_div = 2; } else {
*card_div = (clk_src_freq_hz / 2) / (2 * freq_khz * 1000); /*
if (((clk_src_freq_hz / 2) % (2 * freq_khz * 1000)) > 0) { * for custom frequencies use maximum range of host divider (1-16), find the closest <= div. combination
(*card_div)++; * 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) {
*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)++;
}
} else if ((clk_src_freq_hz % (freq_khz * 1000)) > 0) {
(*host_div)++;
} }
} else if ((clk_src_freq_hz % (freq_khz * 1000)) > 0) {
(*host_div)++;
} }
}
*src = clk_src;
} }
static int sdmmc_host_calc_freq(const int host_div, const int card_div) static int sdmmc_host_calc_freq(soc_periph_sdmmc_clk_src_t src, const int host_div, const int card_div)
{ {
uint32_t clk_src_freq_hz = 0; uint32_t clk_src_freq_hz = 0;
esp_clk_tree_src_get_freq_hz(SDMMC_CLK_SRC_DEFAULT, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz); esp_err_t ret = esp_clk_tree_src_get_freq_hz(src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz);
assert(clk_src_freq_hz == (160 * 1000 * 1000)); assert(ret == ESP_OK);
return clk_src_freq_hz / host_div / ((card_div == 0) ? 1 : card_div * 2) / 1000; return clk_src_freq_hz / host_div / ((card_div == 0) ? 1 : card_div * 2) / 1000;
} }
@@ -282,24 +289,25 @@ esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz)
// Disable clock first // Disable clock first
sdmmc_ll_enable_card_clock(s_host_ctx.hal.dev, slot, false); sdmmc_ll_enable_card_clock(s_host_ctx.hal.dev, slot, false);
esp_err_t err = sdmmc_host_clock_update_command(slot); esp_err_t err = sdmmc_host_clock_update_command(slot, false);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "disabling clk failed"); ESP_LOGE(TAG, "disabling clk failed");
ESP_LOGE(TAG, "%s: sdmmc_host_clock_update_command returned 0x%x", __func__, err); ESP_LOGE(TAG, "%s: sdmmc_host_clock_update_command returned 0x%x", __func__, err);
return err; return err;
} }
soc_periph_sdmmc_clk_src_t clk_src = 0;
int host_div = 0; /* clock divider of the host (SDMMC.clock) */ int host_div = 0; /* clock divider of the host (SDMMC.clock) */
int card_div = 0; /* 1/2 of card clock divider (SDMMC.clkdiv) */ int card_div = 0; /* 1/2 of card clock divider (SDMMC.clkdiv) */
sdmmc_host_get_clk_dividers(freq_khz, &host_div, &card_div); sdmmc_host_get_clk_dividers(freq_khz, &host_div, &card_div, &clk_src);
int real_freq = sdmmc_host_calc_freq(host_div, card_div); int real_freq = sdmmc_host_calc_freq(clk_src, host_div, card_div);
ESP_LOGD(TAG, "slot=%d host_div=%d card_div=%d freq=%dkHz (max %" PRIu32 "kHz)", slot, host_div, card_div, real_freq, freq_khz); ESP_LOGD(TAG, "slot=%d clk_src=%d host_div=%d card_div=%d freq=%dkHz (max %" PRIu32 "kHz)", slot, clk_src, host_div, card_div, real_freq, freq_khz);
// Program card clock settings, send them to the CIU // Program card clock settings, send them to the CIU
sdmmc_ll_set_card_clock_div(s_host_ctx.hal.dev, slot, card_div); sdmmc_ll_set_card_clock_div(s_host_ctx.hal.dev, slot, card_div);
sdmmc_host_set_clk_div(host_div); sdmmc_host_set_clk_div(clk_src, host_div);
err = sdmmc_host_clock_update_command(slot); err = sdmmc_host_clock_update_command(slot, false);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "setting clk div failed"); ESP_LOGE(TAG, "setting clk div failed");
ESP_LOGE(TAG, "%s: sdmmc_host_clock_update_command returned 0x%x", __func__, err); ESP_LOGE(TAG, "%s: sdmmc_host_clock_update_command returned 0x%x", __func__, err);
@@ -309,7 +317,7 @@ esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz)
// Re-enable clocks // Re-enable clocks
sdmmc_ll_enable_card_clock(s_host_ctx.hal.dev, slot, true); sdmmc_ll_enable_card_clock(s_host_ctx.hal.dev, slot, true);
sdmmc_ll_enable_card_clock_low_power(s_host_ctx.hal.dev, slot, true); sdmmc_ll_enable_card_clock_low_power(s_host_ctx.hal.dev, slot, true);
err = sdmmc_host_clock_update_command(slot); err = sdmmc_host_clock_update_command(slot, false);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "re-enabling clk failed"); ESP_LOGE(TAG, "re-enabling clk failed");
ESP_LOGE(TAG, "%s: sdmmc_host_clock_update_command returned 0x%x", __func__, err); ESP_LOGE(TAG, "%s: sdmmc_host_clock_update_command returned 0x%x", __func__, err);
@@ -338,7 +346,7 @@ esp_err_t sdmmc_host_get_real_freq(int slot, int *real_freq_khz)
int host_div = sdmmc_ll_get_clock_div(s_host_ctx.hal.dev); int host_div = sdmmc_ll_get_clock_div(s_host_ctx.hal.dev);
int card_div = sdmmc_ll_get_card_clock_div(s_host_ctx.hal.dev, slot); int card_div = sdmmc_ll_get_card_clock_div(s_host_ctx.hal.dev, slot);
*real_freq_khz = sdmmc_host_calc_freq(host_div, card_div); *real_freq_khz = sdmmc_host_calc_freq(s_host_ctx.clk_src, host_div, card_div);
return ESP_OK; return ESP_OK;
} }
@@ -354,7 +362,7 @@ esp_err_t sdmmc_host_set_input_delay(int slot, sdmmc_delay_phase_t delay_phase)
ESP_RETURN_ON_FALSE(delay_phase < SOC_SDMMC_DELAY_PHASE_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid delay phase"); ESP_RETURN_ON_FALSE(delay_phase < SOC_SDMMC_DELAY_PHASE_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid delay phase");
uint32_t clk_src_freq_hz = 0; uint32_t clk_src_freq_hz = 0;
ESP_RETURN_ON_ERROR(esp_clk_tree_src_get_freq_hz(SDMMC_CLK_SRC_DEFAULT, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz), ESP_RETURN_ON_ERROR(esp_clk_tree_src_get_freq_hz(s_host_ctx.clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz),
TAG, "get source clock frequency failed"); TAG, "get source clock frequency failed");
//Now we're in high speed. Note ESP SDMMC Host HW only supports integer divider. //Now we're in high speed. Note ESP SDMMC Host HW only supports integer divider.
@@ -425,7 +433,25 @@ esp_err_t sdmmc_host_start_command(int slot, sdmmc_hw_cmd_t cmd, uint32_t arg)
int64_t yield_delay_us = 100 * 1000; // initially 100ms int64_t yield_delay_us = 100 * 1000; // initially 100ms
int64_t t0 = esp_timer_get_time(); int64_t t0 = esp_timer_get_time();
int64_t t1 = 0; int64_t t1 = 0;
while (!sdmmc_ll_is_command_taken(s_host_ctx.hal.dev)) { bool skip_wait = (cmd.volt_switch && cmd.update_clk_reg);
if (!skip_wait) {
while (!(sdmmc_ll_is_command_taken(s_host_ctx.hal.dev))) {
t1 = esp_timer_get_time();
if (t1 - t0 > SDMMC_HOST_START_CMD_TIMEOUT_US) {
return ESP_ERR_TIMEOUT;
}
if (t1 - t0 > yield_delay_us) {
yield_delay_us *= 2;
vTaskDelay(1);
}
}
}
sdmmc_ll_set_command_arg(s_host_ctx.hal.dev, arg);
cmd.card_num = slot;
cmd.start_command = 1;
sdmmc_ll_set_command(s_host_ctx.hal.dev, cmd);
while (!(sdmmc_ll_is_command_taken(s_host_ctx.hal.dev))) {
t1 = esp_timer_get_time(); t1 = esp_timer_get_time();
if (t1 - t0 > SDMMC_HOST_START_CMD_TIMEOUT_US) { if (t1 - t0 > SDMMC_HOST_START_CMD_TIMEOUT_US) {
return ESP_ERR_TIMEOUT; return ESP_ERR_TIMEOUT;
@@ -435,10 +461,6 @@ esp_err_t sdmmc_host_start_command(int slot, sdmmc_hw_cmd_t cmd, uint32_t arg)
vTaskDelay(1); vTaskDelay(1);
} }
} }
sdmmc_ll_set_command_arg(s_host_ctx.hal.dev, arg);
cmd.card_num = slot;
cmd.start_command = 1;
sdmmc_ll_set_command(s_host_ctx.hal.dev, cmd);
return ESP_OK; return ESP_OK;
} }
@@ -473,7 +495,7 @@ esp_err_t sdmmc_host_init(void)
sdmmc_hal_init(&s_host_ctx.hal); sdmmc_hal_init(&s_host_ctx.hal);
// Enable clock to peripheral. Use smallest divider first. // Enable clock to peripheral. Use smallest divider first.
sdmmc_host_set_clk_div(2); sdmmc_host_set_clk_div(SDMMC_CLK_SRC_DEFAULT, 2);
// Reset // Reset
esp_err_t err = sdmmc_host_reset(); esp_err_t err = sdmmc_host_reset();
@@ -581,6 +603,18 @@ static bool s_check_pin_not_set(const sdmmc_slot_config_t *slot_config)
#endif #endif
} }
esp_err_t sdmmc_host_is_slot_set_to_uhs1(int slot, bool *is_uhs1)
{
if (s_host_ctx.slot_ctx[slot].slot_id != slot) {
ESP_LOGE(TAG, "%s: slot %d isn't initialized", __func__, slot);
return ESP_ERR_INVALID_STATE;
}
*is_uhs1 = s_host_ctx.slot_ctx[slot].is_uhs1;
return ESP_OK;
}
esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t *slot_config) esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t *slot_config)
{ {
if (!s_host_ctx.intr_handle) { if (!s_host_ctx.intr_handle) {
@@ -592,6 +626,11 @@ esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t *slot_config)
if (slot_config == NULL) { if (slot_config == NULL) {
return ESP_ERR_INVALID_ARG; return ESP_ERR_INVALID_ARG;
} }
if (slot_config->flags & SDMMC_SLOT_FLAG_UHS1) {
s_host_ctx.slot_ctx[slot].is_uhs1 = true;
}
int gpio_cd = slot_config->cd; int gpio_cd = slot_config->cd;
int gpio_wp = slot_config->wp; int gpio_wp = slot_config->wp;
bool gpio_wp_polarity = slot_config->flags & SDMMC_SLOT_FLAG_WP_ACTIVE_HIGH; bool gpio_wp_polarity = slot_config->flags & SDMMC_SLOT_FLAG_WP_ACTIVE_HIGH;
@@ -616,7 +655,17 @@ esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t *slot_config)
if (slot == 0) { if (slot == 0) {
#if !SDMMC_LL_SLOT_SUPPORT_GPIO_MATRIX(0) #if !SDMMC_LL_SLOT_SUPPORT_GPIO_MATRIX(0)
ESP_RETURN_ON_FALSE(!use_gpio_matrix, ESP_ERR_INVALID_ARG, TAG, "doesn't support routing from GPIO matrix, driver uses dedicated IOs"); if (use_gpio_matrix &&
SDMMC_SLOT0_IOMUX_PIN_NUM_CLK == slot_config->clk &&
SDMMC_SLOT0_IOMUX_PIN_NUM_CMD == slot_config->cmd &&
SDMMC_SLOT0_IOMUX_PIN_NUM_D0 == slot_config->d0 &&
SDMMC_SLOT0_IOMUX_PIN_NUM_D1 == slot_config->d1 &&
SDMMC_SLOT0_IOMUX_PIN_NUM_D2 == slot_config->d2 &&
SDMMC_SLOT0_IOMUX_PIN_NUM_D3 == slot_config->d3) {
use_gpio_matrix = false;
} else {
ESP_RETURN_ON_FALSE(!use_gpio_matrix, ESP_ERR_INVALID_ARG, TAG, "doesn't support routing from GPIO matrix, driver uses dedicated IOs");
}
#endif #endif
} else { } else {
#if !SDMMC_LL_SLOT_SUPPORT_GPIO_MATRIX(1) #if !SDMMC_LL_SLOT_SUPPORT_GPIO_MATRIX(1)
@@ -689,18 +738,22 @@ esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t *slot_config)
if (slot_width >= 4) { if (slot_width >= 4) {
configure_pin(slot_gpio->d1, sdmmc_slot_gpio_sig[slot].d1, GPIO_MODE_INPUT_OUTPUT, "d1", use_gpio_matrix); configure_pin(slot_gpio->d1, sdmmc_slot_gpio_sig[slot].d1, GPIO_MODE_INPUT_OUTPUT, "d1", use_gpio_matrix);
configure_pin(slot_gpio->d2, sdmmc_slot_gpio_sig[slot].d2, GPIO_MODE_INPUT_OUTPUT, "d2", use_gpio_matrix); configure_pin(slot_gpio->d2, sdmmc_slot_gpio_sig[slot].d2, GPIO_MODE_INPUT_OUTPUT, "d2", use_gpio_matrix);
// Force D3 high to make slave enter SD mode. if (s_host_ctx.slot_ctx[slot].is_uhs1) {
// Connect to peripheral after width configuration. configure_pin(slot_gpio->d3, sdmmc_slot_gpio_sig[slot].d3, GPIO_MODE_INPUT_OUTPUT, "d3", use_gpio_matrix);
if (slot_gpio->d3 > GPIO_NUM_NC) { } else {
gpio_config_t gpio_conf = { // Force D3 high to make slave enter SD mode.
.pin_bit_mask = BIT64(slot_gpio->d3), // Connect to peripheral after width configuration.
.mode = GPIO_MODE_OUTPUT, if (slot_gpio->d3 > GPIO_NUM_NC) {
.pull_up_en = 0, gpio_config_t gpio_conf = {
.pull_down_en = 0, .pin_bit_mask = BIT64(slot_gpio->d3),
.intr_type = GPIO_INTR_DISABLE, .mode = GPIO_MODE_OUTPUT,
}; .pull_up_en = 0,
gpio_config(&gpio_conf); .pull_down_en = 0,
gpio_set_level(slot_gpio->d3, 1); .intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&gpio_conf);
gpio_set_level(slot_gpio->d3, 1);
}
} }
} }
if (slot_width == 8) { if (slot_width == 8) {
@@ -755,6 +808,8 @@ esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t *slot_config)
return ret; return ret;
} }
s_host_ctx.slot_ctx[slot].slot_id = slot;
#if SOC_SDMMC_NUM_SLOTS >= 2 #if SOC_SDMMC_NUM_SLOTS >= 2
if (s_host_ctx.num_of_init_slots < SOC_SDMMC_NUM_SLOTS && s_host_ctx.active_slot_num != slot) { if (s_host_ctx.num_of_init_slots < SOC_SDMMC_NUM_SLOTS && s_host_ctx.active_slot_num != slot) {
s_host_ctx.num_of_init_slots += 1; s_host_ctx.num_of_init_slots += 1;
@@ -940,10 +995,21 @@ esp_err_t sdmmc_host_set_cclk_always_on(int slot, bool cclk_always_on)
} else { } else {
sdmmc_ll_enable_card_clock_low_power(s_host_ctx.hal.dev, slot, true); sdmmc_ll_enable_card_clock_low_power(s_host_ctx.hal.dev, slot, true);
} }
sdmmc_host_clock_update_command(slot); sdmmc_host_clock_update_command(slot, false);
return ESP_OK; return ESP_OK;
} }
void sdmmc_host_enable_clk_cmd11(int slot, bool enable)
{
sdmmc_ll_enable_card_clock(s_host_ctx.hal.dev, slot, enable);
sdmmc_host_clock_update_command(slot, true);
if (enable) {
sdmmc_ll_enable_1v8_mode(s_host_ctx.hal.dev, slot, true);
} else {
sdmmc_ll_enable_1v8_mode(s_host_ctx.hal.dev, slot, false);
}
}
void sdmmc_host_dma_stop(void) void sdmmc_host_dma_stop(void)
{ {
sdmmc_ll_stop_dma(s_host_ctx.hal.dev); sdmmc_ll_stop_dma(s_host_ctx.hal.dev);

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -36,6 +36,8 @@ void sdmmc_host_dma_resume(void);
bool sdmmc_host_card_busy(void); bool sdmmc_host_card_busy(void);
void sdmmc_host_enable_clk_cmd11(int slot, bool enable);
esp_err_t sdmmc_host_transaction_handler_init(void); esp_err_t sdmmc_host_transaction_handler_init(void);
void sdmmc_host_transaction_handler_deinit(void); void sdmmc_host_transaction_handler_deinit(void);

View File

@@ -5,6 +5,7 @@
*/ */
#include <string.h> #include <string.h>
#include <unistd.h>
#include "esp_err.h" #include "esp_err.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_check.h" #include "esp_check.h"
@@ -19,8 +20,9 @@
#include "driver/sdmmc_host.h" #include "driver/sdmmc_host.h"
#include "esp_cache.h" #include "esp_cache.h"
#include "esp_private/esp_cache_private.h" #include "esp_private/esp_cache_private.h"
#include "sdmmc_private.h" #include "sdmmc_internal.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "hal/sdmmc_ll.h"
/* Number of DMA descriptors used for transfer. /* Number of DMA descriptors used for transfer.
* Increasing this value above 4 doesn't improve performance for the usual case * Increasing this value above 4 doesn't improve performance for the usual case
@@ -37,6 +39,8 @@ typedef enum {
SDMMC_SENDING_CMD, SDMMC_SENDING_CMD,
SDMMC_SENDING_DATA, SDMMC_SENDING_DATA,
SDMMC_BUSY, SDMMC_BUSY,
SDMMC_SENDING_VOLTAGE_SWITCH,
SDMMC_WAITING_VOLTAGE_SWITCH,
} sdmmc_req_state_t; } sdmmc_req_state_t;
typedef struct { typedef struct {
@@ -70,14 +74,17 @@ static esp_pm_lock_handle_t s_pm_lock;
static esp_err_t handle_idle_state_events(void); static esp_err_t handle_idle_state_events(void);
static sdmmc_hw_cmd_t make_hw_cmd(sdmmc_command_t* cmd); static sdmmc_hw_cmd_t make_hw_cmd(sdmmc_command_t* cmd);
static esp_err_t handle_event(sdmmc_command_t* cmd, sdmmc_req_state_t* state, static esp_err_t handle_event(int slot, sdmmc_command_t* cmd, sdmmc_req_state_t* state,
sdmmc_event_t* unhandled_events); sdmmc_event_t* unhandled_events);
static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd, static esp_err_t process_events(int slot, sdmmc_event_t evt, sdmmc_command_t* cmd,
sdmmc_req_state_t* pstate, sdmmc_event_t* unhandled_events); sdmmc_req_state_t* pstate, sdmmc_event_t* unhandled_events);
static void process_command_response(uint32_t status, sdmmc_command_t* cmd); static void process_command_response(uint32_t status, sdmmc_command_t* cmd);
static void fill_dma_descriptors(size_t num_desc); static void fill_dma_descriptors(size_t num_desc);
static size_t get_free_descriptors_count(void); static size_t get_free_descriptors_count(void);
static bool wait_for_busy_cleared(uint32_t timeout_ms); static bool wait_for_busy_cleared(uint32_t timeout_ms);
static void handle_voltage_switch_stage1(int slot, sdmmc_command_t* cmd);
static void handle_voltage_switch_stage2(int slot, sdmmc_command_t* cmd);
static void handle_voltage_switch_stage3(int slot, sdmmc_command_t* cmd);
esp_err_t sdmmc_host_transaction_handler_init(void) esp_err_t sdmmc_host_transaction_handler_init(void)
{ {
@@ -124,6 +131,12 @@ esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo)
// dispose of any events which happened asynchronously // dispose of any events which happened asynchronously
handle_idle_state_events(); handle_idle_state_events();
// special handling for voltage switch command
if (cmdinfo->opcode == SD_SWITCH_VOLTAGE) {
handle_voltage_switch_stage1(slot, cmdinfo);
}
// convert cmdinfo to hardware register value // convert cmdinfo to hardware register value
sdmmc_hw_cmd_t hw_cmd = make_hw_cmd(cmdinfo); sdmmc_hw_cmd_t hw_cmd = make_hw_cmd(cmdinfo);
if (cmdinfo->data) { if (cmdinfo->data) {
@@ -174,9 +187,12 @@ esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo)
// process events until transfer is complete // process events until transfer is complete
cmdinfo->error = ESP_OK; cmdinfo->error = ESP_OK;
sdmmc_req_state_t state = SDMMC_SENDING_CMD; sdmmc_req_state_t state = SDMMC_SENDING_CMD;
if (cmdinfo->opcode == SD_SWITCH_VOLTAGE) {
state = SDMMC_SENDING_VOLTAGE_SWITCH;
}
sdmmc_event_t unhandled_events = { 0 }; sdmmc_event_t unhandled_events = { 0 };
while (state != SDMMC_IDLE) { while (state != SDMMC_IDLE) {
ret = handle_event(cmdinfo, &state, &unhandled_events); ret = handle_event(slot, cmdinfo, &state, &unhandled_events);
if (ret != ESP_OK) { if (ret != ESP_OK) {
break; break;
} }
@@ -286,7 +302,7 @@ static esp_err_t handle_idle_state_events(void)
return ESP_OK; return ESP_OK;
} }
static esp_err_t handle_event(sdmmc_command_t* cmd, sdmmc_req_state_t* state, static esp_err_t handle_event(int slot, sdmmc_command_t* cmd, sdmmc_req_state_t* state,
sdmmc_event_t* unhandled_events) sdmmc_event_t* unhandled_events)
{ {
sdmmc_event_t event; sdmmc_event_t event;
@@ -298,13 +314,13 @@ static esp_err_t handle_event(sdmmc_command_t* cmd, sdmmc_req_state_t* state,
} }
return err; return err;
} }
ESP_LOGV(TAG, "sdmmc_handle_event: event %08"PRIx32" %08"PRIx32", unhandled %08"PRIx32" %08"PRIx32, ESP_LOGV(TAG, "sdmmc_handle_event: slot %d event %08"PRIx32" %08"PRIx32", unhandled %08"PRIx32" %08"PRIx32,
event.sdmmc_status, event.dma_status, slot, event.sdmmc_status, event.dma_status,
unhandled_events->sdmmc_status, unhandled_events->dma_status); unhandled_events->sdmmc_status, unhandled_events->dma_status);
event.sdmmc_status |= unhandled_events->sdmmc_status; event.sdmmc_status |= unhandled_events->sdmmc_status;
event.dma_status |= unhandled_events->dma_status; event.dma_status |= unhandled_events->dma_status;
process_events(event, cmd, state, unhandled_events); process_events(slot, event, cmd, state, unhandled_events);
ESP_LOGV(TAG, "sdmmc_handle_event: events unhandled: %08"PRIx32" %08"PRIx32, unhandled_events->sdmmc_status, unhandled_events->dma_status); ESP_LOGV(TAG, "sdmmc_handle_event: slot %d events unhandled: %08"PRIx32" %08"PRIx32, slot, unhandled_events->sdmmc_status, unhandled_events->dma_status);
return ESP_OK; return ESP_OK;
} }
@@ -327,12 +343,15 @@ static sdmmc_hw_cmd_t make_hw_cmd(sdmmc_command_t* cmd)
res.stop_abort_cmd = 1; res.stop_abort_cmd = 1;
} else if (cmd->opcode == MMC_GO_IDLE_STATE) { } else if (cmd->opcode == MMC_GO_IDLE_STATE) {
res.send_init = 1; res.send_init = 1;
} else if (cmd->opcode == SD_SWITCH_VOLTAGE) {
res.volt_switch = 1;
} else { } else {
res.wait_complete = 1; res.wait_complete = 1;
} }
if (cmd->opcode == MMC_GO_IDLE_STATE) { if (cmd->opcode == MMC_GO_IDLE_STATE) {
res.send_init = 1; res.send_init = 1;
} }
if (cmd->flags & SCF_RSP_PRESENT) { if (cmd->flags & SCF_RSP_PRESENT) {
res.response_expect = 1; res.response_expect = 1;
if (cmd->flags & SCF_RSP_136) { if (cmd->flags & SCF_RSP_136) {
@@ -419,18 +438,20 @@ static inline bool mask_check_and_clear(uint32_t* state, uint32_t mask)
return ret; return ret;
} }
static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd, static esp_err_t process_events(int slot, sdmmc_event_t evt, sdmmc_command_t* cmd,
sdmmc_req_state_t* pstate, sdmmc_event_t* unhandled_events) sdmmc_req_state_t* pstate, sdmmc_event_t* unhandled_events)
{ {
const char* const s_state_names[] __attribute__((unused)) = { const char* const s_state_names[] __attribute__((unused)) = {
"IDLE", "IDLE",
"SENDING_CMD", "SENDING_CMD",
"SENDIND_DATA", "SENDIND_DATA",
"BUSY" "BUSY",
"SENDING_VOLTAGE_SWITCH",
"WAITING_VOLTAGE_SWITCH",
}; };
sdmmc_event_t orig_evt = evt; sdmmc_event_t orig_evt = evt;
ESP_LOGV(TAG, "%s: state=%s evt=%"PRIx32" dma=%"PRIx32, __func__, s_state_names[*pstate], ESP_LOGV(TAG, "%s: slot=%d state=%s evt=%"PRIx32" dma=%"PRIx32, __func__, slot,
evt.sdmmc_status, evt.dma_status); s_state_names[*pstate], evt.sdmmc_status, evt.dma_status);
sdmmc_req_state_t next_state = *pstate; sdmmc_req_state_t next_state = *pstate;
sdmmc_req_state_t state = (sdmmc_req_state_t) -1; sdmmc_req_state_t state = (sdmmc_req_state_t) -1;
while (next_state != state) { while (next_state != state) {
@@ -461,6 +482,32 @@ static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd,
} }
break; break;
case SDMMC_SENDING_VOLTAGE_SWITCH:
if (mask_check_and_clear(&evt.sdmmc_status, SDMMC_CMD_ERR_MASK)) {
process_command_response(orig_evt.sdmmc_status, cmd);
next_state = SDMMC_IDLE;
}
if (mask_check_and_clear(&evt.sdmmc_status, SDMMC_INTMASK_VOLT_SW)) {
handle_voltage_switch_stage2(slot, cmd);
if (cmd->error != ESP_OK) {
next_state = SDMMC_IDLE;
} else {
next_state = SDMMC_WAITING_VOLTAGE_SWITCH;
}
}
break;
case SDMMC_WAITING_VOLTAGE_SWITCH:
if (mask_check_and_clear(&evt.sdmmc_status, SDMMC_CMD_ERR_MASK)) {
process_command_response(orig_evt.sdmmc_status, cmd);
next_state = SDMMC_IDLE;
}
if (mask_check_and_clear(&evt.sdmmc_status, SDMMC_INTMASK_VOLT_SW)) {
handle_voltage_switch_stage3(slot, cmd);
next_state = SDMMC_IDLE;
}
break;
case SDMMC_SENDING_DATA: case SDMMC_SENDING_DATA:
if (mask_check_and_clear(&evt.sdmmc_status, SDMMC_DATA_ERR_MASK)) { if (mask_check_and_clear(&evt.sdmmc_status, SDMMC_DATA_ERR_MASK)) {
process_data_status(orig_evt.sdmmc_status, cmd); process_data_status(orig_evt.sdmmc_status, cmd);
@@ -518,3 +565,32 @@ static bool wait_for_busy_cleared(uint32_t timeout_ms)
} }
return false; return false;
} }
static void handle_voltage_switch_stage1(int slot, sdmmc_command_t* cmd)
{
ESP_LOGV(TAG, "%s: enabling clock", __func__);
sdmmc_host_set_cclk_always_on(slot, true);
}
static void handle_voltage_switch_stage2(int slot, sdmmc_command_t* cmd)
{
ESP_LOGV(TAG, "%s: disabling clock", __func__);
sdmmc_host_enable_clk_cmd11(slot, false);
usleep(100);
ESP_LOGV(TAG, "%s: switching voltage", __func__);
esp_err_t err = cmd->volt_switch_cb(cmd->volt_switch_cb_arg, 1800);
if (err != ESP_OK) {
ESP_LOGE(TAG, "failed to switch voltage (0x%x)", err);
cmd->error = err;
}
ESP_LOGV(TAG, "%s: waiting 10ms", __func__);
usleep(10000);
ESP_LOGV(TAG, "%s: enabling clock", __func__);
sdmmc_host_enable_clk_cmd11(slot, true);
}
static void handle_voltage_switch_stage3(int slot, sdmmc_command_t* cmd)
{
ESP_LOGV(TAG, "%s: voltage switch complete, clock back to low-power mode", __func__);
sdmmc_host_set_cclk_always_on(slot, false);
}

View File

@@ -33,6 +33,7 @@ typedef struct {
bool is_emmc; /*< True if this slot is connected to eMMC */ bool is_emmc; /*< True if this slot is connected to eMMC */
int bus_width; /*< SD interface width (1, 4 or 8) */ int bus_width; /*< SD interface width (1, 4 or 8) */
int max_freq_khz; /*< Max frequency (kHz) of SD interface, supported by the board */ int max_freq_khz; /*< Max frequency (kHz) of SD interface, supported by the board */
bool uhs1_supported; /*< Whether UHS-I mode is supported */
gpio_num_t clk; /*< CLK pin number */ gpio_num_t clk; /*< CLK pin number */
gpio_num_t cmd_mosi; /*< CMD pin number (SD mode) or MOSI (SPI mode) */ gpio_num_t cmd_mosi; /*< CMD pin number (SD mode) or MOSI (SPI mode) */
gpio_num_t d0_miso; /*< D0 pin number (SD mode) or MISO (SPI mode) */ gpio_num_t d0_miso; /*< D0 pin number (SD mode) or MISO (SPI mode) */

View File

@@ -34,6 +34,9 @@ void sdmmc_test_board_get_config_sdmmc(int slot_index, sdmmc_host_t *out_host_co
if (slot->max_freq_khz > 0) { if (slot->max_freq_khz > 0) {
out_host_config->max_freq_khz = slot->max_freq_khz; out_host_config->max_freq_khz = slot->max_freq_khz;
} }
if (slot->uhs1_supported) {
out_slot_config->flags |= SDMMC_SLOT_FLAG_UHS1;
}
#if SOC_SDMMC_USE_GPIO_MATRIX #if SOC_SDMMC_USE_GPIO_MATRIX
out_slot_config->clk = slot->clk; out_slot_config->clk = slot->clk;

View File

@@ -323,12 +323,10 @@ static const sdmmc_test_board_info_t s_board_info = {
static const sdmmc_test_board_info_t s_board_info = { static const sdmmc_test_board_info_t s_board_info = {
.name = "ESP32-P4 Function EV Board", .name = "ESP32-P4 Function EV Board",
.slot = { .slot = {
{
.slot_exists = false
},
{ {
.slot_exists = true, .slot_exists = true,
.bus_width = 4, .bus_width = 4,
.uhs1_supported = true,
.clk = 43, .clk = 43,
.cmd_mosi = 44, .cmd_mosi = 44,
.d0_miso = 39, .d0_miso = 39,
@@ -342,6 +340,9 @@ static const sdmmc_test_board_info_t s_board_info = {
.cd = GPIO_NUM_NC, .cd = GPIO_NUM_NC,
.wp = GPIO_NUM_NC, .wp = GPIO_NUM_NC,
.unused_pin = 54, .unused_pin = 54,
},
{
.slot_exists = false,
} }
}, },
}; };

View File

@@ -58,6 +58,7 @@ void sdmmc_test_sd_begin(int slot, int width, int freq_khz, int ddr, sdmmc_card_
} }
config.max_freq_khz = freq_khz; config.max_freq_khz = freq_khz;
bool slot_is_uhs1 = slot_config.flags & SDMMC_SLOT_FLAG_UHS1;
if (width == 1) { if (width == 1) {
config.flags = SDMMC_HOST_FLAG_1BIT; config.flags = SDMMC_HOST_FLAG_1BIT;
@@ -78,6 +79,10 @@ void sdmmc_test_sd_begin(int slot, int width, int freq_khz, int ddr, sdmmc_card_
config.flags |= SDMMC_HOST_FLAG_DDR; config.flags |= SDMMC_HOST_FLAG_DDR;
} }
if (slot_is_uhs1) {
slot_config.flags |= SDMMC_SLOT_FLAG_UHS1;
}
#if SOC_SDMMC_IO_POWER_EXTERNAL #if SOC_SDMMC_IO_POWER_EXTERNAL
#define SDMMC_PWR_LDO_CHANNEL 4 #define SDMMC_PWR_LDO_CHANNEL 4
sd_pwr_ctrl_ldo_config_t ldo_config = { sd_pwr_ctrl_ldo_config_t ldo_config = {

View File

@@ -40,6 +40,8 @@ typedef int sdspi_dev_handle_t;
.slot = SDSPI_DEFAULT_HOST, \ .slot = SDSPI_DEFAULT_HOST, \
.max_freq_khz = SDMMC_FREQ_DEFAULT, \ .max_freq_khz = SDMMC_FREQ_DEFAULT, \
.io_voltage = 3.3f, \ .io_voltage = 3.3f, \
.driver_strength = SDMMC_DRIVER_STRENGTH_B, \
.current_limit = SDMMC_CURRENT_LIMIT_200MA, \
.init = &sdspi_host_init, \ .init = &sdspi_host_init, \
.set_bus_width = NULL, \ .set_bus_width = NULL, \
.get_bus_width = NULL, \ .get_bus_width = NULL, \
@@ -57,6 +59,7 @@ typedef int sdspi_dev_handle_t;
.dma_aligned_buffer = NULL, \ .dma_aligned_buffer = NULL, \
.pwr_ctrl_handle = NULL, \ .pwr_ctrl_handle = NULL, \
.get_dma_info = &sdspi_host_get_dma_info, \ .get_dma_info = &sdspi_host_get_dma_info, \
.is_slot_set_to_uhs1 = NULL, \
} }
/** /**

View File

@@ -56,8 +56,9 @@ esp_err_t esp_clk_tree_src_get_freq_hz(soc_module_clk_t clk_src, esp_clk_tree_sr
case SOC_MOD_CLK_APLL: case SOC_MOD_CLK_APLL:
clk_src_freq = clk_hal_apll_get_freq_hz(); clk_src_freq = clk_hal_apll_get_freq_hz();
break; break;
// case SOC_MOD_CLK_SDIO_PLL: TODO: IDF-8886 case SOC_MOD_CLK_SDIO_PLL:
// break; clk_src_freq = CLK_LL_PLL_SDIO_FREQ_MHZ * MHZ;
break;
case SOC_MOD_CLK_RTC_SLOW: case SOC_MOD_CLK_RTC_SLOW:
clk_src_freq = esp_clk_tree_lp_slow_get_freq_hz(precision); clk_src_freq = esp_clk_tree_lp_slow_get_freq_hz(precision);
break; break;

View File

@@ -408,6 +408,18 @@ static inline bool sdmmc_ll_is_card_write_protected(sdmmc_dev_t *hw, uint32_t sl
return is_protected; 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)
{
//for compatibility
}
/** /**
* @brief Enable DDR mode * @brief Enable DDR mode
* *

View File

@@ -40,6 +40,7 @@ extern "C" {
#define CLK_LL_PLL_80M_FREQ_MHZ (80) #define CLK_LL_PLL_80M_FREQ_MHZ (80)
#define CLK_LL_PLL_160M_FREQ_MHZ (160) #define CLK_LL_PLL_160M_FREQ_MHZ (160)
#define CLK_LL_PLL_240M_FREQ_MHZ (240) #define CLK_LL_PLL_240M_FREQ_MHZ (240)
#define CLK_LL_PLL_SDIO_FREQ_MHZ (200)
#define CLK_LL_PLL_360M_FREQ_MHZ (360) #define CLK_LL_PLL_360M_FREQ_MHZ (360)
#define CLK_LL_PLL_400M_FREQ_MHZ (400) #define CLK_LL_PLL_400M_FREQ_MHZ (400)

View File

@@ -24,6 +24,7 @@
#include "soc/sdmmc_reg.h" #include "soc/sdmmc_reg.h"
#include "soc/hp_sys_clkrst_struct.h" #include "soc/hp_sys_clkrst_struct.h"
#include "soc/lp_clkrst_struct.h" #include "soc/lp_clkrst_struct.h"
#include "soc/pmu_reg.h"
#ifdef __cplusplus #ifdef __cplusplus
@@ -124,6 +125,37 @@ static inline void sdmmc_ll_reset_register(sdmmc_dev_t *hw)
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance /// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define sdmmc_ll_reset_register(...) (void)__DECLARE_RCC_ATOMIC_ENV; sdmmc_ll_reset_register(__VA_ARGS__) #define sdmmc_ll_reset_register(...) (void)__DECLARE_RCC_ATOMIC_ENV; sdmmc_ll_reset_register(__VA_ARGS__)
/**
* @brief Enable the bus clock for SDIO PLL
*
* @param hw hardware instance address
* @param en enable / disable
*/
static inline void sdmmc_ll_enable_sdio_pll(sdmmc_dev_t *hw, bool en)
{
if (en) {
REG_SET_BIT(PMU_RF_PWC_REG, PMU_SDIO_PLL_XPD);
REG_SET_BIT(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_XPD_SDIOPLL_I2C);
REG_SET_BIT(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_XPD_SDIOPLL);
REG_SET_BIT(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_GLOBAL_SDIOPLL_ICG);
LP_AON_CLKRST.hp_clk_ctrl.hp_sdio_pll0_clk_en = 1;
LP_AON_CLKRST.hp_clk_ctrl.hp_sdio_pll1_clk_en = 1;
LP_AON_CLKRST.hp_clk_ctrl.hp_sdio_pll2_clk_en = 1;
} else {
REG_CLR_BIT(PMU_RF_PWC_REG, PMU_SDIO_PLL_XPD);
REG_CLR_BIT(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_XPD_SDIOPLL_I2C);
REG_CLR_BIT(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_XPD_SDIOPLL);
REG_CLR_BIT(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_GLOBAL_SDIOPLL_ICG);
LP_AON_CLKRST.hp_clk_ctrl.hp_sdio_pll0_clk_en = 0;
LP_AON_CLKRST.hp_clk_ctrl.hp_sdio_pll0_clk_en = 0;
LP_AON_CLKRST.hp_clk_ctrl.hp_sdio_pll2_clk_en = 0;
}
}
/// 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_sdio_pll(...) (void)__DECLARE_RCC_ATOMIC_ENV; sdmmc_ll_enable_sdio_pll(__VA_ARGS__)
/** /**
* @brief Select SDMMC clock source * @brief Select SDMMC clock source
* *
@@ -137,9 +169,9 @@ static inline void sdmmc_ll_select_clk_source(sdmmc_dev_t *hw, soc_periph_sdmmc_
case SDMMC_CLK_SRC_PLL160M: case SDMMC_CLK_SRC_PLL160M:
clk_val = 0; clk_val = 0;
break; break;
// case SDMMC_CLK_SRC_PLL200M: // TODO: IDF-8886 case SDMMC_CLK_SRC_SDIO_200M:
// clk_val = 1; clk_val = 1;
// break; break;
default: default:
HAL_ASSERT(false); HAL_ASSERT(false);
break; break;
@@ -161,12 +193,18 @@ static inline void sdmmc_ll_select_clk_source(sdmmc_dev_t *hw, soc_periph_sdmmc_
*/ */
static inline void sdmmc_ll_set_clock_div(sdmmc_dev_t *hw, uint32_t div) static inline void sdmmc_ll_set_clock_div(sdmmc_dev_t *hw, uint32_t div)
{ {
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_h = div / 2 - 1; if (div > 1) {
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_n = div - 1; HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_h = div / 2 - 1;
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_l = div - 1; HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_n = div - 1;
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_l = div - 1;
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_cfg_update = 1; HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_cfg_update = 1;
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_cfg_update = 0; HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_cfg_update = 0;
} else {
HP_SYS_CLKRST.peri_clk_ctrl01.reg_sdio_hs_mode = 1;
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_h = 0;
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_n = 0;
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_l = 0;
}
} }
/// use a macro to wrap the function, force the caller to use it in a critical section /// use a macro to wrap the function, force the caller to use it in a critical section
@@ -192,7 +230,16 @@ static inline void sdmmc_ll_deinit_clk(sdmmc_dev_t *hw)
*/ */
static inline uint32_t sdmmc_ll_get_clock_div(sdmmc_dev_t *hw) static inline uint32_t sdmmc_ll_get_clock_div(sdmmc_dev_t *hw)
{ {
return HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_l + 1; uint32_t div = 0;
if (HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_h == 0 &&
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_n == 0 &&
HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_l == 0) {
div = 1;
} else {
div = HP_SYS_CLKRST.peri_clk_ctrl02.reg_sdio_ls_clk_edge_l + 1;
}
return div;
} }
/** /**
@@ -451,6 +498,22 @@ static inline bool sdmmc_ll_is_card_write_protected(sdmmc_dev_t *hw, uint32_t sl
return is_protected; 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 * @brief Enable DDR mode
* *

View File

@@ -444,6 +444,18 @@ static inline bool sdmmc_ll_is_card_write_protected(sdmmc_dev_t *hw, uint32_t sl
return is_protected; 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)
{
//for compatibility
}
/** /**
* @brief Enable DDR mode * @brief Enable DDR mode
* *

View File

@@ -3,7 +3,7 @@
* *
* SPDX-License-Identifier: ISC * SPDX-License-Identifier: ISC
* *
* SPDX-FileContributor: 2016-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileContributor: 2016-2024 Espressif Systems (Shanghai) CO LTD
*/ */
/* /*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -46,6 +46,7 @@ extern "C" {
#define MMC_SET_BLOCKLEN 16 /* R1 */ #define MMC_SET_BLOCKLEN 16 /* R1 */
#define MMC_READ_BLOCK_SINGLE 17 /* R1 */ #define MMC_READ_BLOCK_SINGLE 17 /* R1 */
#define MMC_READ_BLOCK_MULTIPLE 18 /* R1 */ #define MMC_READ_BLOCK_MULTIPLE 18 /* R1 */
#define MMC_SEND_TUNING_BLOCK 19 /* R1 */
#define MMC_WRITE_DAT_UNTIL_STOP 20 /* R1 */ #define MMC_WRITE_DAT_UNTIL_STOP 20 /* R1 */
#define MMC_SET_BLOCK_COUNT 23 /* R1 */ #define MMC_SET_BLOCK_COUNT 23 /* R1 */
#define MMC_WRITE_BLOCK_SINGLE 24 /* R1 */ #define MMC_WRITE_BLOCK_SINGLE 24 /* R1 */
@@ -59,6 +60,7 @@ extern "C" {
#define SD_SEND_RELATIVE_ADDR 3 /* R6 */ #define SD_SEND_RELATIVE_ADDR 3 /* R6 */
#define SD_SEND_SWITCH_FUNC 6 /* R1 */ #define SD_SEND_SWITCH_FUNC 6 /* R1 */
#define SD_SEND_IF_COND 8 /* R7 */ #define SD_SEND_IF_COND 8 /* R7 */
#define SD_SWITCH_VOLTAGE 11 /* R1 */
#define SD_ERASE_GROUP_START 32 /* R1 */ #define SD_ERASE_GROUP_START 32 /* R1 */
#define SD_ERASE_GROUP_END 33 /* R1 */ #define SD_ERASE_GROUP_END 33 /* R1 */
#define SD_READ_OCR 58 /* R3 */ #define SD_READ_OCR 58 /* R3 */
@@ -98,8 +100,20 @@ extern "C" {
#define MMC_OCR_2_0V_2_1V (1<<8) #define MMC_OCR_2_0V_2_1V (1<<8)
#define MMC_OCR_1_65V_1_95V (1<<7) #define MMC_OCR_1_65V_1_95V (1<<7)
#define SD_OCR_SDHC_CAP (1<<30) #define SD_OCR_CARD_READY MMC_OCR_MEM_READY /* bit-31: power-up status */
#define SD_OCR_VOL_MASK 0xFF8000 /* bits 23:15 */ #define SD_OCR_SDHC_CAP (1<<30) /* HCS bit */
#define SD_OCR_XPC (1<<28) /* SDXC Power Control (bit 28) */
#define SD_OCR_S18_RA (1<<24) /* S18R/A bit: 1.8V voltage support, UHS-I only */
#define SD_OCR_VOL_MASK 0xFF8000 /* SD OCR voltage bits 23:15 */
#define SD_OCR_3_5V_3_6V MMC_OCR_3_5V_3_6V /* bit-23 */
#define SD_OCR_3_4V_3_5V MMC_OCR_3_4V_3_5V /* bit-22 */
#define SD_OCR_3_3V_3_4V MMC_OCR_3_3V_3_4V /* ... */
#define SD_OCR_3_2V_3_3V MMC_OCR_3_2V_3_3V
#define SD_OCR_3_1V_3_2V MMC_OCR_3_1V_3_2V
#define SD_OCR_3_0V_3_1V MMC_OCR_3_0V_3_1V
#define SD_OCR_2_9V_3_0V MMC_OCR_2_9V_3_0V
#define SD_OCR_2_8V_2_9V MMC_OCR_2_8V_2_9V /* ... */
#define SD_OCR_2_7V_2_8V MMC_OCR_2_7V_2_8V /* bit-15 */
/* SD mode R1 response type bits */ /* SD mode R1 response type bits */
#define MMC_R1_READY_FOR_DATA (1<<8) /* ready for next transfer */ #define MMC_R1_READY_FOR_DATA (1<<8) /* ready for next transfer */
@@ -290,6 +304,8 @@ extern "C" {
#define SD_CSD_SPEED(resp) MMC_RSP_BITS((resp), 96, 8) #define SD_CSD_SPEED(resp) MMC_RSP_BITS((resp), 96, 8)
#define SD_CSD_SPEED_25_MHZ 0x32 #define SD_CSD_SPEED_25_MHZ 0x32
#define SD_CSD_SPEED_50_MHZ 0x5a #define SD_CSD_SPEED_50_MHZ 0x5a
#define SD_CSD_SPEED_100_MHZ 0xb
#define SD_CSD_SPEED_200_MHZ 0x2b
#define SD_CSD_CCC(resp) MMC_RSP_BITS((resp), 84, 12) #define SD_CSD_CCC(resp) MMC_RSP_BITS((resp), 84, 12)
#define SD_CSD_CCC_BASIC (1 << 0) /* basic */ #define SD_CSD_CCC_BASIC (1 << 0) /* basic */
#define SD_CSD_CCC_BR (1 << 2) /* block read */ #define SD_CSD_CCC_BR (1 << 2) /* block read */
@@ -403,6 +419,14 @@ extern "C" {
#define SD_SFUNC_FUNC_MAX 15 #define SD_SFUNC_FUNC_MAX 15
#define SD_ACCESS_MODE 1 /* Function group 1, Access Mode */ #define SD_ACCESS_MODE 1 /* Function group 1, Access Mode */
#define SD_COMMAND_SYSTEM 2 /* Function group 1, Command System */
#define SD_DRIVER_STRENGTH 3 /* Function group 1, Driver Strength */
#define SD_CURRENT_LIMIT 4 /* Function group 1, Current Limit */
#define SD_DRIVER_STRENGTH_B 0 /* Type B */
#define SD_DRIVER_STRENGTH_A 1 /* Type A */
#define SD_DRIVER_STRENGTH_C 2 /* Type C */
#define SD_DRIVER_STRENGTH_D 3 /* Type D */
#define SD_ACCESS_MODE_SDR12 0 /* 25 MHz clock */ #define SD_ACCESS_MODE_SDR12 0 /* 25 MHz clock */
#define SD_ACCESS_MODE_SDR25 1 /* 50 MHz clock */ #define SD_ACCESS_MODE_SDR25 1 /* 50 MHz clock */

View File

@@ -149,12 +149,18 @@ typedef struct {
/** @endcond */ /** @endcond */
esp_err_t error; /*!< error returned from transfer */ esp_err_t error; /*!< error returned from transfer */
uint32_t timeout_ms; /*!< response timeout, in milliseconds */ uint32_t timeout_ms; /*!< response timeout, in milliseconds */
esp_err_t (*volt_switch_cb)(void*, int); /*!< callback to be called during CMD11 to switch voltage */
void* volt_switch_cb_arg; /*!< argument to be passed to the CMD11 callback */
} sdmmc_command_t; } sdmmc_command_t;
/** /**
* SD/MMC Host clock timing delay phases * SD/MMC Host clock timing delay phases
* *
* This will only take effect when the host works in SDMMC_FREQ_HIGHSPEED or SDMMC_FREQ_52M. * This will only take effect when the host works in
* - SDMMC_FREQ_HIGHSPEED
* - SDMMC_FREQ_52M
* - SDR50
* - DDR50
* Driver will print out how long the delay is, in picosecond (ps). * Driver will print out how long the delay is, in picosecond (ps).
*/ */
typedef enum { typedef enum {
@@ -162,8 +168,29 @@ typedef enum {
SDMMC_DELAY_PHASE_1, /*!< Delay phase 1 */ SDMMC_DELAY_PHASE_1, /*!< Delay phase 1 */
SDMMC_DELAY_PHASE_2, /*!< Delay phase 2 */ SDMMC_DELAY_PHASE_2, /*!< Delay phase 2 */
SDMMC_DELAY_PHASE_3, /*!< Delay phase 3 */ SDMMC_DELAY_PHASE_3, /*!< Delay phase 3 */
SDMMC_DELAY_PHASE_AUTO, /*!< Auto detect phase, only valid for UHS-I mode */
} sdmmc_delay_phase_t; } sdmmc_delay_phase_t;
/**
* @brief SD/MMC Driver Strength
*/
typedef enum {
SDMMC_DRIVER_STRENGTH_B, /*!< Type B */
SDMMC_DRIVER_STRENGTH_A, /*!< Type A */
SDMMC_DRIVER_STRENGTH_C, /*!< Type C */
SDMMC_DRIVER_STRENGTH_D, /*!< Type D */
} sdmmc_driver_strength_t;
/**
* @brief SD/MMC Current Limit
*/
typedef enum {
SDMMC_CURRENT_LIMIT_200MA, /*!< 200 mA */
SDMMC_CURRENT_LIMIT_400MA, /*!< 400 mA */
SDMMC_CURRENT_LIMIT_600MA, /*!< 600 mA */
SDMMC_CURRENT_LIMIT_800MA, /*!< 800 mA */
} sdmmc_current_limit_t;
/** /**
* SD/MMC Host description * SD/MMC Host description
* *
@@ -190,7 +217,11 @@ typedef struct {
#define SDMMC_FREQ_PROBING 400 /*!< SD/MMC probing speed */ #define SDMMC_FREQ_PROBING 400 /*!< SD/MMC probing speed */
#define SDMMC_FREQ_52M 52000 /*!< MMC 52MHz speed */ #define SDMMC_FREQ_52M 52000 /*!< MMC 52MHz speed */
#define SDMMC_FREQ_26M 26000 /*!< MMC 26MHz speed */ #define SDMMC_FREQ_26M 26000 /*!< MMC 26MHz speed */
#define SDMMC_FREQ_DDR50 50000 /*!< MMC 50MHz speed */
#define SDMMC_FREQ_SDR50 100000 /*!< MMC 100MHz speed */
float io_voltage; /*!< I/O voltage used by the controller (voltage switching is not supported) */ float io_voltage; /*!< I/O voltage used by the controller (voltage switching is not supported) */
sdmmc_driver_strength_t driver_strength; /*!< Driver Strength */
sdmmc_current_limit_t current_limit; /*!< Current Limit */
esp_err_t (*init)(void); /*!< Host function to initialize the driver */ esp_err_t (*init)(void); /*!< Host function to initialize the driver */
esp_err_t (*set_bus_width)(int slot, size_t width); /*!< host function to set bus width */ esp_err_t (*set_bus_width)(int slot, size_t width); /*!< host function to set bus width */
size_t (*get_bus_width)(int slot); /*!< host function to get bus width */ size_t (*get_bus_width)(int slot); /*!< host function to get bus width */
@@ -211,6 +242,7 @@ typedef struct {
void* dma_aligned_buffer; /*!< Leave it NULL. Reserved for cache aligned buffers for SDIO mode */ void* dma_aligned_buffer; /*!< Leave it NULL. Reserved for cache aligned buffers for SDIO mode */
sd_pwr_ctrl_handle_t pwr_ctrl_handle; /*!< Power control handle */ sd_pwr_ctrl_handle_t pwr_ctrl_handle; /*!< Power control handle */
esp_err_t (*get_dma_info)(int slot, esp_dma_mem_info_t *dma_mem_info); /*!< host function to dma memory information*/ esp_err_t (*get_dma_info)(int slot, esp_dma_mem_info_t *dma_mem_info); /*!< host function to dma memory information*/
esp_err_t (*is_slot_set_to_uhs1)(int slot, bool *is_uhs1); /*!< host slot is set to uhs1 or not*/
} sdmmc_host_t; } sdmmc_host_t;
/** /**
@@ -229,7 +261,7 @@ typedef struct {
sdmmc_ssr_t ssr; /*!< decoded SSR (SD Status Register) value */ sdmmc_ssr_t ssr; /*!< decoded SSR (SD Status Register) value */
sdmmc_ext_csd_t ext_csd; /*!< decoded EXT_CSD (Extended Card Specific Data) register value */ sdmmc_ext_csd_t ext_csd; /*!< decoded EXT_CSD (Extended Card Specific Data) register value */
uint16_t rca; /*!< RCA (Relative Card Address) */ uint16_t rca; /*!< RCA (Relative Card Address) */
uint16_t max_freq_khz; /*!< Maximum frequency, in kHz, supported by the card */ uint32_t max_freq_khz; /*!< Maximum frequency, in kHz, supported by the card */
int real_freq_khz; /*!< Real working frequency, in kHz, configured on the host controller */ int real_freq_khz; /*!< Real working frequency, in kHz, configured on the host controller */
uint32_t is_mem : 1; /*!< Bit indicates if the card is a memory card */ uint32_t is_mem : 1; /*!< Bit indicates if the card is a memory card */
uint32_t is_sdio : 1; /*!< Bit indicates if the card is an IO card */ uint32_t is_sdio : 1; /*!< Bit indicates if the card is an IO card */
@@ -237,7 +269,8 @@ typedef struct {
uint32_t num_io_functions : 3; /*!< If is_sdio is 1, contains the number of IO functions on the card */ uint32_t num_io_functions : 3; /*!< If is_sdio is 1, contains the number of IO functions on the card */
uint32_t log_bus_width : 2; /*!< log2(bus width supported by card) */ uint32_t log_bus_width : 2; /*!< log2(bus width supported by card) */
uint32_t is_ddr : 1; /*!< Card supports DDR mode */ uint32_t is_ddr : 1; /*!< Card supports DDR mode */
uint32_t reserved : 23; /*!< Reserved for future expansion */ uint32_t is_uhs1 : 1; /*!< Card supports UHS-1 mode */
uint32_t reserved : 22; /*!< Reserved for future expansion */
} sdmmc_card_t; } sdmmc_card_t;
/** /**

View File

@@ -42,6 +42,16 @@ esp_err_t sdmmc_init_ocr(sdmmc_card_t* card)
acmd41_arg |= SD_OCR_SDHC_CAP; acmd41_arg |= SD_OCR_SDHC_CAP;
} }
bool to_set_to_uhs1 = false;
if (card->host.is_slot_set_to_uhs1) {
ESP_RETURN_ON_ERROR(card->host.is_slot_set_to_uhs1(card->host.slot, &to_set_to_uhs1), TAG, "failed to get slot info");
}
if (to_set_to_uhs1) {
acmd41_arg |= SD_OCR_S18_RA;
acmd41_arg |= SD_OCR_XPC;
}
ESP_LOGV(TAG, "%s: acmd41_arg=0x%08" PRIx32, __func__, card->ocr);
/* Send SEND_OP_COND (ACMD41) command to the card until it becomes ready. */ /* Send SEND_OP_COND (ACMD41) command to the card until it becomes ready. */
err = sdmmc_send_cmd_send_op_cond(card, acmd41_arg, &card->ocr); err = sdmmc_send_cmd_send_op_cond(card, acmd41_arg, &card->ocr);
@@ -182,6 +192,21 @@ esp_err_t sdmmc_init_card_hs_mode(sdmmc_card_t* card)
return ESP_OK; return ESP_OK;
} }
esp_err_t sdmmc_init_sd_driver_strength(sdmmc_card_t *card)
{
return sdmmc_select_driver_strength(card, card->host.driver_strength);
}
esp_err_t sdmmc_init_sd_current_limit(sdmmc_card_t *card)
{
return sdmmc_select_current_limit(card, card->host.current_limit);
}
esp_err_t sdmmc_init_sd_timing_tuning(sdmmc_card_t *card)
{
return sdmmc_do_timing_tuning(card);
}
esp_err_t sdmmc_init_host_bus_width(sdmmc_card_t* card) esp_err_t sdmmc_init_host_bus_width(sdmmc_card_t* card)
{ {
int bus_width = 1; int bus_width = 1;
@@ -209,6 +234,14 @@ esp_err_t sdmmc_init_host_frequency(sdmmc_card_t* card)
esp_err_t err; esp_err_t err;
assert(card->max_freq_khz <= card->host.max_freq_khz); assert(card->max_freq_khz <= card->host.max_freq_khz);
#if !SOC_SDMMC_UHS_I_SUPPORTED
ESP_RETURN_ON_FALSE(card->host.input_delay_phase != SDMMC_DELAY_PHASE_AUTO, ESP_ERR_INVALID_ARG, TAG, "auto tuning not supported");
#endif
if (card->host.input_delay_phase == SDMMC_DELAY_PHASE_AUTO) {
ESP_RETURN_ON_FALSE((card->host.max_freq_khz == SDMMC_FREQ_SDR50 || card->host.max_freq_khz == SDMMC_FREQ_SDR104), ESP_ERR_INVALID_ARG, TAG, "auto tuning only supported for SDR50 / SDR104");
}
if (card->max_freq_khz > SDMMC_FREQ_PROBING) { if (card->max_freq_khz > SDMMC_FREQ_PROBING) {
err = (*card->host.set_card_clk)(card->host.slot, card->max_freq_khz); err = (*card->host.set_card_clk)(card->host.slot, card->max_freq_khz);
if (err != ESP_OK) { if (err != ESP_OK) {
@@ -278,7 +311,15 @@ void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card)
type = "MMC"; type = "MMC";
print_csd = true; print_csd = true;
} else { } else {
type = (card->ocr & SD_OCR_SDHC_CAP) ? "SDHC/SDXC" : "SDSC"; if ((card->ocr & SD_OCR_SDHC_CAP) == 0) {
type = "SDSC";
} else {
if (card->ocr & SD_OCR_S18_RA) {
type = "SDHC/SDXC (UHS-I)";
} else {
type = "SDHC";
}
}
print_csd = true; print_csd = true;
} }
fprintf(stream, "Type: %s\n", type); fprintf(stream, "Type: %s\n", type);
@@ -332,6 +373,19 @@ esp_err_t sdmmc_fix_host_flags(sdmmc_card_t* card)
card->host.flags |= width_4bit; card->host.flags |= width_4bit;
} }
} }
#if !SOC_SDMMC_UHS_I_SUPPORTED
if ((card->host.max_freq_khz == SDMMC_FREQ_SDR50) ||
(card->host.max_freq_khz == SDMMC_FREQ_DDR50) ||
(card->host.max_freq_khz == SDMMC_FREQ_SDR104)) {
ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "UHS-I is not supported");
}
#else
if (card->host.max_freq_khz == SDMMC_FREQ_DDR50) {
ESP_RETURN_ON_FALSE(((card->host.flags & SDMMC_HOST_FLAG_DDR) != 0), ESP_ERR_INVALID_ARG, TAG, "DDR is not selected");
}
#endif
return ESP_OK; return ESP_OK;
} }

View File

@@ -18,6 +18,7 @@
#include <string.h> #include <string.h>
#include "esp_log.h" #include "esp_log.h"
#include "esp_check.h"
#include "esp_heap_caps.h" #include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
@@ -26,6 +27,7 @@
#include "sdmmc_cmd.h" #include "sdmmc_cmd.h"
#include "sys/param.h" #include "sys/param.h"
#include "soc/soc_memory_layout.h" #include "soc/soc_memory_layout.h"
#include "soc/soc_caps.h"
#include "esp_dma_utils.h" #include "esp_dma_utils.h"
#define SDMMC_GO_IDLE_DELAY_MS 20 #define SDMMC_GO_IDLE_DELAY_MS 20
@@ -57,6 +59,8 @@
#define SDMMC_MMC_TRIM_ARG 1 #define SDMMC_MMC_TRIM_ARG 1
#define SDMMC_MMC_DISCARD_ARG 3 #define SDMMC_MMC_DISCARD_ARG 3
#define SDMMC_FREQ_SDR104 208000 /*!< MMC 208MHz speed */
/* Functions to send individual commands */ /* Functions to send individual commands */
esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd); esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd);
esp_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd); esp_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd);
@@ -77,15 +81,19 @@ esp_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_scr);
esp_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width); esp_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width);
esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status); esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status);
esp_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable); esp_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable);
esp_err_t sdmmc_send_cmd_voltage_switch(sdmmc_card_t* card);
/* Higher level functions */ /* Higher level functions */
esp_err_t sdmmc_enable_hs_mode(sdmmc_card_t* card); esp_err_t sdmmc_enter_higher_speed_mode(sdmmc_card_t* card);
esp_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card); esp_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card);
esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src, esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src,
size_t start_block, size_t block_count, size_t buffer_len); size_t start_block, size_t block_count, size_t buffer_len);
esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst, esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
size_t start_block, size_t block_count, size_t buffer_len); size_t start_block, size_t block_count, size_t buffer_len);
uint32_t sdmmc_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb); uint32_t sdmmc_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb);
esp_err_t sdmmc_select_driver_strength(sdmmc_card_t *card, sdmmc_driver_strength_t driver_strength);
esp_err_t sdmmc_select_current_limit(sdmmc_card_t *card, sdmmc_current_limit_t current_limit);
esp_err_t sdmmc_do_timing_tuning(sdmmc_card_t *card);
/* SD specific */ /* SD specific */
esp_err_t sdmmc_check_scr(sdmmc_card_t* card); esp_err_t sdmmc_check_scr(sdmmc_card_t* card);
@@ -139,6 +147,10 @@ esp_err_t sdmmc_init_mmc_bus_width(sdmmc_card_t* card);
esp_err_t sdmmc_init_card_hs_mode(sdmmc_card_t* card); esp_err_t sdmmc_init_card_hs_mode(sdmmc_card_t* card);
esp_err_t sdmmc_init_host_frequency(sdmmc_card_t* card); esp_err_t sdmmc_init_host_frequency(sdmmc_card_t* card);
esp_err_t sdmmc_init_mmc_check_ext_csd(sdmmc_card_t* card); esp_err_t sdmmc_init_mmc_check_ext_csd(sdmmc_card_t* card);
esp_err_t sdmmc_init_sd_uhs1(sdmmc_card_t* card);
esp_err_t sdmmc_init_sd_driver_strength(sdmmc_card_t *card);
esp_err_t sdmmc_init_sd_current_limit(sdmmc_card_t *card);
esp_err_t sdmmc_init_sd_timing_tuning(sdmmc_card_t *card);
/* Various helper functions */ /* Various helper functions */
static inline bool host_is_spi(const sdmmc_card_t* card) static inline bool host_is_spi(const sdmmc_card_t* card)

View File

@@ -95,6 +95,11 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
ESP_LOGD(TAG, "%s: card type is %s", __func__, ESP_LOGD(TAG, "%s: card type is %s", __func__,
is_sdio ? "SDIO" : is_mmc ? "MMC" : "SD"); is_sdio ? "SDIO" : is_mmc ? "MMC" : "SD");
/* switch to 1.8V if supported (UHS-I) */
bool is_uhs1 = is_sdmem && (card->ocr & SD_OCR_S18_RA) && (card->ocr & SD_OCR_SDHC_CAP);
ESP_LOGV(TAG, "is_uhs1: %d", is_uhs1);
SDMMC_INIT_STEP(is_uhs1, sdmmc_init_sd_uhs1);
/* Read the contents of CID register*/ /* Read the contents of CID register*/
SDMMC_INIT_STEP(is_mem, sdmmc_init_cid); SDMMC_INIT_STEP(is_mem, sdmmc_init_cid);
@@ -142,12 +147,21 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
SDMMC_INIT_STEP(always, sdmmc_init_host_bus_width); SDMMC_INIT_STEP(always, sdmmc_init_host_bus_width);
} }
/* Driver Strength */
SDMMC_INIT_STEP(is_uhs1, sdmmc_init_sd_driver_strength);
/* Current Limit */
SDMMC_INIT_STEP(is_uhs1, sdmmc_init_sd_current_limit);
/* SD card: read SD Status register */ /* SD card: read SD Status register */
SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_ssr); SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_ssr);
/* Switch to the host to use card->max_freq_khz frequency. */ /* Switch to the host to use card->max_freq_khz frequency. */
SDMMC_INIT_STEP(always, sdmmc_init_host_frequency); SDMMC_INIT_STEP(always, sdmmc_init_host_frequency);
/* Timing tuning */
SDMMC_INIT_STEP(is_uhs1, sdmmc_init_sd_timing_tuning);
/* Sanity check after switching the bus mode and frequency */ /* Sanity check after switching the bus mode and frequency */
SDMMC_INIT_STEP(is_sdmem, sdmmc_check_scr); SDMMC_INIT_STEP(is_sdmem, sdmmc_check_scr);
/* Sanity check after eMMC switch to HS mode */ /* Sanity check after eMMC switch to HS mode */

View File

@@ -69,7 +69,7 @@ esp_err_t sdmmc_init_mmc_read_ext_csd(sdmmc_card_t* card)
} }
/* For MMC cards, use speed value from EXT_CSD */ /* For MMC cards, use speed value from EXT_CSD */
card->csd.tr_speed = card->max_freq_khz * 1000; card->csd.tr_speed = card->max_freq_khz * 1000;
ESP_LOGD(TAG, "MMC card type %d, max_freq_khz=%d, is_ddr=%d", card_type, card->max_freq_khz, card->is_ddr); ESP_LOGD(TAG, "MMC card type %d, max_freq_khz=%"PRId32", is_ddr=%d", card_type, card->max_freq_khz, card->is_ddr);
card->max_freq_khz = MIN(card->max_freq_khz, card->host.max_freq_khz); card->max_freq_khz = MIN(card->max_freq_khz, card->host.max_freq_khz);
if (card->host.flags & SDMMC_HOST_FLAG_8BIT) { if (card->host.flags & SDMMC_HOST_FLAG_8BIT) {

View File

@@ -16,6 +16,7 @@
*/ */
#include <inttypes.h> #include <inttypes.h>
#include "esp_check.h"
#include "esp_timer.h" #include "esp_timer.h"
#include "esp_cache.h" #include "esp_cache.h"
#include "sdmmc_common.h" #include "sdmmc_common.h"
@@ -228,7 +229,7 @@ esp_err_t sdmmc_send_cmd_switch_func(sdmmc_card_t* card,
return ESP_OK; return ESP_OK;
} }
esp_err_t sdmmc_enable_hs_mode(sdmmc_card_t* card) esp_err_t sdmmc_enter_higher_speed_mode(sdmmc_card_t* card)
{ {
/* This will determine if the card supports SWITCH_FUNC command, /* This will determine if the card supports SWITCH_FUNC command,
* and high speed mode. If the cards supports both, this will enable * and high speed mode. If the cards supports both, this will enable
@@ -255,14 +256,62 @@ esp_err_t sdmmc_enable_hs_mode(sdmmc_card_t* card)
goto out; goto out;
} }
uint32_t supported_mask = SD_SFUNC_SUPPORTED(response->data, 1); uint32_t supported_mask = SD_SFUNC_SUPPORTED(response->data, 1);
if ((supported_mask & BIT(SD_ACCESS_MODE_SDR25)) == 0) { ESP_LOGV(TAG, "%s: access mode supported_mask: 0x%"PRIx32, __func__, supported_mask);
err = ESP_ERR_NOT_SUPPORTED;
goto out; if (((card->host.flags & SDMMC_HOST_FLAG_DDR) != 0) && (card->is_uhs1 == 1)) {
} //UHS-I DDR50
err = sdmmc_send_cmd_switch_func(card, 1, SD_ACCESS_MODE, SD_ACCESS_MODE_SDR25, response); ESP_LOGV(TAG, "%s: to switch to DDR50", __func__);
if (err != ESP_OK) { if ((supported_mask & BIT(SD_ACCESS_MODE_DDR50)) == 0) {
ESP_LOGD(TAG, "%s: sdmmc_send_cmd_switch_func (2) returned 0x%x", __func__, err); err = ESP_ERR_NOT_SUPPORTED;
goto out; goto out;
}
err = sdmmc_send_cmd_switch_func(card, 1, SD_ACCESS_MODE, SD_ACCESS_MODE_DDR50, response);
if (err != ESP_OK) {
ESP_LOGD(TAG, "%s: sdmmc_send_cmd_switch_func (2) returned 0x%x", __func__, err);
goto out;
}
card->is_ddr = 1;
err = (*card->host.set_bus_ddr_mode)(card->host.slot, true);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: failed to switch bus to DDR mode (0x%x)", __func__, err);
return err;
}
} else if (card->host.max_freq_khz == SDMMC_FREQ_SDR104) {
//UHS-I SDR104
ESP_LOGV(TAG, "%s: to switch to SDR104", __func__);
if ((supported_mask & BIT(SD_ACCESS_MODE_SDR104)) == 0) {
err = ESP_ERR_NOT_SUPPORTED;
goto out;
}
err = sdmmc_send_cmd_switch_func(card, 1, SD_ACCESS_MODE, SD_ACCESS_MODE_SDR104, response);
if (err != ESP_OK) {
ESP_LOGD(TAG, "%s: sdmmc_send_cmd_switch_func (2) returned 0x%x", __func__, err);
goto out;
}
} else if (card->host.max_freq_khz == SDMMC_FREQ_SDR50) {
//UHS-I SDR50
ESP_LOGV(TAG, "%s: to switch to SDR50", __func__);
if ((supported_mask & BIT(SD_ACCESS_MODE_SDR50)) == 0) {
err = ESP_ERR_NOT_SUPPORTED;
goto out;
}
err = sdmmc_send_cmd_switch_func(card, 1, SD_ACCESS_MODE, SD_ACCESS_MODE_SDR50, response);
if (err != ESP_OK) {
ESP_LOGD(TAG, "%s: sdmmc_send_cmd_switch_func (2) returned 0x%x", __func__, err);
goto out;
}
} else {
ESP_LOGV(TAG, "%s: to switch to SDR25", __func__);
if ((supported_mask & BIT(SD_ACCESS_MODE_SDR25)) == 0) {
err = ESP_ERR_NOT_SUPPORTED;
goto out;
}
err = sdmmc_send_cmd_switch_func(card, 1, SD_ACCESS_MODE, SD_ACCESS_MODE_SDR25, response);
if (err != ESP_OK) {
ESP_LOGD(TAG, "%s: sdmmc_send_cmd_switch_func (2) returned 0x%x", __func__, err);
goto out;
}
} }
out: out:
@@ -270,6 +319,172 @@ out:
return err; return err;
} }
static const uint8_t s_tuning_block_pattern[] = {
0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc,
0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
};
/**
* Find consecutive successful sampling points.
* e.g. array: {1, 1, 0, 0, 1, 1, 1, 0}
* out_length: 3
* outout_end_index: 6
*/
static void find_max_consecutive_success_points(int *array, size_t size, size_t *out_length, uint32_t *out_end_index)
{
uint32_t max = 0;
uint32_t match_num = 0;
uint32_t i = 0;
uint32_t end = 0;
while (i < size) {
if (array[i] == 1) {
match_num++;
} else {
if (match_num > max) {
max = match_num;
end = i - 1;
}
match_num = 0;
}
i++;
}
*out_length = match_num > max ? match_num : max;
*out_end_index = match_num == size ? size : end;
}
static esp_err_t read_tuning_block(sdmmc_card_t *card)
{
esp_err_t ret = ESP_FAIL;
size_t tuning_block_size = sizeof(s_tuning_block_pattern);
ESP_LOGV(TAG, "tuning_block_size: %zu", tuning_block_size);
uint8_t *databuf = NULL;
databuf = heap_caps_calloc(1, tuning_block_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
ESP_RETURN_ON_FALSE(databuf, ESP_ERR_NO_MEM, TAG, "no mem for tuning block databuf");
sdmmc_command_t cmd = {
.opcode = MMC_SEND_TUNING_BLOCK,
.flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1,
.blklen = tuning_block_size,
.data = (void *) databuf,
.datalen = 1 * tuning_block_size,
.buflen = tuning_block_size,
};
ret = sdmmc_send_cmd(card, &cmd);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, ret);
return ret;
}
uint32_t status = 0;
size_t count = 0;
int64_t yield_delay_us = 100 * 1000; // initially 100ms
int64_t t0 = esp_timer_get_time();
int64_t t1 = 0;
while (!host_is_spi(card) && !(status & MMC_R1_READY_FOR_DATA)) {
t1 = esp_timer_get_time();
if (t1 - t0 > SDMMC_READY_FOR_DATA_TIMEOUT_US) {
ESP_LOGW(TAG, "read sectors dma - timeout");
return ESP_ERR_TIMEOUT;
}
if (t1 - t0 > yield_delay_us) {
yield_delay_us *= 2;
vTaskDelay(1);
}
ret = sdmmc_send_cmd_send_status(card, &status);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "%s: sdmmc_send_cmd_send_status returned 0x%x", __func__, ret);
return ret;
}
if (++count % 16 == 0) {
ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
}
}
bool success = false;
if (memcmp(s_tuning_block_pattern, databuf, tuning_block_size) == 0) {
success = true;
}
return success ? ESP_OK : ESP_FAIL;
}
esp_err_t sdmmc_do_timing_tuning(sdmmc_card_t *card)
{
esp_err_t ret = ESP_FAIL;
ESP_RETURN_ON_FALSE(!host_is_spi(card), ESP_ERR_NOT_SUPPORTED, TAG, "sdspi not supported timing tuning");
ESP_RETURN_ON_FALSE(card->host.set_input_delay, ESP_ERR_NOT_SUPPORTED, TAG, "input phase delay feature isn't supported");
int results[SDMMC_DELAY_PHASE_AUTO] = {};
int slot = card->host.slot;
for (int i = SDMMC_DELAY_PHASE_0; i < SDMMC_DELAY_PHASE_AUTO; i++) {
ESP_RETURN_ON_ERROR((*card->host.set_input_delay)(slot, i), TAG, "failed to set input delay");
ret = read_tuning_block(card);
if (ret == ESP_OK) {
results[i] += 1;
}
}
for (int i = 0; i < 4; i++) {
ESP_LOGV(TAG, "results[%d]: %d", i, results[i]);
}
size_t consecutive_len = 0;
uint32_t end = 0;
find_max_consecutive_success_points(results, SDMMC_DELAY_PHASE_AUTO, &consecutive_len, &end);
sdmmc_delay_phase_t proper_delay_phase = SDMMC_DELAY_PHASE_AUTO;
if (consecutive_len == 1) {
proper_delay_phase = end;
} else if (consecutive_len <= SDMMC_DELAY_PHASE_AUTO) {
proper_delay_phase = end / 2;
} else {
assert(false && "exceeds max tuning point");
}
ESP_LOGV(TAG, "%s: proper_delay_phase: %d\n", __func__, proper_delay_phase);
if (proper_delay_phase != SDMMC_DELAY_PHASE_AUTO) {
ESP_RETURN_ON_ERROR((*card->host.set_input_delay)(slot, proper_delay_phase), TAG, "failed to set input delay");
}
return ESP_OK;
}
esp_err_t sdmmc_select_driver_strength(sdmmc_card_t *card, sdmmc_driver_strength_t driver_strength)
{
if (card->scr.sd_spec < SCR_SD_SPEC_VER_1_10 ||
((card->csd.card_command_class & SD_CSD_CCC_SWITCH) == 0)) {
return ESP_ERR_NOT_SUPPORTED;
}
esp_err_t ret = ESP_FAIL;
sdmmc_switch_func_rsp_t *response = NULL;
response = heap_caps_calloc(1, sizeof(*response), MALLOC_CAP_DMA);
ESP_RETURN_ON_FALSE(response, ESP_ERR_NO_MEM, TAG, "no mem for response buf");
ret = sdmmc_send_cmd_switch_func(card, 1, SD_DRIVER_STRENGTH, driver_strength, response);
ESP_GOTO_ON_ERROR(ret, out, TAG, "%s: sdmmc_send_cmd_switch_func (1) returned 0x%x", __func__, ret);
uint32_t supported_mask = SD_SFUNC_SELECTED(response->data, SD_DRIVER_STRENGTH);
ESP_GOTO_ON_FALSE(supported_mask != 0xf, ESP_ERR_NOT_SUPPORTED, out, TAG, "switch group1 result fail");
ESP_LOGV(TAG, "driver strength: supported_mask: 0x%"PRIx32, supported_mask);
ESP_GOTO_ON_FALSE(supported_mask == driver_strength, ESP_ERR_INVALID_ARG, out, TAG, "fail to switch to type 0x%x", driver_strength);
out:
free(response);
return ret;
}
esp_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card) esp_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card)
{ {
/* All cards should support at least default speed */ /* All cards should support at least default speed */
@@ -281,10 +496,11 @@ esp_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card)
} }
/* Try to enabled HS mode */ /* Try to enabled HS mode */
esp_err_t err = sdmmc_enable_hs_mode(card); esp_err_t err = sdmmc_enter_higher_speed_mode(card);
if (err != ESP_OK) { if (err != ESP_OK) {
return err; return err;
} }
/* HS mode has been enabled on the card. /* HS mode has been enabled on the card.
* Read CSD again, it should now indicate that the card supports * Read CSD again, it should now indicate that the card supports
* 50MHz clock. * 50MHz clock.
@@ -313,13 +529,61 @@ esp_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card)
} }
} }
if (card->csd.tr_speed != 50000000) { ESP_LOGD(TAG, "%s: after enabling HS mode, tr_speed=%d", __func__, card->csd.tr_speed);
ESP_LOGW(TAG, "unexpected: after enabling HS mode, tr_speed=%d", card->csd.tr_speed); card->max_freq_khz = MIN(card->host.max_freq_khz, SDMMC_FREQ_SDR104);
return ESP_ERR_NOT_SUPPORTED;
return ESP_OK;
}
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);
return sd_pwr_ctrl_set_io_voltage(card->host.pwr_ctrl_handle, voltage_mv);
}
esp_err_t sdmmc_init_sd_uhs1(sdmmc_card_t* card)
{
sdmmc_command_t cmd = {
.opcode = SD_SWITCH_VOLTAGE,
.arg = 0,
.flags = SCF_CMD_AC | SCF_RSP_R1,
.volt_switch_cb = &sdmmc_init_sd_uhs1_volt_sw_cb,
.volt_switch_cb_arg = card
};
esp_err_t err = sdmmc_send_cmd(card, &cmd);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_cmd returned 0x%x", __func__, err);
} }
card->max_freq_khz = MIN(card->host.max_freq_khz, SDMMC_FREQ_HIGHSPEED); card->is_uhs1 = 1;
return ESP_OK;
return err;
}
esp_err_t sdmmc_select_current_limit(sdmmc_card_t *card, sdmmc_current_limit_t current_limit)
{
if (card->scr.sd_spec < SCR_SD_SPEC_VER_1_10 ||
((card->csd.card_command_class & SD_CSD_CCC_SWITCH) == 0)) {
return ESP_ERR_NOT_SUPPORTED;
}
esp_err_t ret = ESP_FAIL;
sdmmc_switch_func_rsp_t *response = NULL;
response = heap_caps_calloc(1, sizeof(*response), MALLOC_CAP_DMA);
ESP_RETURN_ON_FALSE(response, ESP_ERR_NO_MEM, TAG, "no mem for response buf");
ret = sdmmc_send_cmd_switch_func(card, 1, SD_CURRENT_LIMIT, current_limit, response);
ESP_GOTO_ON_ERROR(ret, out, TAG, "%s: sdmmc_send_cmd_switch_func (1) returned 0x%x", __func__, ret);
uint32_t supported_mask = SD_SFUNC_SELECTED(response->data, SD_CURRENT_LIMIT);
ESP_GOTO_ON_FALSE(supported_mask != 0xf, ESP_ERR_NOT_SUPPORTED, out, TAG, "switch group4 result fail");
ESP_LOGV(TAG, "current limit: supported_mask: 0x%"PRIx32, supported_mask);
ESP_GOTO_ON_FALSE(supported_mask == current_limit, ESP_ERR_INVALID_ARG, out, TAG, "fail to switch to type 0x%x", current_limit);
out:
free(response);
return ret;
} }
esp_err_t sdmmc_check_scr(sdmmc_card_t* card) esp_err_t sdmmc_check_scr(sdmmc_card_t* card)
@@ -390,11 +654,22 @@ esp_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd)
out_csd->capacity *= read_bl_size / out_csd->sector_size; out_csd->capacity *= read_bl_size / out_csd->sector_size;
} }
int speed = SD_CSD_SPEED(response); int speed = SD_CSD_SPEED(response);
if (speed == SD_CSD_SPEED_50_MHZ) { ESP_LOGV(TAG, "%s: speed: 0x%x", __func__, speed);
switch (speed) {
case SD_CSD_SPEED_50_MHZ:
out_csd->tr_speed = 50000000; out_csd->tr_speed = 50000000;
} else { break;
case SD_CSD_SPEED_100_MHZ:
out_csd->tr_speed = 100000000;
break;
case SD_CSD_SPEED_200_MHZ:
out_csd->tr_speed = 200000000;
break;
default:
out_csd->tr_speed = 25000000; out_csd->tr_speed = 25000000;
break;
} }
return ESP_OK; return ESP_OK;
} }

View File

@@ -67,6 +67,7 @@
#define SDMMC_INTMASK_HLE BIT(12) #define SDMMC_INTMASK_HLE BIT(12)
#define SDMMC_INTMASK_FRUN BIT(11) #define SDMMC_INTMASK_FRUN BIT(11)
#define SDMMC_INTMASK_HTO BIT(10) #define SDMMC_INTMASK_HTO BIT(10)
#define SDMMC_INTMASK_VOLT_SW SDMMC_INTMASK_HTO
#define SDMMC_INTMASK_DTO BIT(9) #define SDMMC_INTMASK_DTO BIT(9)
#define SDMMC_INTMASK_RTO BIT(8) #define SDMMC_INTMASK_RTO BIT(8)
#define SDMMC_INTMASK_DCRC BIT(7) #define SDMMC_INTMASK_DCRC BIT(7)

View File

@@ -1359,6 +1359,10 @@ config SOC_SDMMC_PSRAM_DMA_CAPABLE
bool bool
default y default y
config SOC_SDMMC_UHS_I_SUPPORTED
bool
default y
config SOC_SHA_DMA_MAX_BUFFER_SIZE config SOC_SHA_DMA_MAX_BUFFER_SIZE
int int
default 3968 default 3968
@@ -1947,6 +1951,10 @@ config SOC_CLK_MPLL_SUPPORTED
bool bool
default y default y
config SOC_CLK_SDIO_PLL_SUPPORTED
bool
default y
config SOC_CLK_XTAL32K_SUPPORTED config SOC_CLK_XTAL32K_SUPPORTED
bool bool
default y default y

View File

@@ -151,6 +151,7 @@ typedef enum {
SOC_MOD_CLK_CPLL, /*!< CPLL is from 40MHz XTAL oscillator frequency multipliers */ SOC_MOD_CLK_CPLL, /*!< CPLL is from 40MHz XTAL oscillator frequency multipliers */
SOC_MOD_CLK_SPLL, /*!< SPLL is from 40MHz XTAL oscillator frequency multipliers, it has a "fixed" frequency of 480MHz */ SOC_MOD_CLK_SPLL, /*!< SPLL is from 40MHz XTAL oscillator frequency multipliers, it has a "fixed" frequency of 480MHz */
SOC_MOD_CLK_MPLL, /*!< MPLL is from 40MHz XTAL oscillator frequency multipliers */ SOC_MOD_CLK_MPLL, /*!< MPLL is from 40MHz XTAL oscillator frequency multipliers */
SOC_MOD_CLK_SDIO_PLL, /*!< SDIO PLL is from 40MHz XTAL oscillator frequency multipliers, it has a "fixed" frequency of 200MHz */
SOC_MOD_CLK_XTAL32K, /*!< XTAL32K_CLK comes from the external 32kHz crystal, passing a clock gating to the peripherals */ SOC_MOD_CLK_XTAL32K, /*!< XTAL32K_CLK comes from the external 32kHz crystal, passing a clock gating to the peripherals */
SOC_MOD_CLK_RC_FAST, /*!< RC_FAST_CLK comes from the internal 20MHz rc oscillator, passing a clock gating to the peripherals */ SOC_MOD_CLK_RC_FAST, /*!< RC_FAST_CLK comes from the internal 20MHz rc oscillator, passing a clock gating to the peripherals */
SOC_MOD_CLK_XTAL, /*!< XTAL_CLK comes from the external 40MHz crystal */ SOC_MOD_CLK_XTAL, /*!< XTAL_CLK comes from the external 40MHz crystal */
@@ -706,7 +707,7 @@ typedef enum {
/** /**
* @brief Array initializer for all supported clock sources of SDMMC * @brief Array initializer for all supported clock sources of SDMMC
*/ */
#define SOC_SDMMC_CLKS {SOC_MOD_CLK_PLL_F160M} #define SOC_SDMMC_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_SDIO_PLL}
/** /**
* @brief Type of SDMMC clock source * @brief Type of SDMMC clock source
@@ -714,7 +715,7 @@ typedef enum {
typedef enum { typedef enum {
SDMMC_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_160M as the default choice */ SDMMC_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_160M as the default choice */
SDMMC_CLK_SRC_PLL160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_160M as the source clock */ SDMMC_CLK_SRC_PLL160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_160M as the source clock */
// SOC_MOD_CLK_SDIO_PLL TODO:IDF-8886 SDMMC_CLK_SRC_SDIO_200M = SOC_MOD_CLK_SDIO_PLL,
} soc_periph_sdmmc_clk_src_t; } soc_periph_sdmmc_clk_src_t;
//////////////////////////////////////////////////Temp Sensor/////////////////////////////////////////////////////////// //////////////////////////////////////////////////Temp Sensor///////////////////////////////////////////////////////////

View File

@@ -500,6 +500,7 @@
#define SOC_SDMMC_DELAY_PHASE_NUM 4 #define SOC_SDMMC_DELAY_PHASE_NUM 4
#define SOC_SDMMC_IO_POWER_EXTERNAL 1 ///< SDMMC IO power controlled by external power supply #define SOC_SDMMC_IO_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 PSRAM #define SOC_SDMMC_PSRAM_DMA_CAPABLE 1 ///< SDMMC peripheral can do DMA transfer to/from PSRAM
#define SOC_SDMMC_UHS_I_SUPPORTED 1
// TODO: IDF-5353 (Copy from esp32c3, need check) // TODO: IDF-5353 (Copy from esp32c3, need check)
/*--------------------------- SHA CAPS ---------------------------------------*/ /*--------------------------- SHA CAPS ---------------------------------------*/
@@ -733,6 +734,7 @@
#define SOC_CLK_APLL_SUPPORTED (1) /*!< Support Audio PLL */ #define SOC_CLK_APLL_SUPPORTED (1) /*!< Support Audio PLL */
#define SOC_CLK_MPLL_SUPPORTED (1) /*!< Support MSPI PLL */ #define SOC_CLK_MPLL_SUPPORTED (1) /*!< Support MSPI PLL */
#define SOC_CLK_SDIO_PLL_SUPPORTED (1) /*!< Support SDIO PLL */
#define SOC_CLK_XTAL32K_SUPPORTED (1) /*!< Support to connect an external low frequency crystal */ #define SOC_CLK_XTAL32K_SUPPORTED (1) /*!< Support to connect an external low frequency crystal */
#define SOC_CLK_RC32K_SUPPORTED (1) /*!< Support an internal 32kHz RC oscillator */ #define SOC_CLK_RC32K_SUPPORTED (1) /*!< Support an internal 32kHz RC oscillator */

View File

@@ -1,5 +1,5 @@
/** /**
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -1502,6 +1502,7 @@ extern "C" {
#define SDMMC_INTMASK_HLE BIT(12) #define SDMMC_INTMASK_HLE BIT(12)
#define SDMMC_INTMASK_FRUN BIT(11) #define SDMMC_INTMASK_FRUN BIT(11)
#define SDMMC_INTMASK_HTO BIT(10) #define SDMMC_INTMASK_HTO BIT(10)
#define SDMMC_INTMASK_VOLT_SW SDMMC_INTMASK_HTO
#define SDMMC_INTMASK_DTO BIT(9) #define SDMMC_INTMASK_DTO BIT(9)
#define SDMMC_INTMASK_RTO BIT(8) #define SDMMC_INTMASK_RTO BIT(8)
#define SDMMC_INTMASK_DCRC BIT(7) #define SDMMC_INTMASK_DCRC BIT(7)

View File

@@ -1,5 +1,5 @@
/** /**
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -458,7 +458,13 @@ typedef union {
* not masked. * not masked.
*/ */
uint32_t ccs_expected:1; 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_hole_reg : R/W; bitpos: [29]; default: 1;
* Use Hold Register. * Use Hold Register.
* 0: CMD and DATA sent to card bypassing HOLD Register; * 0: CMD and DATA sent to card bypassing HOLD Register;
@@ -907,11 +913,17 @@ typedef union {
*/ */
typedef union { typedef union {
struct { struct {
uint32_t reserved_0:16; /** volt: R/W; bitpos: [1:0]; default: 0;
/** ddr : R/W; bitpos: [17:16]; default: 0; * Voltage mode selection, 1 bit for each card. On the ESP32-P4, this bit doesn't do anything, I/O voltage is controlled using LDO API instead.
* DDR mode selection,1 bit for each card. * 0: 3.3V mode.
* 0-Non-DDR mode. * 1: 1.8V mode.
* 1-DDR 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:2; uint32_t ddr:2;
uint32_t reserved_18:14; uint32_t reserved_18:14;

View File

@@ -66,6 +66,7 @@
#define SDMMC_INTMASK_HLE BIT(12) #define SDMMC_INTMASK_HLE BIT(12)
#define SDMMC_INTMASK_FRUN BIT(11) #define SDMMC_INTMASK_FRUN BIT(11)
#define SDMMC_INTMASK_HTO BIT(10) #define SDMMC_INTMASK_HTO BIT(10)
#define SDMMC_INTMASK_VOLT_SW SDMMC_INTMASK_HTO
#define SDMMC_INTMASK_DTO BIT(9) #define SDMMC_INTMASK_DTO BIT(9)
#define SDMMC_INTMASK_RTO BIT(8) #define SDMMC_INTMASK_RTO BIT(8)
#define SDMMC_INTMASK_DCRC BIT(7) #define SDMMC_INTMASK_DCRC BIT(7)

View File

@@ -83,14 +83,17 @@ Supported Speed Modes
SDMMC Host driver supports the following speed modes: SDMMC Host driver supports the following speed modes:
- Default Speed (20 MHz): 1-line or 4-line with SD cards, and 1-line, 4-line, or 8-line with 3.3 V eMMC .. list::
- High Speed (40 MHz): 1-line or 4-line with SD cards, and 1-line, 4-line, or 8-line with 3.3 V eMMC
- High Speed DDR (40 MHz): 4-line with 3.3 V eMMC - Default Speed (20 MHz): 1-line or 4-line with SD cards, and 1-line, 4-line, or 8-line with 3.3 V eMMC
- High Speed (40 MHz): 1-line or 4-line with SD cards, and 1-line, 4-line, or 8-line with 3.3 V eMMC
:SOC_SDMMC_UHS_I_SUPPORTED: - UHS-I 1.8 V, SDR50 (100 MHz): 4-line with SD cards
:SOC_SDMMC_UHS_I_SUPPORTED: - UHS-I 1.8 V, DDR50 (50 MHz): 4-line with SD cards
- High Speed DDR (40 MHz): 4-line with 3.3 V eMMC
Speed modes not supported at present: Speed modes not supported at present:
- High Speed DDR mode: 8-line eMMC - High Speed DDR mode: 8-line eMMC
- UHS-I 1.8 V modes: 4-line SD cards
Using the SDMMC Host Driver Using the SDMMC Host Driver

View File

@@ -83,14 +83,17 @@ SDMMC 主机驱动
SDMMC 主机驱动支持以下速率模式: SDMMC 主机驱动支持以下速率模式:
- 默认速率 (20 MHz):对于 SD 卡,支持 1 线或 4 线传输;对于 3.3 V eMMC支持 1 线、4 线或 8 线传输。 .. list::
- 高速模式 (40 MHz):对于 SD 卡,支持 1 线或 4 线传输;对于 3.3 V eMMC支持 1 线、4 线或 8 线传输。
- 高速 DDR 模式 (40 MHz):对于 3.3 V eMMC支持 4 线传输。 - 默认速率 (20 MHz):对于 SD 卡,支持 1 线或 4 线传输;对于 3.3 V eMMC支持 1 线、4 线或 8 线传输。
- 高速模式 (40 MHz):对于 SD 卡,支持 1 线或 4 线传输;对于 3.3 V eMMC支持 1 线、4 线或 8 线传输。
:SOC_SDMMC_UHS_I_SUPPORTED: - UHS-I 1.8 V SDR50 模式 (100 MHz):支持 4 线 SD 卡传输。
:SOC_SDMMC_UHS_I_SUPPORTED: - UHS-I 1.8 V DDR50 模式 (50 MHz):支持 4 线 SD 卡传输。
- 高速 DDR 模式 (40 MHz):对于 3.3 V eMMC支持 4 线传输。
当前尚不支持的速率模式: 当前尚不支持的速率模式:
- 高速 DDR 模式:不支持 8 线 eMMC 传输 - 高速 DDR 模式:不支持 8 线 eMMC 传输
- UHS-I 1.8 V 模式:不支持 4 线 SD 卡传输
使用 SDMMC 主机驱动 使用 SDMMC 主机驱动

View File

@@ -168,7 +168,7 @@ sdmmc_card_t * mount_sdcard(void)
ESP_LOGI(TAG, "Card size: %lluMB, speed: %dMHz", ESP_LOGI(TAG, "Card size: %lluMB, speed: %dMHz",
(((uint64_t)sdmmc_card->csd.capacity) * sdmmc_card->csd.sector_size) >> 20, (((uint64_t)sdmmc_card->csd.capacity) * sdmmc_card->csd.sector_size) >> 20,
sdmmc_card->max_freq_khz / 1000); (int)(sdmmc_card->max_freq_khz / 1000));
return sdmmc_card; return sdmmc_card;
} }
@@ -234,7 +234,7 @@ static esp_err_t record_wav(i2s_chan_handle_t i2s_rx_chan)
ESP_GOTO_ON_ERROR(i2s_channel_enable(i2s_rx_chan), err, TAG, "error while starting i2s rx channel"); ESP_GOTO_ON_ERROR(i2s_channel_enable(i2s_rx_chan), err, TAG, "error while starting i2s rx channel");
while (wav_written < wav_size) { while (wav_written < wav_size) {
if (wav_written % byte_rate < sizeof(i2s_readraw_buff)) { if (wav_written % byte_rate < sizeof(i2s_readraw_buff)) {
ESP_LOGI(TAG, "Recording: %"PRIu32"/%ds", wav_written / byte_rate + 1, EXAMPLE_RECORD_TIME_SEC); ESP_LOGI(TAG, "Recording: %"PRIu32"/%ds", wav_written / byte_rate + 1, (int)EXAMPLE_RECORD_TIME_SEC);
} }
size_t bytes_read = 0; size_t bytes_read = 0;
/* Read RAW samples from ES7210 */ /* Read RAW samples from ES7210 */

View File

@@ -28,6 +28,22 @@ menu "SD/MMC Example Configuration"
bool "1 line (D0)" bool "1 line (D0)"
endchoice endchoice
choice EXAMPLE_SDMMC_SPEED_MODE
prompt "SD/MMC speed mode"
default EXAMPLE_SDMMC_SPEED_DS
config EXAMPLE_SDMMC_SPEED_DS
bool "Default Speed"
config EXAMPLE_SDMMC_SPEED_HS
bool "High Speed"
config EXAMPLE_SDMMC_SPEED_UHS_I_SDR50
bool "UHS-I SDR50 (100 MHz, 50 MB/s)"
depends on SOC_SDMMC_UHS_I_SUPPORTED
config EXAMPLE_SDMMC_SPEED_UHS_I_DDR50
bool "UHS-I DDR50 (50 MHz, 50 MB/s)"
depends on SOC_SDMMC_UHS_I_SUPPORTED
endchoice
if SOC_SDMMC_USE_GPIO_MATRIX if SOC_SDMMC_USE_GPIO_MATRIX
config EXAMPLE_PIN_CMD config EXAMPLE_PIN_CMD

View File

@@ -24,6 +24,7 @@
static const char *TAG = "example"; static const char *TAG = "example";
#define MOUNT_POINT "/sdcard" #define MOUNT_POINT "/sdcard"
#define EXAMPLE_IS_UHS1 (CONFIG_EXAMPLE_SDMMC_SPEED_UHS_I_SDR50 || CONFIG_EXAMPLE_SDMMC_SPEED_UHS_I_DDR50)
#ifdef CONFIG_EXAMPLE_DEBUG_PIN_CONNECTIONS #ifdef CONFIG_EXAMPLE_DEBUG_PIN_CONNECTIONS
const char* names[] = {"CLK", "CMD", "D0", "D1", "D2", "D3"}; const char* names[] = {"CLK", "CMD", "D0", "D1", "D2", "D3"};
@@ -128,6 +129,16 @@ void app_main(void)
// For setting a specific frequency, use host.max_freq_khz (range 400kHz - 40MHz for SDMMC) // For setting a specific frequency, use host.max_freq_khz (range 400kHz - 40MHz for SDMMC)
// Example: for fixed frequency of 10MHz, use host.max_freq_khz = 10000; // Example: for fixed frequency of 10MHz, use host.max_freq_khz = 10000;
sdmmc_host_t host = SDMMC_HOST_DEFAULT(); sdmmc_host_t host = SDMMC_HOST_DEFAULT();
#if CONFIG_EXAMPLE_SDMMC_SPEED_HS
host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
#elif CONFIG_EXAMPLE_SDMMC_SPEED_UHS_I_SDR50
host.slot = SDMMC_HOST_SLOT_0;
host.max_freq_khz = SDMMC_FREQ_SDR50;
host.flags &= ~SDMMC_HOST_FLAG_DDR;
#elif CONFIG_EXAMPLE_SDMMC_SPEED_UHS_I_DDR50
host.slot = SDMMC_HOST_SLOT_0;
host.max_freq_khz = SDMMC_FREQ_DDR50;
#endif
// For SoCs where the SD power can be supplied both via an internal or external (e.g. on-board LDO) power supply. // 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 // When using specific IO pins (which can be used for ultra high-speed SDMMC) to connect to the SD card
@@ -149,6 +160,9 @@ void app_main(void)
// This initializes the slot without card detect (CD) and write protect (WP) signals. // This initializes the slot without card detect (CD) and write protect (WP) signals.
// Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals. // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
#if EXAMPLE_IS_UHS1
slot_config.flags |= SDMMC_SLOT_FLAG_UHS1;
#endif
// Set bus width to use: // Set bus width to use:
#ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4 #ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4