diff --git a/components/driver/include/driver/sdmmc_defs.h b/components/driver/include/driver/sdmmc_defs.h index 455d1c7aff..c13df6d1aa 100644 --- a/components/driver/include/driver/sdmmc_defs.h +++ b/components/driver/include/driver/sdmmc_defs.h @@ -294,23 +294,67 @@ #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 + * + * 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/include/driver/sdmmc_types.h b/components/driver/include/driver/sdmmc_types.h index d9ffde3ffd..4bc1e05e51 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/driver/sdmmc_transaction.c b/components/driver/sdmmc_transaction.c index c3a5827544..8f06be88e8 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; } @@ -261,11 +264,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 0784a279a1..2e8b0f85c6 100644 --- a/components/sdmmc/sdmmc_cmd.c +++ b/components/sdmmc/sdmmc_cmd.c @@ -41,9 +41,13 @@ 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); +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); @@ -51,7 +55,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, @@ -126,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) { @@ -151,9 +158,10 @@ 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 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; @@ -163,13 +171,23 @@ 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); + 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; } } + + /* 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) { @@ -177,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"); @@ -202,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 @@ -214,32 +244,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; } @@ -419,7 +504,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,18 +586,22 @@ 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) +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); } @@ -520,8 +609,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 +686,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; } } @@ -768,3 +862,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; +} diff --git a/components/sdmmc/test/test_sd.c b/components/sdmmc/test/test_sd.c index 86ff501b85..5602bd6084 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]") {