From e6258ac6cbf0a0453a3701cc1438c6b5d40a61d3 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 12 Jul 2017 19:44:17 +0800 Subject: [PATCH 1/5] =?UTF-8?q?sdmmc:=20don=E2=80=99t=20flip=20word=20orde?= =?UTF-8?q?r=20in=20MMC=5FRSP=5FBITS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MMC_RSP_BITS helper function had a hack that it flipped word order in the response, assuming that response size is 4 words. This hack does not work for responses which are not 4 words long (such as the SWITCH_FUNC response, which is 64 words long). This change removes the hack and the matching word order reversal code in sdmmc driver. --- components/driver/include/driver/sdmmc_defs.h | 29 +++++++++++++++++-- components/driver/sdmmc_transaction.c | 7 ++--- components/sdmmc/sdmmc_cmd.c | 25 ++++++++++------ components/sdmmc/test/test_sd.c | 9 ++++++ 4 files changed, 54 insertions(+), 16 deletions(-) diff --git a/components/driver/include/driver/sdmmc_defs.h b/components/driver/include/driver/sdmmc_defs.h index 455d1c7af..30df7e2fc 100644 --- a/components/driver/include/driver/sdmmc_defs.h +++ b/components/driver/include/driver/sdmmc_defs.h @@ -304,13 +304,38 @@ #define SD_ACCESS_MODE_SDR104 3 #define SD_ACCESS_MODE_DDR50 4 +/** + * @brief Extract up to 32 sequential bits from an array of 32-bit words + * + * Bits within the word are numbered in the increasing order from LSB to MSB. + * + * As an example, consider 2 32-bit words: + * + * 0x01234567 0x89abcdef + * + * On a little-endian system, the bytes are stored in memory as follows: + * + * 67 45 23 01 ef cd ab 89 + * + * MMC_RSP_BITS will extact bits as follows: + * + * start=0 len=4 -> result=0x00000007 + * start=0 len=12 -> result=0x00000567 + * start=28 len=8 -> result=0x000000f0 + * start=59 len=5 -> result=0x00000011 + * + * @param src array of words to extract bits from + * @param start index of the first bit to extract + * @param len number of bits to extract, 1 to 32 + * @return 32-bit word where requested bits start from LSB + */ static inline uint32_t MMC_RSP_BITS(uint32_t *src, int start, int len) { uint32_t mask = (len % 32 == 0) ? UINT_MAX : UINT_MAX >> (32 - (len % 32)); - size_t word = 3 - start / 32; + size_t word = start / 32; size_t shift = start % 32; uint32_t right = src[word] >> shift; - uint32_t left = (len + shift <= 32) ? 0 : src[word - 1] << ((32 - shift) % 32); + uint32_t left = (len + shift <= 32) ? 0 : src[word + 1] << ((32 - shift) % 32); return (left | right) & mask; } diff --git a/components/driver/sdmmc_transaction.c b/components/driver/sdmmc_transaction.c index c3a582754..3cc5ee5da 100644 --- a/components/driver/sdmmc_transaction.c +++ b/components/driver/sdmmc_transaction.c @@ -261,11 +261,8 @@ static void process_command_response(uint32_t status, sdmmc_command_t* cmd) { if (cmd->flags & SCF_RSP_PRESENT) { if (cmd->flags & SCF_RSP_136) { - cmd->response[3] = SDMMC.resp[0]; - cmd->response[2] = SDMMC.resp[1]; - cmd->response[1] = SDMMC.resp[2]; - cmd->response[0] = SDMMC.resp[3]; - + /* Destination is 4-byte aligned, can memcopy from peripheral registers */ + memcpy(cmd->response, (uint32_t*) SDMMC.resp, 4 * sizeof(uint32_t)); } else { cmd->response[0] = SDMMC.resp[0]; cmd->response[1] = 0; diff --git a/components/sdmmc/sdmmc_cmd.c b/components/sdmmc/sdmmc_cmd.c index 0784a279a..65363d7d7 100644 --- a/components/sdmmc/sdmmc_cmd.c +++ b/components/sdmmc/sdmmc_cmd.c @@ -51,7 +51,7 @@ static esp_err_t sdmmc_send_cmd_stop_transmission(sdmmc_card_t* card, uint32_t* 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); -static void response_ntoh(sdmmc_response_t response); +static void flip_byte_order(uint32_t* response, size_t size); static esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src, size_t start_block, size_t block_count); static esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst, @@ -419,7 +419,7 @@ static esp_err_t sdmmc_send_cmd_send_cid(sdmmc_card_t *card, sdmmc_cid_t *out_ci if (err != ESP_OK) { return err; } - response_ntoh(buf); + flip_byte_order(buf, sizeof(buf)); return sdmmc_decode_cid(buf, out_cid); } @@ -501,10 +501,12 @@ static esp_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_cs if (err != ESP_OK) { return err; } + uint32_t* ptr = cmd.response; if (is_spi) { - response_ntoh(spi_buf); + flip_byte_order(spi_buf, sizeof(spi_buf)); + ptr = spi_buf; } - return sdmmc_decode_csd(is_spi ? spi_buf : cmd.response, out_csd); + return sdmmc_decode_csd(ptr, out_csd); } static esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card) @@ -520,8 +522,8 @@ static esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card) static esp_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr) { sdmmc_response_t resp = {0xabababab, 0xabababab, 0x12345678, 0x09abcdef}; - resp[2] = __builtin_bswap32(raw_scr[0]); - resp[3] = __builtin_bswap32(raw_scr[1]); + resp[1] = __builtin_bswap32(raw_scr[0]); + resp[0] = __builtin_bswap32(raw_scr[1]); int ver = SCR_STRUCTURE(resp); if (ver != 0) { return ESP_ERR_NOT_SUPPORTED; @@ -597,10 +599,15 @@ static uint32_t get_host_ocr(float voltage) return SD_OCR_VOL_MASK; } -static void response_ntoh(sdmmc_response_t response) +static void flip_byte_order(uint32_t* response, size_t size) { - for (int i = 0; i < 4; ++i) { - response[i] = __builtin_bswap32(response[i]); + assert(size % (2 * sizeof(uint32_t)) == 0); + const size_t n_words = size / sizeof(uint32_t); + for (int i = 0; i < n_words / 2; ++i) { + uint32_t left = __builtin_bswap32(response[i]); + uint32_t right = __builtin_bswap32(response[n_words - i - 1]); + response[i] = right; + response[n_words - i - 1] = left; } } diff --git a/components/sdmmc/test/test_sd.c b/components/sdmmc/test/test_sd.c index 86ff501b8..5602bd608 100644 --- a/components/sdmmc/test/test_sd.c +++ b/components/sdmmc/test/test_sd.c @@ -26,6 +26,15 @@ #include #include +TEST_CASE("MMC_RSP_BITS", "[sd]") +{ + uint32_t data[2] = { 0x01234567, 0x89abcdef }; + TEST_ASSERT_EQUAL_HEX32(0x7, MMC_RSP_BITS(data, 0, 4)); + TEST_ASSERT_EQUAL_HEX32(0x567, MMC_RSP_BITS(data, 0, 12)); + TEST_ASSERT_EQUAL_HEX32(0xf0, MMC_RSP_BITS(data, 28, 8)); + TEST_ASSERT_EQUAL_HEX32(0x3, MMC_RSP_BITS(data, 1, 3)); + TEST_ASSERT_EQUAL_HEX32(0x11, MMC_RSP_BITS(data, 59, 5)); +} TEST_CASE("can probe SD", "[sd][ignore]") { From f908425b134363bc2f549075bd3a8b49f0f28e27 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 12 Jul 2017 21:04:54 +0800 Subject: [PATCH 2/5] sdmmc: introduce is_app_cmd flag, check it along with opcode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ‘make_hw_cmd’ function checks opcodes in a few cases. Comparing opcode does not tell the whole story, because for some SD commands there is are APP commands with the same opcodes. This change introduces a flag which indicates whether the next command is going to be an APP command. The check for APP_SET_BUS_WIDTH command is updated to use this flag. This fixes a bug with an unexpected STOP_TRANSMISSION command sent after SWITCH_FUNC command, which has opcode 6, same as APP_SET_BUS_WIDTH. --- components/driver/sdmmc_transaction.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components/driver/sdmmc_transaction.c b/components/driver/sdmmc_transaction.c index 3cc5ee5da..8f06be88e 100644 --- a/components/driver/sdmmc_transaction.c +++ b/components/driver/sdmmc_transaction.c @@ -73,6 +73,7 @@ const uint32_t SDMMC_CMD_ERR_MASK = static sdmmc_desc_t s_dma_desc[SDMMC_DMA_DESC_CNT]; static sdmmc_transfer_state_t s_cur_transfer = { 0 }; static QueueHandle_t s_request_mutex; +static bool s_is_app_cmd; // This flag is set if the next command is an APP command static esp_err_t handle_idle_state_events(); static sdmmc_hw_cmd_t make_hw_cmd(sdmmc_command_t* cmd); @@ -88,6 +89,7 @@ esp_err_t sdmmc_host_transaction_handler_init() if (!s_request_mutex) { return ESP_ERR_NO_MEM; } + s_is_app_cmd = false; return ESP_OK; } @@ -145,6 +147,7 @@ esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo) break; } } + s_is_app_cmd = (ret == ESP_OK && cmdinfo->opcode == MMC_APP_CMD); xSemaphoreGive(s_request_mutex); return ret; } @@ -228,7 +231,7 @@ static sdmmc_hw_cmd_t make_hw_cmd(sdmmc_command_t* cmd) } else { res.wait_complete = 1; } - if (cmd->opcode == SD_APP_SET_BUS_WIDTH) { + if (s_is_app_cmd && cmd->opcode == SD_APP_SET_BUS_WIDTH) { res.send_auto_stop = 1; res.data_expected = 1; } From 9253d316e79890aba0a28fde0371a1a7e169ebb8 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 12 Jul 2017 21:11:47 +0800 Subject: [PATCH 3/5] sdmmc: implement deselecting a card In some cases the card needs to be returned to standby mode from data transfer mode. This is done using CMD7 command, which does not receive any response in this case. --- components/sdmmc/sdmmc_cmd.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/components/sdmmc/sdmmc_cmd.c b/components/sdmmc/sdmmc_cmd.c index 65363d7d7..3cefe5ad9 100644 --- a/components/sdmmc/sdmmc_cmd.c +++ b/components/sdmmc/sdmmc_cmd.c @@ -43,7 +43,7 @@ static esp_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* static esp_err_t sdmmc_send_cmd_set_blocklen(sdmmc_card_t* card, sdmmc_csd_t* csd); static esp_err_t sdmmc_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_send_cmd_select_card(sdmmc_card_t* card); +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); @@ -164,7 +164,7 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card) card->csd.capacity = max_sdsc_capacity; } if (!is_spi) { - err = sdmmc_send_cmd_select_card(card); + err = sdmmc_send_cmd_select_card(card, card->rca); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: select_card returned 0x%x", __func__, err); return err; @@ -509,12 +509,14 @@ static esp_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_cs return sdmmc_decode_csd(ptr, out_csd); } -static esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card) +static esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card, uint32_t rca) { + /* Don't expect to see a response when de-selecting a card */ + uint32_t response = (rca == 0) ? 0 : SCF_RSP_R1; sdmmc_command_t cmd = { .opcode = MMC_SELECT_CARD, - .arg = MMC_ARG_RCA(card->rca), - .flags = SCF_CMD_AC | SCF_RSP_R1 + .arg = MMC_ARG_RCA(rca), + .flags = SCF_CMD_AC | response }; return sdmmc_send_cmd(card, &cmd); } From 6baf8195ee78b5ce95ff68d9a61243424c6b06f5 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 12 Jul 2017 21:17:28 +0800 Subject: [PATCH 4/5] sdmmc: add support for high speed (HS) mode By default SD cards are initialized in default speed (DS) mode. Enabling HS mode requires SWITCH_FUNC command to be sent twice: first time to query if the card supports switching to HS mode, second time to perform the switch. This change implements SWITCH_FUNC command and adds the procedure to switch to HS mode. --- components/driver/include/driver/sdmmc_defs.h | 35 +++- .../driver/include/driver/sdmmc_types.h | 7 + components/sdmmc/sdmmc_cmd.c | 184 ++++++++++++++++-- 3 files changed, 200 insertions(+), 26 deletions(-) diff --git a/components/driver/include/driver/sdmmc_defs.h b/components/driver/include/driver/sdmmc_defs.h index 30df7e2fc..c13df6d1a 100644 --- a/components/driver/include/driver/sdmmc_defs.h +++ b/components/driver/include/driver/sdmmc_defs.h @@ -294,15 +294,34 @@ #define SCR_CMD_SUPPORT_CMD20(scr) MMC_RSP_BITS((scr), 32, 1) #define SCR_RESERVED2(scr) MMC_RSP_BITS((scr), 0, 32) -/* Status of Switch Function */ -#define SFUNC_STATUS_GROUP(status, group) \ - (__bitfield((uint32_t *)(status), 400 + (group - 1) * 16, 16)) +/* Max supply current in SWITCH_FUNC response (in mA) */ +#define SD_SFUNC_I_MAX(status) (MMC_RSP_BITS((uint32_t *)(status), 496, 16)) -#define SD_ACCESS_MODE_SDR12 0 -#define SD_ACCESS_MODE_SDR25 1 -#define SD_ACCESS_MODE_SDR50 2 -#define SD_ACCESS_MODE_SDR104 3 -#define SD_ACCESS_MODE_DDR50 4 +/* Supported flags in SWITCH_FUNC response */ +#define SD_SFUNC_SUPPORTED(status, group) \ + (MMC_RSP_BITS((uint32_t *)(status), 400 + (group - 1) * 16, 16)) + +/* Selected function in SWITCH_FUNC response */ +#define SD_SFUNC_SELECTED(status, group) \ + (MMC_RSP_BITS((uint32_t *)(status), 376 + (group - 1) * 4, 4)) + +/* Busy flags in SWITCH_FUNC response */ +#define SD_SFUNC_BUSY(status, group) \ + (MMC_RSP_BITS((uint32_t *)(status), 272 + (group - 1) * 16, 16)) + +/* Version of SWITCH_FUNC response */ +#define SD_SFUNC_VER(status) (MMC_RSP_BITS((uint32_t *)(status), 368, 8)) + +#define SD_SFUNC_GROUP_MAX 6 +#define SD_SFUNC_FUNC_MAX 15 + +#define SD_ACCESS_MODE 1 /* Function group 1, Access Mode */ + +#define SD_ACCESS_MODE_SDR12 0 /* 25 MHz clock */ +#define SD_ACCESS_MODE_SDR25 1 /* 50 MHz clock */ +#define SD_ACCESS_MODE_SDR50 2 /* UHS-I, 100 MHz clock */ +#define SD_ACCESS_MODE_SDR104 3 /* UHS-I, 208 MHz clock */ +#define SD_ACCESS_MODE_DDR50 4 /* UHS-I, 50 MHz clock, DDR */ /** * @brief Extract up to 32 sequential bits from an array of 32-bit words diff --git a/components/driver/include/driver/sdmmc_types.h b/components/driver/include/driver/sdmmc_types.h index d9ffde3ff..4bc1e05e5 100644 --- a/components/driver/include/driver/sdmmc_types.h +++ b/components/driver/include/driver/sdmmc_types.h @@ -60,6 +60,13 @@ typedef struct { */ typedef uint32_t sdmmc_response_t[4]; +/** + * SD SWITCH_FUNC response buffer + */ +typedef struct { + uint32_t data[512 / 8 / sizeof(uint32_t)]; /*!< response data */ +} sdmmc_switch_func_rsp_t; + /** * SD/MMC command information */ diff --git a/components/sdmmc/sdmmc_cmd.c b/components/sdmmc/sdmmc_cmd.c index 3cefe5ad9..87da936e1 100644 --- a/components/sdmmc/sdmmc_cmd.c +++ b/components/sdmmc/sdmmc_cmd.c @@ -41,6 +41,10 @@ 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_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, + uint32_t mode, uint32_t group, uint32_t function, + sdmmc_switch_func_rsp_t* resp); +static esp_err_t sdmmc_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 sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd); static esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card, uint32_t rca); @@ -153,7 +157,7 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card) err = sdmmc_send_cmd_send_csd(card, &card->csd); if (err != ESP_OK) { - ESP_LOGE(TAG, "%s: send_csd returned 0x%x", __func__, err); + ESP_LOGE(TAG, "%s: send_csd (1) returned 0x%x", __func__, err); return err; } const size_t max_sdsc_capacity = UINT32_MAX / card->csd.sector_size + 1; @@ -214,32 +218,87 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card) ESP_LOGV(TAG, "waiting for card to become ready (%d)", count); } } - if (config->max_freq_khz >= SDMMC_FREQ_HIGHSPEED && - card->csd.tr_speed / 1000 >= 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"); + + /* 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) { + /* This will determine if the card supports SWITCH_FUNC command, + * and high speed mode. If the cards supports both, this will enable + * high speed mode at the card side. + */ + err = sdmmc_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) { + /* some other error */ return err; + } else { /* ESP_OK */ + /* HS mode has been enabled on the card. + * Read CSD again, it should now indicate that the card supports + * 50MHz clock. + * Since SEND_CSD is allowed only in standby mode, and the card is + * currently in data transfer more, deselect the card first, then + * get the CSD, then select the card again. + */ + err = sdmmc_send_cmd_select_card(card, 0); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: select_card (2) returned 0x%x", __func__, err); + return err; + } + err = sdmmc_send_cmd_send_csd(card, &card->csd); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: send_csd (2) returned 0x%x", __func__, err); + return err; + } + err = sdmmc_send_cmd_select_card(card, card->rca); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: select_card (3) returned 0x%x", __func__, err); + return err; + } + + if (card->csd.tr_speed != 50000000) { + ESP_LOGW(TAG, "unexpected: after enabling HS mode, tr_speed=%d", card->csd.tr_speed); + } else { + /* Finally can 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; + } } - } else if (config->max_freq_khz >= SDMMC_FREQ_DEFAULT && - card->csd.tr_speed / 1000 >= SDMMC_FREQ_DEFAULT) { + } + /* 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; } - sdmmc_scr_t scr_tmp; - err = sdmmc_send_cmd_send_scr(card, &scr_tmp); - if (err != ESP_OK) { - ESP_LOGE(TAG, "%s: send_scr 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; + /* 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) { + 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; + } } return ESP_OK; } @@ -777,3 +836,92 @@ static esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst, } return ESP_OK; } + +static esp_err_t sdmmc_send_cmd_switch_func(sdmmc_card_t* card, + uint32_t mode, uint32_t group, uint32_t function, + sdmmc_switch_func_rsp_t* resp) +{ + 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; + } + + if (group == 0 || + group > SD_SFUNC_GROUP_MAX || + function > SD_SFUNC_FUNC_MAX) { + return ESP_ERR_INVALID_ARG; + } + + if (mode > 1) { + return ESP_ERR_INVALID_ARG; + } + + uint32_t group_shift = (group - 1) << 2; + /* all functions which should not be affected are set to 0xf (no change) */ + uint32_t other_func_mask = (0x00ffffff & ~(0xf << group_shift)); + uint32_t func_val = (function << group_shift) | other_func_mask; + + sdmmc_command_t cmd = { + .opcode = MMC_SWITCH, + .flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1, + .blklen = sizeof(sdmmc_switch_func_rsp_t), + .data = resp->data, + .datalen = sizeof(sdmmc_switch_func_rsp_t), + .arg = (!!mode << 31) | func_val + }; + + esp_err_t err = sdmmc_send_cmd(card, &cmd); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err); + return err; + } + flip_byte_order(resp->data, sizeof(sdmmc_switch_func_rsp_t)); + uint32_t resp_ver = SD_SFUNC_VER(resp->data); + if (resp_ver == 0) { + /* busy response is never sent */ + } else if (resp_ver == 1) { + if (SD_SFUNC_BUSY(resp->data, group) & (1 << function)) { + ESP_LOGD(TAG, "%s: response indicates function %d:%d is busy", + __func__, group, function); + return ESP_ERR_INVALID_STATE; + } + } else { + ESP_LOGD(TAG, "%s: got an invalid version of SWITCH_FUNC response: 0x%02x", + __func__, resp_ver); + return ESP_ERR_INVALID_RESPONSE; + } + return ESP_OK; +} + +static esp_err_t sdmmc_enable_hs_mode(sdmmc_card_t* card) +{ + 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; + } + sdmmc_switch_func_rsp_t* response = (sdmmc_switch_func_rsp_t*) + heap_caps_malloc(sizeof(*response), MALLOC_CAP_DMA); + if (response == NULL) { + return ESP_ERR_NO_MEM; + } + + esp_err_t err = sdmmc_send_cmd_switch_func(card, 0, SD_ACCESS_MODE, 0, response); + if (err != ESP_OK) { + ESP_LOGD(TAG, "%s: sdmmc_send_cmd_switch_func (1) returned 0x%x", __func__, err); + 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; + } + +out: + free(response); + return err; +} From 14c96838bf584809300be121659524456b679998 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 12 Jul 2017 21:17:51 +0800 Subject: [PATCH 5/5] sdmmc: add more comments about card initialization steps --- components/sdmmc/sdmmc_cmd.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/components/sdmmc/sdmmc_cmd.c b/components/sdmmc/sdmmc_cmd.c index 87da936e1..2e8b0f85c 100644 --- a/components/sdmmc/sdmmc_cmd.c +++ b/components/sdmmc/sdmmc_cmd.c @@ -130,12 +130,15 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card) } } ESP_LOGD(TAG, "host_ocr=0x%x card_ocr=0x%x", host_ocr, card->ocr); + /* Clear all voltage bits in host's OCR which the card doesn't support. * Don't touch CCS bit because in SPI mode cards don't report CCS in ACMD41 * response. */ host_ocr &= (card->ocr | (~SD_OCR_VOL_MASK)); ESP_LOGD(TAG, "sdmmc_card_init: host_ocr=%08x, card_ocr=%08x", host_ocr, card->ocr); + + /* Read and decode the contents of CID register */ if (!is_spi) { err = sddmc_send_cmd_all_send_cid(card, &card->cid); if (err != ESP_OK) { @@ -155,6 +158,7 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card) } } + /* Get and decode the contents of CSD register. Determine card capacity. */ err = sdmmc_send_cmd_send_csd(card, &card->csd); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: send_csd (1) returned 0x%x", __func__, err); @@ -167,6 +171,11 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card) __func__, card->csd.capacity, max_sdsc_capacity); card->csd.capacity = max_sdsc_capacity; } + + /* Switch the card from stand-by mode to data transfer mode (not needed if + * SPI interface is used). This is needed to issue SET_BLOCKLEN and + * SEND_SCR commands. + */ if (!is_spi) { err = sdmmc_send_cmd_select_card(card, card->rca); if (err != ESP_OK) { @@ -174,6 +183,11 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card) return err; } } + + /* 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) { @@ -181,11 +195,21 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card) 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 returned 0x%x", __func__, err); + ESP_LOGE(TAG, "%s: send_scr (1) returned 0x%x", __func__, err); return err; } + + /* 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 ((config->flags & SDMMC_HOST_FLAG_4BIT) && (card->scr.bus_width & SCR_SD_BUS_WIDTHS_4BIT)) { ESP_LOGD(TAG, "switching to 4-bit bus mode"); @@ -206,6 +230,8 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card) return err; } } + + /* Wait for the card to be ready for data transfers */ uint32_t status = 0; while (!host_is_spi(card) && !(status & MMC_R1_READY_FOR_DATA)) { // TODO: add some timeout here