From 04cd469e4a75605b19a27f1a7345e910f61a6997 Mon Sep 17 00:00:00 2001 From: Armando Date: Wed, 6 Nov 2024 12:09:25 +0800 Subject: [PATCH] feat(sdmmc): supported UHS-I SDR50 (100Mhz) and DDR50 mode --- .../include/driver/sdmmc_default_configs.h | 24 +- .../include/driver/sdmmc_host.h | 16 +- components/esp_driver_sdmmc/src/sdmmc_host.c | 202 +++++++----- .../src/{sdmmc_private.h => sdmmc_internal.h} | 2 +- .../esp_driver_sdmmc/src/sdmmc_transaction.c | 7 +- .../sdmmc_test_boards/sdmmc_test_board.c | 2 +- .../sdmmc_tests/sdmmc_test_begin_end_sd.c | 4 +- .../include/driver/sdspi_host.h | 3 + .../port/esp32p4/esp_clk_tree.c | 5 +- components/hal/esp32/include/hal/sdmmc_ll.h | 12 + .../hal/esp32p4/include/hal/clk_tree_ll.h | 1 + components/hal/esp32p4/include/hal/sdmmc_ll.h | 69 ++++- components/hal/esp32s3/include/hal/sdmmc_ll.h | 12 + components/sdmmc/include/sd_protocol_defs.h | 11 + components/sdmmc/include/sd_protocol_types.h | 38 ++- components/sdmmc/sdmmc_common.c | 42 ++- components/sdmmc/sdmmc_common.h | 12 +- components/sdmmc/sdmmc_init.c | 10 + components/sdmmc/sdmmc_mmc.c | 2 +- components/sdmmc/sdmmc_sd.c | 288 ++++++++++++++++-- components/soc/esp32/register/soc/sdmmc_reg.h | 1 + .../esp32p4/include/soc/Kconfig.soc_caps.in | 8 + .../soc/esp32p4/include/soc/clk_tree_defs.h | 5 +- components/soc/esp32p4/include/soc/soc_caps.h | 2 + .../soc/esp32p4/register/soc/sdmmc_struct.h | 2 +- .../soc/esp32s3/register/soc/sdmmc_reg.h | 1 + 26 files changed, 650 insertions(+), 131 deletions(-) rename components/esp_driver_sdmmc/src/{sdmmc_private.h => sdmmc_internal.h} (94%) diff --git a/components/esp_driver_sdmmc/include/driver/sdmmc_default_configs.h b/components/esp_driver_sdmmc/include/driver/sdmmc_default_configs.h index 7b72fdb201..5b0bc07f0f 100644 --- a/components/esp_driver_sdmmc/include/driver/sdmmc_default_configs.h +++ b/components/esp_driver_sdmmc/include/driver/sdmmc_default_configs.h @@ -31,6 +31,8 @@ extern "C" { .slot = SDMMC_HOST_SLOT_1, \ .max_freq_khz = SDMMC_FREQ_DEFAULT, \ .io_voltage = 3.3f, \ + .driver_strength = SDMMC_DRIVER_STRENGTH_B, \ + .current_limit = SDMMC_CURRENT_LIMIT_200MA, \ .init = &sdmmc_host_init, \ .set_bus_width = &sdmmc_host_set_bus_width, \ .get_bus_width = &sdmmc_host_get_slot_width, \ @@ -48,29 +50,35 @@ extern "C" { .dma_aligned_buffer = NULL, \ .pwr_ctrl_handle = NULL, \ .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_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 -#if SOC_SDMMC_USE_IOMUX && !SOC_SDMMC_USE_GPIO_MATRIX /** * Macro defining default configuration of SDMMC host slot */ +#if CONFIG_IDF_TARGET_ESP32 #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, \ .wp = SDMMC_SLOT_NO_WP, \ .width = SDMMC_SLOT_WIDTH_DEFAULT, \ .flags = 0, \ } -#else - -/** - * Macro defining default configuration of SDMMC host slot - */ -#if CONFIG_IDF_TARGET_ESP32P4 +#elif CONFIG_IDF_TARGET_ESP32P4 #define SDMMC_SLOT_CONFIG_DEFAULT() {\ .clk = GPIO_NUM_43, \ .cmd = GPIO_NUM_44, \ @@ -107,8 +115,6 @@ extern "C" { } #endif // GPIO Matrix chips -#endif - #ifdef __cplusplus } #endif diff --git a/components/esp_driver_sdmmc/include/driver/sdmmc_host.h b/components/esp_driver_sdmmc/include/driver/sdmmc_host.h index 9457eaf5c1..037dbe83cf 100644 --- a/components/esp_driver_sdmmc/include/driver/sdmmc_host.h +++ b/components/esp_driver_sdmmc/include/driver/sdmmc_host.h @@ -24,7 +24,6 @@ extern "C" { * Extra configuration for SDMMC peripheral slot */ typedef struct { -#ifdef SOC_SDMMC_USE_GPIO_MATRIX gpio_num_t clk; ///< GPIO number of CLK signal. gpio_num_t cmd; ///< GPIO number of CMD 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 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. -#endif // SOC_SDMMC_USE_GPIO_MATRIX union { gpio_num_t gpio_cd; ///< GPIO number of card detect signal 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; * 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; /** @@ -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); +/** + * @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 * diff --git a/components/esp_driver_sdmmc/src/sdmmc_host.c b/components/esp_driver_sdmmc/src/sdmmc_host.c index 7a77b55eb8..fad7045ece 100644 --- a/components/esp_driver_sdmmc/src/sdmmc_host.c +++ b/components/esp_driver_sdmmc/src/sdmmc_host.c @@ -20,7 +20,7 @@ #include "driver/sdmmc_host.h" #include "esp_private/esp_clk_tree_common.h" #include "esp_private/periph_ctrl.h" -#include "sdmmc_private.h" +#include "sdmmc_internal.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "esp_clk_tree.h" @@ -33,6 +33,8 @@ #define SDMMC_EVENT_QUEUE_LENGTH 32 +#define SDMMC_FREQ_SDR104 208000 /*!< MMC 208MHz speed */ + #if !SOC_RCC_IS_INDEPENDENT // 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() @@ -65,9 +67,11 @@ if (!GPIO_IS_VALID_GPIO(_gpio_num)) { \ * Slot contexts */ typedef struct slot_ctx_t { + int slot_id; size_t slot_width; sdmmc_slot_io_info_t slot_gpio_num; bool use_gpio_matrix; + bool is_uhs1; #if SOC_SDMMC_NUM_SLOTS >= 2 int slot_host_div; uint32_t slot_freq_khz; @@ -79,14 +83,15 @@ typedef struct slot_ctx_t { * Host contexts */ typedef struct host_ctx_t { - intr_handle_t intr_handle; - QueueHandle_t event_queue; - SemaphoreHandle_t io_intr_event; - sdmmc_hal_context_t hal; - slot_ctx_t slot_ctx[SOC_SDMMC_NUM_SLOTS]; + intr_handle_t intr_handle; + QueueHandle_t event_queue; + SemaphoreHandle_t io_intr_event; + sdmmc_hal_context_t hal; + soc_periph_sdmmc_clk_src_t clk_src; + slot_ctx_t slot_ctx[SOC_SDMMC_NUM_SLOTS]; #if SOC_SDMMC_NUM_SLOTS >= 2 - uint8_t num_of_init_slots; - int8_t active_slot_num; + uint8_t num_of_init_slots; + int8_t active_slot_num; #endif } host_ctx_t; @@ -163,13 +168,18 @@ esp_err_t sdmmc_host_reset(void) * Of the second stage dividers, div0 is used for card 0, and div1 is used * 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_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); +#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 @@ -192,11 +202,23 @@ static esp_err_t sdmmc_host_clock_update_command(int slot, bool is_cmd11) 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; - esp_clk_tree_src_get_freq_hz(SDMMC_CLK_SRC_DEFAULT, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz); - assert(clk_src_freq_hz == (160 * 1000 * 1000)); + soc_periph_sdmmc_clk_src_t clk_src = 0; +#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 (freq_khz >= SDMMC_LL_MAX_FREQ_KHZ_FPGA) { @@ -204,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; } #endif + // Calculate new dividers - if (freq_khz >= SDMMC_FREQ_HIGHSPEED) { - *host_div = 4; // 160 MHz / 4 = 40 MHz +#if SOC_SDMMC_UHS_I_SUPPORTED + if (freq_khz == SDMMC_FREQ_SDR104) { + *host_div = 1; // 200 MHz / 1 = 200 MHz *card_div = 0; - } else if (freq_khz == SDMMC_FREQ_DEFAULT) { - *host_div = 8; // 160 MHz / 8 = 20 MHz + } else if (freq_khz == SDMMC_FREQ_SDR50) { + *host_div = 2; // 200 MHz / 2 = 100 MHz *card_div = 0; - } else if (freq_khz == SDMMC_FREQ_PROBING) { - *host_div = 10; // 160 MHz / 10 / (20 * 2) = 400 kHz - *card_div = 20; - } else { - /* - * for custom frequencies use maximum range of host divider (1-16), find the closest <= div. combination - * if exceeded, combine with the card divider to keep reasonable precision (applies mainly to low frequencies) - * effective frequency range: 400 kHz - 32 MHz (32.1 - 39.9 MHz cannot be covered with given divider scheme) - */ - *host_div = (clk_src_freq_hz) / (freq_khz * 1000); - if (*host_div > 15) { - *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 +#endif + if (freq_khz >= SDMMC_FREQ_HIGHSPEED) { + *host_div = 4; // 160 MHz / 4 = 40 MHz + *card_div = 0; + } else if (freq_khz == SDMMC_FREQ_DEFAULT) { + *host_div = 8; // 160 MHz / 8 = 20 MHz + *card_div = 0; + } else if (freq_khz == SDMMC_FREQ_PROBING) { + *host_div = 10; // 160 MHz / 10 / (20 * 2) = 400 kHz + *card_div = 20; + } else { + /* + * for custom frequencies use maximum range of host divider (1-16), find the closest <= div. combination + * if exceeded, combine with the card divider to keep reasonable precision (applies mainly to low frequencies) + * effective frequency range: 400 kHz - 32 MHz (32.1 - 39.9 MHz cannot be covered with given divider scheme) + */ + *host_div = (clk_src_freq_hz) / (freq_khz * 1000); + if (*host_div > 15) { + *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; - esp_clk_tree_src_get_freq_hz(SDMMC_CLK_SRC_DEFAULT, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz); - assert(clk_src_freq_hz == (160 * 1000 * 1000)); + esp_err_t ret = esp_clk_tree_src_get_freq_hz(src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz); + assert(ret == ESP_OK); + return clk_src_freq_hz / host_div / ((card_div == 0) ? 1 : card_div * 2) / 1000; } @@ -261,16 +296,17 @@ esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz) return err; } + soc_periph_sdmmc_clk_src_t clk_src = 0; int host_div = 0; /* clock divider of the host (SDMMC.clock) */ 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); - 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); + int real_freq = sdmmc_host_calc_freq(clk_src, host_div, card_div); + 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 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, false); if (err != ESP_OK) { ESP_LOGE(TAG, "setting clk div failed"); @@ -310,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 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; } @@ -326,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"); 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"); //Now we're in high speed. Note ESP SDMMC Host HW only supports integer divider. @@ -459,7 +495,7 @@ esp_err_t sdmmc_host_init(void) sdmmc_hal_init(&s_host_ctx.hal); // 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 esp_err_t err = sdmmc_host_reset(); @@ -567,6 +603,18 @@ static bool s_check_pin_not_set(const sdmmc_slot_config_t *slot_config) #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) { if (!s_host_ctx.intr_handle) { @@ -578,6 +626,11 @@ esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t *slot_config) if (slot_config == NULL) { 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_wp = slot_config->wp; bool gpio_wp_polarity = slot_config->flags & SDMMC_SLOT_FLAG_WP_ACTIVE_HIGH; @@ -603,12 +656,12 @@ esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t *slot_config) if (slot == 0) { #if !SDMMC_LL_SLOT_SUPPORT_GPIO_MATRIX(0) 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) { + 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"); @@ -685,20 +738,23 @@ esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t *slot_config) 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->d2, sdmmc_slot_gpio_sig[slot].d2, GPIO_MODE_INPUT_OUTPUT, "d2", use_gpio_matrix); - configure_pin(slot_gpio->d3, sdmmc_slot_gpio_sig[slot].d3, GPIO_MODE_INPUT_OUTPUT, "d3", use_gpio_matrix); - // // Force D3 high to make slave enter SD mode. - // // Connect to peripheral after width configuration. - // if (slot_gpio->d3 > GPIO_NUM_NC) { - // gpio_config_t gpio_conf = { - // .pin_bit_mask = BIT64(slot_gpio->d3), - // .mode = GPIO_MODE_OUTPUT, - // .pull_up_en = 0, - // .pull_down_en = 0, - // .intr_type = GPIO_INTR_DISABLE, - // }; - // gpio_config(&gpio_conf); - // gpio_set_level(slot_gpio->d3, 1); - // } + if (s_host_ctx.slot_ctx[slot].is_uhs1) { + configure_pin(slot_gpio->d3, sdmmc_slot_gpio_sig[slot].d3, GPIO_MODE_INPUT_OUTPUT, "d3", use_gpio_matrix); + } else { + // Force D3 high to make slave enter SD mode. + // Connect to peripheral after width configuration. + if (slot_gpio->d3 > GPIO_NUM_NC) { + gpio_config_t gpio_conf = { + .pin_bit_mask = BIT64(slot_gpio->d3), + .mode = GPIO_MODE_OUTPUT, + .pull_up_en = 0, + .pull_down_en = 0, + .intr_type = GPIO_INTR_DISABLE, + }; + gpio_config(&gpio_conf); + gpio_set_level(slot_gpio->d3, 1); + } + } } if (slot_width == 8) { configure_pin(slot_gpio->d4, sdmmc_slot_gpio_sig[slot].d4, GPIO_MODE_INPUT_OUTPUT, "d4", use_gpio_matrix); @@ -752,6 +808,8 @@ esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t *slot_config) return ret; } + s_host_ctx.slot_ctx[slot].slot_id = slot; + #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) { s_host_ctx.num_of_init_slots += 1; @@ -893,12 +951,12 @@ esp_err_t sdmmc_host_set_bus_width(int slot, size_t width) sdmmc_ll_set_card_width(s_host_ctx.hal.dev, slot, SD_BUS_WIDTH_1_BIT); } else if (width == 4) { sdmmc_ll_set_card_width(s_host_ctx.hal.dev, slot, SD_BUS_WIDTH_4_BIT); - // // D3 was set to GPIO high to force slave into SD mode, until 4-bit mode is set - // configure_pin(s_host_ctx.slot_ctx[slot].slot_gpio_num.d3, sdmmc_slot_gpio_sig[slot].d3, GPIO_MODE_INPUT_OUTPUT, "d3", s_host_ctx.slot_ctx[slot].use_gpio_matrix); + // D3 was set to GPIO high to force slave into SD mode, until 4-bit mode is set + configure_pin(s_host_ctx.slot_ctx[slot].slot_gpio_num.d3, sdmmc_slot_gpio_sig[slot].d3, GPIO_MODE_INPUT_OUTPUT, "d3", s_host_ctx.slot_ctx[slot].use_gpio_matrix); } else if (width == 8) { sdmmc_ll_set_card_width(s_host_ctx.hal.dev, slot, SD_BUS_WIDTH_8_BIT); - // // D3 was set to GPIO high to force slave into SD mode, until 4-bit mode is set - // configure_pin(s_host_ctx.slot_ctx[slot].slot_gpio_num.d3, sdmmc_slot_gpio_sig[slot].d3, GPIO_MODE_INPUT_OUTPUT, "d3", s_host_ctx.slot_ctx[slot].use_gpio_matrix); + // D3 was set to GPIO high to force slave into SD mode, until 4-bit mode is set + configure_pin(s_host_ctx.slot_ctx[slot].slot_gpio_num.d3, sdmmc_slot_gpio_sig[slot].d3, GPIO_MODE_INPUT_OUTPUT, "d3", s_host_ctx.slot_ctx[slot].use_gpio_matrix); } else { return ESP_ERR_INVALID_ARG; } @@ -946,7 +1004,9 @@ 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_18v_mode(s_host_ctx.hal.dev, slot, true); + 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); } } diff --git a/components/esp_driver_sdmmc/src/sdmmc_private.h b/components/esp_driver_sdmmc/src/sdmmc_internal.h similarity index 94% rename from components/esp_driver_sdmmc/src/sdmmc_private.h rename to components/esp_driver_sdmmc/src/sdmmc_internal.h index a68a6bf3fb..20ad83289a 100644 --- a/components/esp_driver_sdmmc/src/sdmmc_private.h +++ b/components/esp_driver_sdmmc/src/sdmmc_internal.h @@ -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 */ diff --git a/components/esp_driver_sdmmc/src/sdmmc_transaction.c b/components/esp_driver_sdmmc/src/sdmmc_transaction.c index fb6facbaa1..55cf55a402 100644 --- a/components/esp_driver_sdmmc/src/sdmmc_transaction.c +++ b/components/esp_driver_sdmmc/src/sdmmc_transaction.c @@ -20,8 +20,9 @@ #include "driver/sdmmc_host.h" #include "esp_cache.h" #include "esp_private/esp_cache_private.h" -#include "sdmmc_private.h" +#include "sdmmc_internal.h" #include "soc/soc_caps.h" +#include "hal/sdmmc_ll.h" /* Number of DMA descriptors used for transfer. * Increasing this value above 4 doesn't improve performance for the usual case @@ -350,7 +351,7 @@ static sdmmc_hw_cmd_t make_hw_cmd(sdmmc_command_t* cmd) if (cmd->opcode == MMC_GO_IDLE_STATE) { res.send_init = 1; } - + if (cmd->flags & SCF_RSP_PRESENT) { res.response_expect = 1; if (cmd->flags & SCF_RSP_136) { @@ -450,7 +451,7 @@ static esp_err_t process_events(int slot, sdmmc_event_t evt, sdmmc_command_t* cm }; sdmmc_event_t orig_evt = evt; ESP_LOGV(TAG, "%s: slot=%d state=%s evt=%"PRIx32" dma=%"PRIx32, __func__, slot, - s_state_names[*pstate], 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 state = (sdmmc_req_state_t) -1; while (next_state != state) { diff --git a/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/sdmmc_test_board.c b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/sdmmc_test_board.c index 33965e0557..08f37abe66 100644 --- a/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/sdmmc_test_board.c +++ b/components/esp_driver_sdmmc/test_apps/sd_test_utils/components/sdmmc_test_boards/sdmmc_test_board.c @@ -35,7 +35,7 @@ void sdmmc_test_board_get_config_sdmmc(int slot_index, sdmmc_host_t *out_host_co out_host_config->max_freq_khz = slot->max_freq_khz; } if (slot->uhs1_supported) { - out_host_config->flags |= SDMMC_HOST_FLAG_UHS1; + out_slot_config->flags |= SDMMC_SLOT_FLAG_UHS1; } #if SOC_SDMMC_USE_GPIO_MATRIX diff --git a/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_begin_end_sd.c b/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_begin_end_sd.c index 93b50bd387..cd37a75364 100644 --- a/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_begin_end_sd.c +++ b/components/esp_driver_sdmmc/test_apps/sdmmc/components/sdmmc_tests/sdmmc_test_begin_end_sd.c @@ -58,7 +58,7 @@ void sdmmc_test_sd_begin(int slot, int width, int freq_khz, int ddr, sdmmc_card_ } config.max_freq_khz = freq_khz; - bool slot_is_uhs1 = config.flags & SDMMC_HOST_FLAG_UHS1; + bool slot_is_uhs1 = slot_config.flags & SDMMC_SLOT_FLAG_UHS1; if (width == 1) { config.flags = SDMMC_HOST_FLAG_1BIT; @@ -80,7 +80,7 @@ void sdmmc_test_sd_begin(int slot, int width, int freq_khz, int ddr, sdmmc_card_ } if (slot_is_uhs1) { - config.flags |= SDMMC_HOST_FLAG_UHS1; + slot_config.flags |= SDMMC_SLOT_FLAG_UHS1; } #if SOC_SDMMC_IO_POWER_EXTERNAL diff --git a/components/esp_driver_sdspi/include/driver/sdspi_host.h b/components/esp_driver_sdspi/include/driver/sdspi_host.h index a614d9185c..091757b564 100644 --- a/components/esp_driver_sdspi/include/driver/sdspi_host.h +++ b/components/esp_driver_sdspi/include/driver/sdspi_host.h @@ -40,6 +40,8 @@ typedef int sdspi_dev_handle_t; .slot = SDSPI_DEFAULT_HOST, \ .max_freq_khz = SDMMC_FREQ_DEFAULT, \ .io_voltage = 3.3f, \ + .driver_strength = SDMMC_DRIVER_STRENGTH_B, \ + .current_limit = SDMMC_CURRENT_LIMIT_200MA, \ .init = &sdspi_host_init, \ .set_bus_width = NULL, \ .get_bus_width = NULL, \ @@ -57,6 +59,7 @@ typedef int sdspi_dev_handle_t; .dma_aligned_buffer = NULL, \ .pwr_ctrl_handle = NULL, \ .get_dma_info = &sdspi_host_get_dma_info, \ + .is_slot_set_to_uhs1 = NULL, \ } /** diff --git a/components/esp_hw_support/port/esp32p4/esp_clk_tree.c b/components/esp_hw_support/port/esp32p4/esp_clk_tree.c index 880dff56f9..03b6377107 100644 --- a/components/esp_hw_support/port/esp32p4/esp_clk_tree.c +++ b/components/esp_hw_support/port/esp32p4/esp_clk_tree.c @@ -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: clk_src_freq = clk_hal_apll_get_freq_hz(); break; - // case SOC_MOD_CLK_SDIO_PLL: TODO: IDF-8886 - // break; + case SOC_MOD_CLK_SDIO_PLL: + clk_src_freq = CLK_LL_PLL_SDIO_FREQ_MHZ * MHZ; + break; case SOC_MOD_CLK_RTC_SLOW: clk_src_freq = esp_clk_tree_lp_slow_get_freq_hz(precision); break; diff --git a/components/hal/esp32/include/hal/sdmmc_ll.h b/components/hal/esp32/include/hal/sdmmc_ll.h index c9ccf8ffe4..0f5a62ea37 100644 --- a/components/hal/esp32/include/hal/sdmmc_ll.h +++ b/components/hal/esp32/include/hal/sdmmc_ll.h @@ -408,6 +408,18 @@ static inline bool sdmmc_ll_is_card_write_protected(sdmmc_dev_t *hw, uint32_t sl 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 * diff --git a/components/hal/esp32p4/include/hal/clk_tree_ll.h b/components/hal/esp32p4/include/hal/clk_tree_ll.h index 3a6ad12024..2447dde728 100644 --- a/components/hal/esp32p4/include/hal/clk_tree_ll.h +++ b/components/hal/esp32p4/include/hal/clk_tree_ll.h @@ -40,6 +40,7 @@ extern "C" { #define CLK_LL_PLL_80M_FREQ_MHZ (80) #define CLK_LL_PLL_160M_FREQ_MHZ (160) #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_400M_FREQ_MHZ (400) diff --git a/components/hal/esp32p4/include/hal/sdmmc_ll.h b/components/hal/esp32p4/include/hal/sdmmc_ll.h index 2f7eb74bcd..9f168b2620 100644 --- a/components/hal/esp32p4/include/hal/sdmmc_ll.h +++ b/components/hal/esp32p4/include/hal/sdmmc_ll.h @@ -24,6 +24,7 @@ #include "soc/sdmmc_reg.h" #include "soc/hp_sys_clkrst_struct.h" #include "soc/lp_clkrst_struct.h" +#include "soc/pmu_reg.h" #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 #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 * @@ -137,9 +169,9 @@ static inline void sdmmc_ll_select_clk_source(sdmmc_dev_t *hw, soc_periph_sdmmc_ case SDMMC_CLK_SRC_PLL160M: clk_val = 0; break; - // case SDMMC_CLK_SRC_PLL200M: // TODO: IDF-8886 - // clk_val = 1; - // break; + case SDMMC_CLK_SRC_SDIO_200M: + clk_val = 1; + break; default: HAL_ASSERT(false); 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) { - 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_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 = 0; + if (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_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 = 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 @@ -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) { - 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; } /** @@ -458,7 +505,7 @@ static inline bool sdmmc_ll_is_card_write_protected(sdmmc_dev_t *hw, uint32_t sl * @param slot slot * @param en enable / disable 1.8V (3.3V on disable) */ -static inline void sdmmc_ll_enable_18v_mode(sdmmc_dev_t *hw, uint32_t slot, bool en) +static inline void sdmmc_ll_enable_1v8_mode(sdmmc_dev_t *hw, uint32_t slot, bool en) { if (en) { hw->uhs.volt |= BIT(slot); diff --git a/components/hal/esp32s3/include/hal/sdmmc_ll.h b/components/hal/esp32s3/include/hal/sdmmc_ll.h index 89aa2a8529..2747905931 100644 --- a/components/hal/esp32s3/include/hal/sdmmc_ll.h +++ b/components/hal/esp32s3/include/hal/sdmmc_ll.h @@ -444,6 +444,18 @@ static inline bool sdmmc_ll_is_card_write_protected(sdmmc_dev_t *hw, uint32_t sl 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 * diff --git a/components/sdmmc/include/sd_protocol_defs.h b/components/sdmmc/include/sd_protocol_defs.h index 8fbaa2c2cf..9072bd3d47 100644 --- a/components/sdmmc/include/sd_protocol_defs.h +++ b/components/sdmmc/include/sd_protocol_defs.h @@ -46,6 +46,7 @@ extern "C" { #define MMC_SET_BLOCKLEN 16 /* R1 */ #define MMC_READ_BLOCK_SINGLE 17 /* 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_SET_BLOCK_COUNT 23 /* R1 */ #define MMC_WRITE_BLOCK_SINGLE 24 /* R1 */ @@ -303,6 +304,8 @@ extern "C" { #define SD_CSD_SPEED(resp) MMC_RSP_BITS((resp), 96, 8) #define SD_CSD_SPEED_25_MHZ 0x32 #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_BASIC (1 << 0) /* basic */ #define SD_CSD_CCC_BR (1 << 2) /* block read */ @@ -416,6 +419,14 @@ extern "C" { #define SD_SFUNC_FUNC_MAX 15 #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_SDR25 1 /* 50 MHz clock */ diff --git a/components/sdmmc/include/sd_protocol_types.h b/components/sdmmc/include/sd_protocol_types.h index 3f3a29d1e4..70cdf76d12 100644 --- a/components/sdmmc/include/sd_protocol_types.h +++ b/components/sdmmc/include/sd_protocol_types.h @@ -156,7 +156,11 @@ typedef struct { /** * 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). */ typedef enum { @@ -164,8 +168,29 @@ typedef enum { SDMMC_DELAY_PHASE_1, /*!< Delay phase 1 */ SDMMC_DELAY_PHASE_2, /*!< Delay phase 2 */ SDMMC_DELAY_PHASE_3, /*!< Delay phase 3 */ + SDMMC_DELAY_PHASE_AUTO, /*!< Auto detect phase, only valid for UHS-I mode */ } 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 * @@ -185,7 +210,6 @@ typedef struct { Currently this is only used by the SDIO driver. Set this flag when using SDIO CMD53 byte mode, with user buffer that is behind the cache or not aligned to 4 byte boundary. */ -#define SDMMC_HOST_FLAG_UHS1 BIT(7) /*!< host supports UHS-I mode */ int slot; /*!< slot number, to be passed to host functions */ int max_freq_khz; /*!< max frequency supported by the host */ #define SDMMC_FREQ_DEFAULT 20000 /*!< SD/MMC Default speed (limited by clock divider) */ @@ -193,7 +217,11 @@ typedef struct { #define SDMMC_FREQ_PROBING 400 /*!< SD/MMC probing speed */ #define SDMMC_FREQ_52M 52000 /*!< MMC 52MHz 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) */ + 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 (*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 */ @@ -214,6 +242,7 @@ typedef struct { 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 */ 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; /** @@ -232,7 +261,7 @@ typedef struct { 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 */ 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 */ 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 */ @@ -240,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 log_bus_width : 2; /*!< log2(bus width supported by card) */ 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; /** diff --git a/components/sdmmc/sdmmc_common.c b/components/sdmmc/sdmmc_common.c index c59f879b01..68342e41a9 100644 --- a/components/sdmmc/sdmmc_common.c +++ b/components/sdmmc/sdmmc_common.c @@ -42,7 +42,11 @@ esp_err_t sdmmc_init_ocr(sdmmc_card_t* card) acmd41_arg |= SD_OCR_SDHC_CAP; } - if ((card->host.flags & SDMMC_HOST_FLAG_UHS1) != 0) { + 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; } @@ -188,6 +192,21 @@ esp_err_t sdmmc_init_card_hs_mode(sdmmc_card_t* card) 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) { int bus_width = 1; @@ -215,6 +234,14 @@ esp_err_t sdmmc_init_host_frequency(sdmmc_card_t* card) esp_err_t err; 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) { err = (*card->host.set_card_clk)(card->host.slot, card->max_freq_khz); if (err != ESP_OK) { @@ -346,6 +373,19 @@ esp_err_t sdmmc_fix_host_flags(sdmmc_card_t* card) 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; } diff --git a/components/sdmmc/sdmmc_common.h b/components/sdmmc/sdmmc_common.h index 64b48e6062..f326546ffe 100644 --- a/components/sdmmc/sdmmc_common.h +++ b/components/sdmmc/sdmmc_common.h @@ -18,6 +18,7 @@ #include #include "esp_log.h" +#include "esp_check.h" #include "esp_heap_caps.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -26,6 +27,7 @@ #include "sdmmc_cmd.h" #include "sys/param.h" #include "soc/soc_memory_layout.h" +#include "soc/soc_caps.h" #include "esp_dma_utils.h" #define SDMMC_GO_IDLE_DELAY_MS 20 @@ -57,6 +59,8 @@ #define SDMMC_MMC_TRIM_ARG 1 #define SDMMC_MMC_DISCARD_ARG 3 +#define SDMMC_FREQ_SDR104 208000 /*!< MMC 208MHz speed */ + /* Functions to send individual commands */ 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); @@ -80,13 +84,16 @@ 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 */ -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_write_sectors_dma(sdmmc_card_t* card, const void* src, 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, 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); +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 */ esp_err_t sdmmc_check_scr(sdmmc_card_t* card); @@ -141,6 +148,9 @@ 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_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 */ static inline bool host_is_spi(const sdmmc_card_t* card) diff --git a/components/sdmmc/sdmmc_init.c b/components/sdmmc/sdmmc_init.c index 45528f6785..cd8a517e92 100644 --- a/components/sdmmc/sdmmc_init.c +++ b/components/sdmmc/sdmmc_init.c @@ -97,6 +97,7 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card) /* 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*/ @@ -146,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); } + /* 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 */ SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_ssr); /* Switch to the host to use card->max_freq_khz 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 */ SDMMC_INIT_STEP(is_sdmem, sdmmc_check_scr); /* Sanity check after eMMC switch to HS mode */ diff --git a/components/sdmmc/sdmmc_mmc.c b/components/sdmmc/sdmmc_mmc.c index 23f18272f4..5662462aa2 100644 --- a/components/sdmmc/sdmmc_mmc.c +++ b/components/sdmmc/sdmmc_mmc.c @@ -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 */ 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); if (card->host.flags & SDMMC_HOST_FLAG_8BIT) { diff --git a/components/sdmmc/sdmmc_sd.c b/components/sdmmc/sdmmc_sd.c index 41700965d7..d9f5564acb 100644 --- a/components/sdmmc/sdmmc_sd.c +++ b/components/sdmmc/sdmmc_sd.c @@ -16,6 +16,7 @@ */ #include +#include "esp_check.h" #include "esp_timer.h" #include "esp_cache.h" #include "sdmmc_common.h" @@ -228,7 +229,7 @@ esp_err_t sdmmc_send_cmd_switch_func(sdmmc_card_t* card, 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, * 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; } uint32_t supported_mask = SD_SFUNC_SUPPORTED(response->data, 1); - 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; + ESP_LOGV(TAG, "%s: access mode supported_mask: 0x%"PRIx32, __func__, supported_mask); + + if (((card->host.flags & SDMMC_HOST_FLAG_DDR) != 0) && (card->is_uhs1 == 1)) { + //UHS-I DDR50 + ESP_LOGV(TAG, "%s: to switch to DDR50", __func__); + if ((supported_mask & BIT(SD_ACCESS_MODE_DDR50)) == 0) { + err = ESP_ERR_NOT_SUPPORTED; + 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: @@ -270,6 +319,172 @@ out: 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) { /* 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 */ - esp_err_t err = sdmmc_enable_hs_mode(card); + esp_err_t err = sdmmc_enter_higher_speed_mode(card); if (err != ESP_OK) { return err; } + /* HS mode has been enabled on the card. * Read CSD again, it should now indicate that the card supports * 50MHz clock. @@ -313,12 +529,9 @@ esp_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card) } } - if (card->csd.tr_speed != 50000000) { - ESP_LOGW(TAG, "unexpected: after enabling HS mode, tr_speed=%d", card->csd.tr_speed); - return ESP_ERR_NOT_SUPPORTED; - } + ESP_LOGD(TAG, "%s: after enabling HS mode, tr_speed=%d", __func__, card->csd.tr_speed); + card->max_freq_khz = MIN(card->host.max_freq_khz, SDMMC_FREQ_SDR104); - card->max_freq_khz = MIN(card->host.max_freq_khz, SDMMC_FREQ_HIGHSPEED); return ESP_OK; } @@ -341,10 +554,36 @@ esp_err_t sdmmc_init_sd_uhs1(sdmmc_card_t* 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); - return err; } - return ESP_OK; + card->is_uhs1 = 1; + + 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) @@ -415,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; } 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; - } 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; + break; } + return ESP_OK; } diff --git a/components/soc/esp32/register/soc/sdmmc_reg.h b/components/soc/esp32/register/soc/sdmmc_reg.h index 8370f9dc82..0cdb0272d7 100644 --- a/components/soc/esp32/register/soc/sdmmc_reg.h +++ b/components/soc/esp32/register/soc/sdmmc_reg.h @@ -67,6 +67,7 @@ #define SDMMC_INTMASK_HLE BIT(12) #define SDMMC_INTMASK_FRUN BIT(11) #define SDMMC_INTMASK_HTO BIT(10) +#define SDMMC_INTMASK_VOLT_SW SDMMC_INTMASK_HTO #define SDMMC_INTMASK_DTO BIT(9) #define SDMMC_INTMASK_RTO BIT(8) #define SDMMC_INTMASK_DCRC BIT(7) diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 81ee7c7c5c..deea07d8a7 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -1359,6 +1359,10 @@ config SOC_SDMMC_PSRAM_DMA_CAPABLE bool default y +config SOC_SDMMC_UHS_I_SUPPORTED + bool + default y + config SOC_SHA_DMA_MAX_BUFFER_SIZE int default 3968 @@ -1947,6 +1951,10 @@ config SOC_CLK_MPLL_SUPPORTED bool default y +config SOC_CLK_SDIO_PLL_SUPPORTED + bool + default y + config SOC_CLK_XTAL32K_SUPPORTED bool default y diff --git a/components/soc/esp32p4/include/soc/clk_tree_defs.h b/components/soc/esp32p4/include/soc/clk_tree_defs.h index 8e140f0571..acc86142aa 100644 --- a/components/soc/esp32p4/include/soc/clk_tree_defs.h +++ b/components/soc/esp32p4/include/soc/clk_tree_defs.h @@ -151,6 +151,7 @@ typedef enum { 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_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_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 */ @@ -706,7 +707,7 @@ typedef enum { /** * @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 @@ -714,7 +715,7 @@ typedef enum { typedef enum { 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 */ - // SOC_MOD_CLK_SDIO_PLL TODO:IDF-8886 + SDMMC_CLK_SRC_SDIO_200M = SOC_MOD_CLK_SDIO_PLL, } soc_periph_sdmmc_clk_src_t; //////////////////////////////////////////////////Temp Sensor/////////////////////////////////////////////////////////// diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index ee00d3083d..6a0d8a1364 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -499,6 +499,7 @@ #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_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) /*--------------------------- SHA CAPS ---------------------------------------*/ @@ -732,6 +733,7 @@ #define SOC_CLK_APLL_SUPPORTED (1) /*!< Support Audio 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_RC32K_SUPPORTED (1) /*!< Support an internal 32kHz RC oscillator */ diff --git a/components/soc/esp32p4/register/soc/sdmmc_struct.h b/components/soc/esp32p4/register/soc/sdmmc_struct.h index 0cf50596bb..6dd9dfc7af 100644 --- a/components/soc/esp32p4/register/soc/sdmmc_struct.h +++ b/components/soc/esp32p4/register/soc/sdmmc_struct.h @@ -914,7 +914,7 @@ typedef union { typedef union { struct { /** volt: R/W; bitpos: [1:0]; default: 0; - * Voltage mode selection, 1 bit for each card. + * 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. * 0: 3.3V mode. * 1: 1.8V mode. */ diff --git a/components/soc/esp32s3/register/soc/sdmmc_reg.h b/components/soc/esp32s3/register/soc/sdmmc_reg.h index 4d0a141781..84b5dd5b09 100644 --- a/components/soc/esp32s3/register/soc/sdmmc_reg.h +++ b/components/soc/esp32s3/register/soc/sdmmc_reg.h @@ -66,6 +66,7 @@ #define SDMMC_INTMASK_HLE BIT(12) #define SDMMC_INTMASK_FRUN BIT(11) #define SDMMC_INTMASK_HTO BIT(10) +#define SDMMC_INTMASK_VOLT_SW SDMMC_INTMASK_HTO #define SDMMC_INTMASK_DTO BIT(9) #define SDMMC_INTMASK_RTO BIT(8) #define SDMMC_INTMASK_DCRC BIT(7)