eMMC/MMC support for ESP32

Merges https://github.com/espressif/esp-idf/pull/1941
Previous work in https://github.com/espressif/esp-idf/pull/590
This commit is contained in:
sergiu2014 2018-05-09 13:12:00 -04:00 committed by Ivan Grokhotkov
parent 80e47a005d
commit 383464749a
4 changed files with 419 additions and 113 deletions

View file

@ -91,6 +91,7 @@
/* SD mode R1 response type bits */
#define MMC_R1_READY_FOR_DATA (1<<8) /* ready for next transfer */
#define MMC_R1_APP_CMD (1<<5) /* app. commands supported */
#define MMC_R1_SWITCH_ERROR (1<<7) /* switch command did not succeed */
/* SPI mode R1 response type bits */
#define SD_SPI_R1_IDLE_STATE (1<<0)
@ -131,6 +132,13 @@
#define EXT_CSD_STRUCTURE 194 /* RO */
#define EXT_CSD_CARD_TYPE 196 /* RO */
#define EXT_CSD_SEC_COUNT 212 /* RO */
#define EXT_CSD_PWR_CL_26_360 203 /* RO */
#define EXT_CSD_PWR_CL_52_360 202 /* RO */
#define EXT_CSD_PWR_CL_26_195 201 /* RO */
#define EXT_CSD_PWR_CL_52_195 200 /* RO */
#define EXT_CSD_POWER_CLASS 187 /* R/W */
#define EXT_CSD_CMD_SET 191 /* R/W */
#define EXT_CSD_S_CMD_SET 504 /* RO */
/* EXT_CSD field definitions */
#define EXT_CSD_CMD_SET_NORMAL (1U << 0)
@ -163,6 +171,9 @@
#define EXT_CSD_CARD_TYPE_52M_V12 0x0b
#define EXT_CSD_CARD_TYPE_52M_V12_18 0x0f
/* EXT_CSD MMC */
#define EXT_CSD_MMC_SIZE 512
/* MMC_SWITCH access mode */
#define MMC_SWITCH_MODE_CMD_SET 0x00 /* Change the command set */
#define MMC_SWITCH_MODE_SET_BITS 0x01 /* Set bits in value */
@ -447,5 +458,9 @@ static inline uint32_t MMC_RSP_BITS(uint32_t *src, int start, int len)
/* CISTPL_FUNCID codes */
#define TPLFID_FUNCTION_SDIO 0x0c
/* Timing */
#define SDMMC_TIMING_LEGACY 0
#define SDMMC_TIMING_HIGHSPEED 1
#define SDMMC_TIMING_MMC_DDR52 2
#endif //_SDMMC_DEFS_H_

View file

@ -33,7 +33,7 @@ extern "C" {
* Uses SDMMC peripheral, with 4-bit mode enabled, and max frequency set to 20MHz
*/
#define SDMMC_HOST_DEFAULT() {\
.flags = SDMMC_HOST_FLAG_4BIT, \
.flags = (SDMMC_HOST_FLAG_4BIT | SDMMC_HOST_MEM_CARD), \
.slot = SDMMC_HOST_SLOT_1, \
.max_freq_khz = SDMMC_FREQ_DEFAULT, \
.io_voltage = 3.3f, \

View file

@ -125,6 +125,8 @@ typedef struct {
#define SDMMC_FREQ_DEFAULT 20000 /*!< SD/MMC Default speed (limited by clock divider) */
#define SDMMC_FREQ_HIGHSPEED 40000 /*!< SD High speed (limited by clock divider) */
#define SDMMC_FREQ_PROBING 400 /*!< SD/MMC probing speed */
#define SDMCC_FREQ_52M 52000 /*!< MMC 52Mhz speed */
#define SDMCC_FREQ_26M 26000 /*!< MMC 26Mhz speed */
float io_voltage; /*!< I/O voltage used by the controller (voltage switching is not supported) */
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 */
@ -147,6 +149,9 @@ typedef struct {
sdmmc_csd_t csd; /*!< decoded CSD (Card-Specific Data) register value */
sdmmc_scr_t scr; /*!< decoded SCR (SD card Configuration Register) value */
uint16_t rca; /*!< RCA (Relative Card Address) */
#define SDMMC_HOST_MMC_CARD BIT(8) /*!< card in MMC mode (SD otherwise) */
#define SDMMC_HOST_IO_CARD BIT(9) /*!< card in IO mode (SD moe only) */
#define SDMMC_HOST_MEM_CARD BIT(10) /*!< card in memory mode (SD or MMC) */
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 num_io_functions : 3; /*!< If is_sdio is 1, contains the number of IO functions on the card */

View file

@ -53,7 +53,7 @@ static esp_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, u
static esp_err_t sdmmc_send_cmd_read_ocr(sdmmc_card_t *card, uint32_t *ocrp);
static esp_err_t sdmmc_send_cmd_send_cid(sdmmc_card_t *card, sdmmc_cid_t *out_cid);
static esp_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid);
static esp_err_t sddmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_cid_t* out_cid);
static esp_err_t sdmmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_cid_t* out_cid);
static esp_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* out_rca);
static esp_err_t sdmmc_send_cmd_set_blocklen(sdmmc_card_t* card, sdmmc_csd_t* csd);
static esp_err_t sdmmc_send_cmd_switch_func(sdmmc_card_t* card,
@ -62,12 +62,15 @@ static esp_err_t sdmmc_send_cmd_switch_func(sdmmc_card_t* card,
static esp_err_t sdmmc_enable_hs_mode(sdmmc_card_t* card);
static esp_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card);
static esp_err_t sdmmc_io_enable_hs_mode(sdmmc_card_t* card);
static esp_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd);
static esp_err_t mmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd);
static esp_err_t sd_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd);
static esp_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd);
static esp_err_t sdmmc_mem_send_cxd_data(sdmmc_card_t* card , int opcode, void *data, size_t datalen);
static esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card, uint32_t rca);
static esp_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr);
static esp_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_scr);
static esp_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width);
static esp_err_t sdmmc_mmc_switch(sdmmc_card_t* card, uint8_t set, uint8_t index, uint8_t value);
static esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status);
static esp_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable);
static uint32_t get_host_ocr(float voltage);
@ -83,6 +86,7 @@ static esp_err_t sdmmc_io_rw_extended(sdmmc_card_t* card, int function,
uint32_t reg, int arg, void *data, size_t size);
static void sdmmc_fix_host_flags(sdmmc_card_t* card);
static bool host_is_spi(const sdmmc_card_t* card)
{
return (card->host.flags & SDMMC_HOST_FLAG_SPI) != 0;
@ -191,6 +195,17 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
/* Send SEND_OP_COND (ACMD41) command to the card until it becomes ready. */
err = sdmmc_send_cmd_send_op_cond(card, host_ocr, &card->ocr);
//if time-out try switching from SD to MMC and vice-versa
if (err == ESP_ERR_TIMEOUT){
if (card->host.flags & SDMMC_HOST_MMC_CARD) {
card->host.flags &= ~((uint32_t)(SDMMC_HOST_MMC_CARD));
} else {
card->host.flags |= SDMMC_HOST_MMC_CARD;
}
//retry SEND_OP_COND operation
err = sdmmc_send_cmd_send_op_cond(card, host_ocr, &card->ocr);
}
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_op_cond (1) returned 0x%x", __func__, err);
return err;
@ -215,7 +230,7 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
/* Read and decode the contents of CID register */
if (!is_spi) {
if (card->is_mem) {
err = sddmc_send_cmd_all_send_cid(card, &card->cid);
err = sdmmc_send_cmd_all_send_cid(card, &card->cid);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: all_send_cid returned 0x%x", __func__, err);
return err;
@ -263,61 +278,213 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
}
if (card->is_mem) {
/* SDSC cards support configurable data block lengths.
* We don't use this feature and set the block length to 512 bytes,
* same as the block length for SDHC cards.
*/
if ((card->ocr & SD_OCR_SDHC_CAP) == 0) {
err = sdmmc_send_cmd_set_blocklen(card, &card->csd);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: set_blocklen returned 0x%x", __func__, err);
return err;
}
}
/* Get the contents of SCR register: bus width and the version of SD spec
* supported by the card.
* In SD mode, this is the first command which uses D0 line. Errors at
* this step usually indicate connection issue or lack of pull-up resistor.
*/
err = sdmmc_send_cmd_send_scr(card, &card->scr);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_scr (1) returned 0x%x", __func__, err);
return err;
}
}
if (card->host.flags & SDMMC_HOST_MMC_CARD) { //MMC CARD
/* sdmmc_mem_mmc_init */
int width, value;
int card_type;
int speed = SDMMC_FREQ_DEFAULT;
uint8_t powerclass = 0;
if (card->is_mem) {
/* If the host has been initialized with 4-bit bus support, and the card
* supports 4-bit bus, switch to 4-bit bus now.
*/
if ((card->host.flags & SDMMC_HOST_FLAG_4BIT) &&
(card->scr.bus_width & SCR_SD_BUS_WIDTHS_4BIT)) {
ESP_LOGD(TAG, "switching to 4-bit bus mode");
err = sdmmc_send_cmd_set_bus_width(card, 4);
if (err != ESP_OK) {
ESP_LOGE(TAG, "set_bus_width failed");
return err;
//!!!remember to free(ext_csd) before all return-s in this block !!!
//if passing this buffer to the host driver, it might need to be in DMA-capable memory
uint8_t* ext_csd = heap_caps_malloc(EXT_CSD_MMC_SIZE,MALLOC_CAP_DMA);
if(!ext_csd){
ESP_LOGE(TAG, "%s: could not allocate ext_csd\n", __func__);
free(ext_csd);
return ESP_ERR_NO_MEM;
}
err = (*config->set_bus_width)(config->slot, 4);
if (err != ESP_OK) {
ESP_LOGE(TAG, "slot->set_bus_width failed");
return err;
}
}
int timing = SDMMC_TIMING_LEGACY;
uint32_t sectors = 0;
/* Wait for the card to be ready for data transfers */
uint32_t status = 0;
while (!is_spi && !(status & MMC_R1_READY_FOR_DATA)) {
// TODO: add some timeout here
uint32_t count = 0;
err = sdmmc_send_cmd_send_status(card, &status);
if (card->csd.mmc_ver >= MMC_CSD_MMCVER_4_0) {
/* read EXT_CSD */
err = sdmmc_mem_send_cxd_data(card,
MMC_SEND_EXT_CSD, ext_csd, EXT_CSD_MMC_SIZE);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: can't read EXT_CSD", __func__);
free(ext_csd);
return err;
}
card_type = ext_csd[EXT_CSD_CARD_TYPE];
//NOTE: ESP32 doesn't support DDR
if (card_type & EXT_CSD_CARD_TYPE_F_52M_1_8V) {
speed = SDMCC_FREQ_52M;
timing = SDMMC_TIMING_HIGHSPEED;
} else if (card_type & EXT_CSD_CARD_TYPE_F_52M) {
speed = SDMCC_FREQ_52M;
timing = SDMMC_TIMING_HIGHSPEED;
} else if (card_type & EXT_CSD_CARD_TYPE_F_26M) {
speed = SDMCC_FREQ_26M;
} else {
ESP_LOGE(TAG, "%s: unknown CARD_TYPE 0x%x\n", __func__,
ext_csd[EXT_CSD_CARD_TYPE]);
}
if (timing != SDMMC_TIMING_LEGACY) {
/* switch to high speed timing */
err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, EXT_CSD_HS_TIMING_HS);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: can't change high speed\n",
__func__);
free(ext_csd);
return err;
}
ets_delay_us(10000);
}
if (config->max_freq_khz >= SDMMC_FREQ_HIGHSPEED &&
speed >= SDMMC_FREQ_HIGHSPEED) {
ESP_LOGD(TAG, "switching to HS bus mode");
err = (*config->set_card_clk)(config->slot, SDMMC_FREQ_HIGHSPEED);
if (err != ESP_OK) {
ESP_LOGE(TAG, "failed to switch peripheral to HS bus mode");
free(ext_csd);
return err;
}
} else if (config->max_freq_khz >= SDMMC_FREQ_DEFAULT &&
speed >= SDMMC_FREQ_DEFAULT) {
ESP_LOGD(TAG, "switching to DS bus mode");
err = (*config->set_card_clk)(config->slot, SDMMC_FREQ_DEFAULT);
if (err != ESP_OK) {
ESP_LOGE(TAG, "failed to switch peripheral to HS bus mode");
free(ext_csd);
return err;
}
}
if (timing != SDMMC_TIMING_LEGACY) {
/* read EXT_CSD again */
err = sdmmc_mem_send_cxd_data(card,
MMC_SEND_EXT_CSD, ext_csd, EXT_CSD_MMC_SIZE);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: can't re-read EXT_CSD\n", __func__);
free(ext_csd);
return err;
}
if (ext_csd[EXT_CSD_HS_TIMING] != EXT_CSD_HS_TIMING_HS) {
ESP_LOGE(TAG, "%s, HS_TIMING set failed\n", __func__);
free(ext_csd);
return ESP_ERR_INVALID_RESPONSE;
}
}
if (card->host.flags & SDMMC_HOST_FLAG_8BIT) {
width = 8;
value = EXT_CSD_BUS_WIDTH_8;
powerclass = ext_csd[(speed > SDMCC_FREQ_26M) ? EXT_CSD_PWR_CL_52_360 : EXT_CSD_PWR_CL_26_360] >> 4;
} else if (card->host.flags & SDMMC_HOST_FLAG_4BIT) {
width = 4;
value = EXT_CSD_BUS_WIDTH_4;
powerclass = ext_csd[(speed > SDMCC_FREQ_26M) ? EXT_CSD_PWR_CL_52_360 : EXT_CSD_PWR_CL_26_360] & 0x0f;
} else {
width = 1;
value = EXT_CSD_BUS_WIDTH_1;
powerclass = 0; //card must be able to do full rate at powerclass 0 in 1-bit mode
}
if (powerclass != 0) {
err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_POWER_CLASS, powerclass);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: can't change power class"
" (%d bit)\n", __func__, powerclass);
free(ext_csd);
return err;
}
}
if (width != 1) {
err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH, value);
if (err == ESP_OK) {
err = (*config->set_bus_width)(config->slot, width);
if (err != ESP_OK) {
ESP_LOGE(TAG, "slot->set_bus_width failed");
free(ext_csd);
return err;
}
} else {
ESP_LOGE(TAG, "%s: can't change bus width"
" (%d bit)\n", __func__, width);
free(ext_csd);
return err;
}
/* XXXX: need bus test? (using by CMD14 & CMD19) */
ets_delay_us(10000);
}
sectors = ( ext_csd[EXT_CSD_SEC_COUNT + 0] << 0 )
| ( ext_csd[EXT_CSD_SEC_COUNT + 1] << 8 )
| ( ext_csd[EXT_CSD_SEC_COUNT + 2] << 16 )
| ( ext_csd[EXT_CSD_SEC_COUNT + 3] << 24 );
if (sectors > (2u * 1024 * 1024 * 1024) / 512) {
//card->flags |= SFF_SDHC;
card->csd.capacity = sectors;
}
free(ext_csd); //done with ext_csd
}
} else { //SD CARD
/* SDSC cards support configurable data block lengths.
* We don't use this feature and set the block length to 512 bytes,
* same as the block length for SDHC cards.
*/
if ((card->ocr & SD_OCR_SDHC_CAP) == 0) {
err = sdmmc_send_cmd_set_blocklen(card, &card->csd);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: set_blocklen returned 0x%x", __func__, err);
return err;
}
}
/* Get the contents of SCR register: bus width and the version of SD spec
* supported by the card.
* In SD mode, this is the first command which uses D0 line. Errors at
* this step usually indicate connection issue or lack of pull-up resistor.
*/
err = sdmmc_send_cmd_send_scr(card, &card->scr);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_scr (1) returned 0x%x", __func__, err);
return err;
}
if (++count % 16 == 0) {
ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
/* If the host has been initialized with 4-bit bus support, and the card
* supports 4-bit bus, switch to 4-bit bus now.
*/
if ((card->host.flags & SDMMC_HOST_FLAG_4BIT) &&
(card->scr.bus_width & SCR_SD_BUS_WIDTHS_4BIT)) {
ESP_LOGD(TAG, "switching to 4-bit bus mode");
err = sdmmc_send_cmd_set_bus_width(card, 4);
if (err != ESP_OK) {
ESP_LOGE(TAG, "set_bus_width failed");
return err;
}
err = (*config->set_bus_width)(config->slot, 4);
if (err != ESP_OK) {
ESP_LOGE(TAG, "slot->set_bus_width failed");
return err;
}
}
}
/* Wait for the card to be ready for data transfers */
uint32_t status = 0;
while (!is_spi && !(status & MMC_R1_READY_FOR_DATA)) {
// TODO: add some timeout here
uint32_t count = 0;
err = sdmmc_send_cmd_send_status(card, &status);
if (err != ESP_OK) {
return err;
}
if (++count % 16 == 0) {
ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
}
}
}
} else {
/* IO card */
if (config->flags & SDMMC_HOST_FLAG_4BIT) {
@ -347,70 +514,77 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
}
}
}
/* So far initialization has been done using 400kHz clock. Determine the
* clock rate which both host and the card support, and switch to it.
*/
bool freq_switched = false;
if (config->max_freq_khz >= SDMMC_FREQ_HIGHSPEED &&
!is_spi /* SPI doesn't support >26MHz in some cases */) {
if (card->is_mem) {
err = sdmmc_enable_hs_mode_and_check(card);
} else {
err = sdmmc_io_enable_hs_mode(card);
}
if ( !(card->host.flags & SDMMC_HOST_MMC_CARD) ) { //SD / SDIO
/* So far initialization has been done using 400kHz clock. Determine the
* clock rate which both host and the card support, and switch to it.
*/
bool freq_switched = false;
if (config->max_freq_khz >= SDMMC_FREQ_HIGHSPEED &&
!is_spi /* SPI doesn't support >26MHz in some cases */) {
if (card->is_mem) {
err = sdmmc_enable_hs_mode_and_check(card);
} else {
err = sdmmc_io_enable_hs_mode(card);
}
if (err == ESP_ERR_NOT_SUPPORTED) {
ESP_LOGD(TAG, "%s: host supports HS mode, but card doesn't", __func__);
} else if (err != ESP_OK) {
return err;
} else {
ESP_LOGD(TAG, "%s: switching host to HS mode", __func__);
/* ESP_OK, HS mode has been enabled on the card side.
* Switch the host to HS mode.
*/
err = (*config->set_card_clk)(config->slot, SDMMC_FREQ_HIGHSPEED);
if (err == ESP_ERR_NOT_SUPPORTED) {
ESP_LOGD(TAG, "%s: host supports HS mode, but card doesn't", __func__);
} else if (err != ESP_OK) {
return err;
} else {
ESP_LOGD(TAG, "%s: switching host to HS mode", __func__);
/* ESP_OK, HS mode has been enabled on the card side.
* Switch the host to HS mode.
*/
err = (*config->set_card_clk)(config->slot, SDMMC_FREQ_HIGHSPEED);
if (err != ESP_OK) {
ESP_LOGE(TAG, "failed to switch peripheral to HS bus mode");
return err;
}
freq_switched = true;
}
}
/* All SD cards must support default speed mode (25MHz).
* config->max_freq_khz may be used to limit the clock frequency.
*/
if (!freq_switched &&
config->max_freq_khz >= SDMMC_FREQ_DEFAULT) {
ESP_LOGD(TAG, "switching to DS bus mode");
err = (*config->set_card_clk)(config->slot, SDMMC_FREQ_DEFAULT);
if (err != ESP_OK) {
ESP_LOGE(TAG, "failed to switch peripheral to HS bus mode");
return err;
}
freq_switched = true;
}
}
/* All SD cards must support default speed mode (25MHz).
* config->max_freq_khz may be used to limit the clock frequency.
*/
if (!freq_switched &&
config->max_freq_khz >= SDMMC_FREQ_DEFAULT) {
ESP_LOGD(TAG, "switching to DS bus mode");
err = (*config->set_card_clk)(config->slot, SDMMC_FREQ_DEFAULT);
if (err != ESP_OK) {
ESP_LOGE(TAG, "failed to switch peripheral to HS bus mode");
return err;
}
freq_switched = true;
}
/* If frequency switch has been performed, read SCR register one more time
* and compare the result with the previous one. Use this simple check as
* an indicator of potential signal integrity issues.
*/
if (freq_switched) {
if (card->is_mem) {
sdmmc_scr_t scr_tmp;
err = sdmmc_send_cmd_send_scr(card, &scr_tmp);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_scr (2) returned 0x%x", __func__, err);
return err;
/* If frequency switch has been performed, read SCR register one more time
* and compare the result with the previous one. Use this simple check as
* an indicator of potential signal integrity issues.
*/
if (freq_switched) {
if (card->is_mem) {
sdmmc_scr_t scr_tmp;
err = sdmmc_send_cmd_send_scr(card, &scr_tmp);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_scr (2) returned 0x%x", __func__, err);
return err;
}
if (memcmp(&card->scr, &scr_tmp, sizeof(scr_tmp)) != 0) {
ESP_LOGE(TAG, "got corrupted data after increasing clock frequency");
return ESP_ERR_INVALID_RESPONSE;
}
} else {
/* TODO: For IO cards, read some data to see if frequency switch
* was successful.
*/
}
if (memcmp(&card->scr, &scr_tmp, sizeof(scr_tmp)) != 0) {
ESP_LOGE(TAG, "got corrupted data after increasing clock frequency");
return ESP_ERR_INVALID_RESPONSE;
}
} else {
/* TODO: For IO cards, read some data to see if frequency switch
* was successful.
*/
}
}
}
return ESP_OK;
}
@ -539,6 +713,8 @@ static esp_err_t sdmmc_send_cmd_send_if_cond(sdmmc_card_t* card, uint32_t ocr)
static esp_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp)
{
esp_err_t err;
sdmmc_command_t cmd = {
.arg = ocr,
.flags = SCF_CMD_BCR | SCF_RSP_R3,
@ -547,7 +723,19 @@ static esp_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, u
int nretries = SDMMC_SEND_OP_COND_MAX_RETRIES;
int err_cnt = SDMMC_SEND_OP_COND_MAX_ERRORS;
for (; nretries != 0; --nretries) {
esp_err_t err = sdmmc_send_app_cmd(card, &cmd);
bzero(&cmd, sizeof cmd);
cmd.arg = ocr;
cmd.flags = SCF_CMD_BCR | SCF_RSP_R3;
if (card->host.flags & SDMMC_HOST_MMC_CARD) { /* MMC mode */
cmd.arg &= ~MMC_OCR_ACCESS_MODE_MASK;
cmd.arg |= MMC_OCR_SECTOR_MODE;
cmd.opcode = MMC_SEND_OP_COND;
err = sdmmc_send_cmd(card, &cmd);
} else { /* SD mode */
cmd.opcode = SD_APP_OP_COND;
err = sdmmc_send_app_cmd(card, &cmd);
}
if (err != ESP_OK) {
if (--err_cnt == 0) {
ESP_LOGD(TAG, "%s: sdmmc_send_app_cmd err=0x%x", __func__, err);
@ -606,7 +794,7 @@ esp_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid)
return ESP_OK;
}
static esp_err_t sddmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_cid_t* out_cid)
static esp_err_t sdmmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_cid_t* out_cid)
{
assert(out_cid);
sdmmc_command_t cmd = {
@ -643,17 +831,26 @@ static esp_err_t sdmmc_send_cmd_send_cid(sdmmc_card_t *card, sdmmc_cid_t *out_ci
static esp_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* out_rca)
{
static uint16_t next_rca_mmc = 0;
assert(out_rca);
sdmmc_command_t cmd = {
.opcode = SD_SEND_RELATIVE_ADDR,
.flags = SCF_CMD_BCR | SCF_RSP_R6
};
if (card->host.flags & SDMMC_HOST_MMC_CARD) {
// MMC cards expect you to set the RCA, so just keep a counter of them
next_rca_mmc++;
if (next_rca_mmc == 0) /* 0 means deselcted, so can't use that for an RCA */
next_rca_mmc++;
cmd.arg = MMC_ARG_RCA(next_rca_mmc);
}
esp_err_t err = sdmmc_send_cmd(card, &cmd);
if (err != ESP_OK) {
return err;
}
*out_rca = SD_R6_RCA(cmd.response);
*out_rca = (card->host.flags & SDMMC_HOST_MMC_CARD) ? next_rca_mmc : SD_R6_RCA(cmd.response);
return ESP_OK;
}
@ -668,7 +865,35 @@ static esp_err_t sdmmc_send_cmd_set_blocklen(sdmmc_card_t* card, sdmmc_csd_t* cs
return sdmmc_send_cmd(card, &cmd);
}
static esp_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd)
static esp_err_t mmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd)
{
out_csd->csd_ver = MMC_CSD_CSDVER(response);
if (out_csd->csd_ver == MMC_CSD_CSDVER_1_0 ||
out_csd->csd_ver == MMC_CSD_CSDVER_2_0 ||
out_csd->csd_ver == MMC_CSD_CSDVER_EXT_CSD) {
out_csd->mmc_ver = MMC_CSD_MMCVER(response);
out_csd->capacity = MMC_CSD_CAPACITY(response);
out_csd->read_block_len = MMC_CSD_READ_BL_LEN(response);
} else {
ESP_LOGE(TAG, "unknown MMC CSD structure version 0x%x\n", out_csd->csd_ver);
return 1;
}
int read_bl_size = 1 << out_csd->read_block_len;
out_csd->sector_size = MIN(read_bl_size, 512);
if (out_csd->sector_size < read_bl_size) {
out_csd->capacity *= read_bl_size / out_csd->sector_size;
}
/* MMC special handling? */
int speed = SD_CSD_SPEED(response);
if (speed == SD_CSD_SPEED_50_MHZ) {
out_csd->tr_speed = 50000000;
} else {
out_csd->tr_speed = 25000000;
}
return ESP_OK;
}
static esp_err_t sd_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd)
{
out_csd->csd_ver = SD_CSD_CSDVER(response);
switch (out_csd->csd_ver) {
@ -723,7 +948,46 @@ static esp_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_cs
flip_byte_order(spi_buf, sizeof(spi_buf));
ptr = spi_buf;
}
return sdmmc_decode_csd(ptr, out_csd);
if (card->host.flags & SDMMC_HOST_MMC_CARD) {/* MMC mode */
err = mmc_decode_csd(cmd.response, out_csd);
} else {/* SD mode */
err = sd_decode_csd(ptr, out_csd);
}
return err;
}
static esp_err_t sdmmc_mem_send_cxd_data(sdmmc_card_t* card , int opcode, void *data, size_t datalen)
{
sdmmc_command_t cmd;
void *ptr = NULL;
esp_err_t error = ESP_OK;
ptr = malloc(datalen);
if (ptr == NULL) {
error = ESP_ERR_NO_MEM;
} else {
memset(&cmd, 0, sizeof(cmd));
cmd.data = ptr;
cmd.datalen = datalen;
cmd.blklen = datalen;
cmd.opcode = opcode;
cmd.arg = 0;
cmd.flags = SCF_CMD_ADTC | SCF_CMD_READ;
if (opcode == MMC_SEND_EXT_CSD) {
cmd.flags |= SCF_RSP_R1;
} else {
cmd.flags |= SCF_RSP_R2;
}
error = sdmmc_send_cmd(card, &cmd);
if (error == 0) {
memcpy(data, ptr, datalen);
}
if (ptr != NULL) {
free(ptr);
}
}
return error;
}
static esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card, uint32_t rca)
@ -776,15 +1040,37 @@ static esp_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_sc
static esp_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width)
{
uint8_t ignored[8];
sdmmc_command_t cmd = {
.opcode = SD_APP_SET_BUS_WIDTH,
.flags = SCF_RSP_R1 | SCF_CMD_AC,
.arg = (width == 4) ? SD_ARG_BUS_WIDTH_4 : SD_ARG_BUS_WIDTH_1
.arg = (width == 4) ? SD_ARG_BUS_WIDTH_4 : SD_ARG_BUS_WIDTH_1,
.data = ignored,
.datalen = 8,
.blklen = 4,
};
return sdmmc_send_app_cmd(card, &cmd);
}
static esp_err_t sdmmc_mmc_switch(sdmmc_card_t* card, uint8_t set, uint8_t index, uint8_t value)
{
sdmmc_command_t cmd = {
.opcode = MMC_SWITCH,
.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8) | set,
.flags = SCF_RSP_R1B | SCF_CMD_AC,
};
esp_err_t err = sdmmc_send_cmd(card, &cmd);
if (err == ESP_OK) {
//check response bit to see that switch was accepted
if (MMC_R1(cmd.response) & MMC_R1_SWITCH_ERROR)
err = ESP_ERR_INVALID_RESPONSE;
}
return err;
}
static esp_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable)
{
assert(host_is_spi(card) && "CRC_ON_OFF can only be used in SPI mode");