Merge branch 'feat/sdmmc_uhs_tuning' into 'master'

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

Closes IDF-10294, IDF-11447, and IDF-8886

See merge request espressif/esp-idf!34441
This commit is contained in:
Armando (Dou Yiwen)
2024-11-13 11:05:10 +08:00
34 changed files with 924 additions and 186 deletions

View File

@@ -18,6 +18,7 @@
#include <string.h>
#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"
#ifdef __cplusplus
@@ -61,6 +63,8 @@ extern "C" {
#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);
@@ -82,15 +86,19 @@ 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_crc_on_off(sdmmc_card_t* card, bool crc_enable);
esp_err_t sdmmc_send_cmd_num_of_written_blocks(sdmmc_card_t* card, size_t* out_num_blocks);
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);
@@ -144,6 +152,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_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)

View File

@@ -3,7 +3,7 @@
*
* 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>
@@ -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 */
@@ -59,6 +60,7 @@ extern "C" {
#define SD_SEND_RELATIVE_ADDR 3 /* R6 */
#define SD_SEND_SWITCH_FUNC 6 /* R1 */
#define SD_SEND_IF_COND 8 /* R7 */
#define SD_SWITCH_VOLTAGE 11 /* R1 */
#define SD_ERASE_GROUP_START 32 /* R1 */
#define SD_ERASE_GROUP_END 33 /* R1 */
#define SD_READ_OCR 58 /* R3 */
@@ -99,8 +101,20 @@ extern "C" {
#define MMC_OCR_2_0V_2_1V (1<<8)
#define MMC_OCR_1_65V_1_95V (1<<7)
#define SD_OCR_SDHC_CAP (1<<30)
#define SD_OCR_VOL_MASK 0xFF8000 /* bits 23:15 */
#define SD_OCR_CARD_READY MMC_OCR_MEM_READY /* bit-31: power-up status */
#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 */
#define MMC_R1_READY_FOR_DATA (1<<8) /* ready for next transfer */
@@ -292,6 +306,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 */
@@ -405,6 +421,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 */

View File

@@ -149,12 +149,18 @@ typedef struct {
/** @endcond */
esp_err_t error; /*!< error returned from transfer */
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;
/**
* 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 {
@@ -162,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
*
@@ -190,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 */
@@ -211,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;
/**
@@ -229,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 */
@@ -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 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;
/**

View File

@@ -44,6 +44,16 @@ esp_err_t sdmmc_init_ocr(sdmmc_card_t* card)
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. */
err = sdmmc_send_cmd_send_op_cond(card, acmd41_arg, &card->ocr);
@@ -184,6 +194,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;
@@ -211,6 +236,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) {
@@ -280,7 +313,15 @@ void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card)
type = "MMC";
print_csd = true;
} 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;
}
fprintf(stream, "Type: %s\n", type);
@@ -334,6 +375,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;
}

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__,
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*/
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);
}
/* 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 */

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 */
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) {

View File

@@ -16,6 +16,7 @@
*/
#include <inttypes.h>
#include "esp_check.h"
#include "esp_timer.h"
#include "esp_cache.h"
#include "esp_private/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,13 +529,61 @@ 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);
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);
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)
@@ -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;
}
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;
}