From 383464749afb3da5a9a1630a3f1325ac9faf5566 Mon Sep 17 00:00:00 2001 From: sergiu2014 Date: Wed, 9 May 2018 13:12:00 -0400 Subject: [PATCH] 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 --- components/driver/include/driver/sdmmc_defs.h | 15 + components/driver/include/driver/sdmmc_host.h | 2 +- .../driver/include/driver/sdmmc_types.h | 5 + components/sdmmc/sdmmc_cmd.c | 510 ++++++++++++++---- 4 files changed, 419 insertions(+), 113 deletions(-) diff --git a/components/driver/include/driver/sdmmc_defs.h b/components/driver/include/driver/sdmmc_defs.h index c3a63bd8d6..a6b135d6a0 100644 --- a/components/driver/include/driver/sdmmc_defs.h +++ b/components/driver/include/driver/sdmmc_defs.h @@ -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_ diff --git a/components/driver/include/driver/sdmmc_host.h b/components/driver/include/driver/sdmmc_host.h index f57b959cc7..8f582f0940 100644 --- a/components/driver/include/driver/sdmmc_host.h +++ b/components/driver/include/driver/sdmmc_host.h @@ -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, \ diff --git a/components/driver/include/driver/sdmmc_types.h b/components/driver/include/driver/sdmmc_types.h index 99e9715762..59c654cc81 100644 --- a/components/driver/include/driver/sdmmc_types.h +++ b/components/driver/include/driver/sdmmc_types.h @@ -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 */ diff --git a/components/sdmmc/sdmmc_cmd.c b/components/sdmmc/sdmmc_cmd.c index f315a1246b..8d90a72751 100644 --- a/components/sdmmc/sdmmc_cmd.c +++ b/components/sdmmc/sdmmc_cmd.c @@ -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");