Merge branch 'bugfix/spi_mode_status_check' into 'master'

sdmmc, sdspi: fixes related to status checks, R1b response support, erase fix for SPI mode, fix for erase timeout calculation

Closes IDF-4728

See merge request espressif/esp-idf!17727
This commit is contained in:
Martin Vychodil 2022-06-10 13:15:47 +08:00
commit 16a4ee7c36
12 changed files with 163 additions and 34 deletions

View File

@ -119,6 +119,20 @@
#define SDIO_R1_FUNC_NUM_ERR (1<<4)
/* SPI mode R2 response type bits.
* The first byte is the same as for R1.
* The bits below belong to the second byte.
* Bits 10, 11, 12, 15 can also be reported in data error token of a read command response.
*/
#define SD_SPI_R2_CARD_LOCKED (1<<8) /* Set when the card is locked by the user */
#define SD_SPI_R2_UNLOCK_FAILED (1<<9) /* Host attempts to erase a write-protected sector or makes an error during card lock/unlock operation */
#define SD_SPI_R2_ERROR (1<<10) /* A general or an unknown error occurred during the operation */
#define SD_SPI_R2_CC_ERROR (1<<11) /* Internal card controller error */
#define SD_SPI_R2_ECC_FAILED (1<<12) /* Card internal ECC was applied but failed to correct the data */
#define SD_SPI_R2_WP_VIOLATION (1<<13) /* The command tried to write a write-protected block */
#define SD_SPI_R2_ERASE_PARAM (1<<14) /* An invalid selection for erase, sectors or groups */
#define SD_SPI_R2_OUT_OF_RANGE (1<<15) /* The command argument was out of the allowed range for this card */
/* 48-bit response decoding (32 bits w/o CRC) */
#define MMC_R1(resp) ((resp)[0])
#define MMC_R3(resp) ((resp)[0])
@ -358,6 +372,9 @@
/* SSR (SD Status Register) */
#define SSR_DAT_BUS_WIDTH(ssr) MMC_RSP_BITS((ssr), 510, 2)
#define SSR_AU_SIZE(ssr) MMC_RSP_BITS((ssr), 428, 4)
#define SSR_ERASE_SIZE(ssr) MMC_RSP_BITS((ssr), 408, 16)
#define SSR_ERASE_TIMEOUT(ssr) MMC_RSP_BITS((ssr), 402, 6)
#define SSR_ERASE_OFFSET(ssr) MMC_RSP_BITS((ssr), 400, 2)
#define SSR_DISCARD_SUPPORT(ssr) MMC_RSP_BITS((ssr), 313, 1)
#define SSR_FULE_SUPPORT(ssr) MMC_RSP_BITS((ssr), 312, 1)

View File

@ -71,10 +71,14 @@ typedef struct {
* Note: When new member is added, update reserved bits accordingly
*/
typedef struct {
uint32_t alloc_unit_kb: 16; /*!< Allocation unit of the card, in multiples of kB (1024 bytes) */
uint32_t erase_size_au: 16; /*!< Erase size for the purpose of timeout calculation, in multiples of allocation unit */
uint32_t cur_bus_width: 2; /*!< SD current bus width */
uint32_t discard_support: 1; /*!< SD discard feature support */
uint32_t fule_support: 1; /*!< SD FULE (Full User Area Logical Erase) feature support */
uint32_t reserved: 28; /*!< reserved for future expansion */
uint32_t erase_timeout: 6; /*!< Timeout (in seconds) for erase of a single allocation unit */
uint32_t erase_offset: 2; /*!< Constant timeout offset (in seconds) for any erase operation */
uint32_t reserved: 20; /*!< reserved for future expansion */
} sdmmc_ssr_t;
/**

View File

@ -482,7 +482,8 @@ static esp_err_t start_command_default(slot_info_t *slot, int flags, sdspi_hw_cm
{
size_t cmd_size = SDSPI_CMD_R1_SIZE;
if ((flags & SDSPI_CMD_FLAG_RSP_R1) ||
(flags & SDSPI_CMD_FLAG_NORSP)) {
(flags & SDSPI_CMD_FLAG_NORSP) ||
(flags & SDSPI_CMD_FLAG_RSP_R1B )) {
cmd_size = SDSPI_CMD_R1_SIZE;
} else if (flags & SDSPI_CMD_FLAG_RSP_R2) {
cmd_size = SDSPI_CMD_R2_SIZE;
@ -522,6 +523,13 @@ static esp_err_t start_command_default(slot_info_t *slot, int flags, sdspi_hw_cm
ret = shift_cmd_response(cmd, cmd_size);
if (ret != ESP_OK) return ESP_ERR_TIMEOUT;
if (flags & SDSPI_CMD_FLAG_RSP_R1B) {
ret = poll_busy(slot, cmd->timeout_ms, no_use_polling);
if (ret != ESP_OK) {
return ret;
}
}
return ESP_OK;
}
@ -777,7 +785,7 @@ static esp_err_t start_command_read_blocks(slot_info_t *slot, sdspi_hw_cmd_t *cm
// card to process it
sdspi_hw_cmd_t stop_cmd;
make_hw_cmd(MMC_STOP_TRANSMISSION, 0, cmd->timeout_ms, &stop_cmd);
ret = start_command_default(slot, SDSPI_CMD_FLAG_RSP_R1, &stop_cmd);
ret = start_command_default(slot, SDSPI_CMD_FLAG_RSP_R1B, &stop_cmd);
if (ret != ESP_OK) {
return ret;
}

View File

@ -85,13 +85,14 @@ typedef struct {
#define SDSPI_CMD_FLAG_DATA BIT(0) //!< Command has data transfer
#define SDSPI_CMD_FLAG_WRITE BIT(1) //!< Data is written to the card
#define SDSPI_CMD_FLAG_RSP_R1 BIT(2) //!< Response format R1 (1 byte)
#define SDSPI_CMD_FLAG_RSP_R2 BIT(3) //!< Response format R2 (2 bytes)
#define SDSPI_CMD_FLAG_RSP_R3 BIT(4) //!< Response format R3 (5 bytes)
#define SDSPI_CMD_FLAG_RSP_R4 BIT(5) //!< Response format R4 (5 bytes)
#define SDSPI_CMD_FLAG_RSP_R5 BIT(6) //!< Response format R5 (2 bytes)
#define SDSPI_CMD_FLAG_RSP_R7 BIT(7) //!< Response format R7 (5 bytes)
#define SDSPI_CMD_FLAG_NORSP BIT(8) //!< Don't expect response (used when sending CMD0 first time).
#define SDSPI_CMD_FLAG_MULTI_BLK BIT(9) //!< For the write multiblock commands, the start token should be different
#define SDSPI_CMD_FLAG_RSP_R1B BIT(3) //!< Response format R1 (1 byte), with busy polling
#define SDSPI_CMD_FLAG_RSP_R2 BIT(4) //!< Response format R2 (2 bytes)
#define SDSPI_CMD_FLAG_RSP_R3 BIT(5) //!< Response format R3 (5 bytes)
#define SDSPI_CMD_FLAG_RSP_R4 BIT(6) //!< Response format R4 (5 bytes)
#define SDSPI_CMD_FLAG_RSP_R5 BIT(7) //!< Response format R5 (2 bytes)
#define SDSPI_CMD_FLAG_RSP_R7 BIT(8) //!< Response format R7 (5 bytes)
#define SDSPI_CMD_FLAG_NORSP BIT(9) //!< Don't expect response (used when sending CMD0 first time).
#define SDSPI_CMD_FLAG_MULTI_BLK BIT(10) //!< For the write multiblock commands, the start token should be different
#define SDSPI_MAX_DATA_LEN 512 //!< Max size of single block transfer

View File

@ -140,6 +140,8 @@ esp_err_t sdspi_host_do_transaction(int slot, sdmmc_command_t *cmdinfo)
if (cmdinfo->arg & SD_ARG_CMD53_WRITE) flags |= SDSPI_CMD_FLAG_WRITE;
// The CMD53 can assign block mode in the arg when the length is exactly 512 bytes
if (cmdinfo->arg & SD_ARG_CMD53_BLOCK_MODE) flags |= SDSPI_CMD_FLAG_MULTI_BLK;
} else if (!s_app_cmd && (cmdinfo->opcode == MMC_ERASE || cmdinfo->opcode == MMC_STOP_TRANSMISSION)) {
flags |= SDSPI_CMD_FLAG_RSP_R1B;
} else {
flags |= SDSPI_CMD_FLAG_RSP_R1;
}
@ -152,11 +154,11 @@ esp_err_t sdspi_host_do_transaction(int slot, sdmmc_command_t *cmdinfo)
if (ret == ESP_OK) {
ESP_LOGV(TAG, "r1 = 0x%02x hw_cmd.r[0]=0x%08x", hw_cmd.r1, hw_cmd.response[0]);
// Some errors should be reported using return code
if (flags & SDSPI_CMD_FLAG_RSP_R1) {
if (flags & (SDSPI_CMD_FLAG_RSP_R1 | SDSPI_CMD_FLAG_RSP_R1B)) {
cmdinfo->response[0] = hw_cmd.r1;
r1_response_to_err(hw_cmd.r1, cmdinfo->opcode, &ret);
} else if (flags & SDSPI_CMD_FLAG_RSP_R2) {
cmdinfo->response[0] = (((uint32_t)hw_cmd.r1) << 8) | (hw_cmd.response[0] >> 24);
cmdinfo->response[0] = ((uint32_t)hw_cmd.r1) | ((hw_cmd.response[0] & 0xff) << 8);
} else if (flags & (SDSPI_CMD_FLAG_RSP_R3 | SDSPI_CMD_FLAG_RSP_R7)) {
r1_response_to_err(hw_cmd.r1, cmdinfo->opcode, &ret);
cmdinfo->response[0] = __builtin_bswap32(hw_cmd.response[0]);

View File

@ -101,13 +101,6 @@ DRESULT ff_sdmmc_ioctl (BYTE pdrv, BYTE cmd, void* buff)
return RES_ERROR;
#if FF_USE_TRIM
case CTRL_TRIM:
/*
* limitation with sector erase when used in SDSPI mode
* hence return if host is SPI.
*/
if ((card->host.flags & SDMMC_HOST_FLAG_SPI) != 0) {
return RES_ERROR;
}
return ff_sdmmc_trim (pdrv, *((DWORD*)buff), //start_sector
(*((DWORD*)buff + 1) - *((DWORD*)buff) + 1)); //sector_count
#endif //FF_USE_TRIM

View File

@ -347,7 +347,11 @@ esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status)
return err;
}
if (out_status) {
*out_status = MMC_R1(cmd.response);
if (host_is_spi(card)) {
*out_status = SD_SPI_R2(cmd.response);
} else {
*out_status = MMC_R1(cmd.response);
}
}
return ESP_OK;
}
@ -414,6 +418,7 @@ esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src,
}
uint32_t status = 0;
size_t count = 0;
/* SD mode: wait for the card to become idle based on R1 status */
while (!host_is_spi(card) && !(status & MMC_R1_READY_FOR_DATA)) {
// TODO: add some timeout here
err = sdmmc_send_cmd_send_status(card, &status);
@ -424,6 +429,27 @@ esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src,
ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
}
}
/* SPI mode: although card busy indication is based on the busy token,
* SD spec recommends that the host checks the results of programming by sending
* SEND_STATUS command. Some of the conditions reported in SEND_STATUS are not
* reported via a data error token.
*/
if (host_is_spi(card)) {
err = sdmmc_send_cmd_send_status(card, &status);
if (err != ESP_OK) {
return err;
}
if (status & SD_SPI_R2_CARD_LOCKED) {
ESP_LOGE(TAG, "%s: write failed, card is locked: r2=0x%04x",
__func__, status);
return ESP_ERR_INVALID_STATE;
}
if (status != 0) {
ESP_LOGE(TAG, "%s: card status indicates an error after write operation: r2=0x%04x",
__func__, status);
return ESP_ERR_INVALID_RESPONSE;
}
}
return ESP_OK;
}
@ -546,7 +572,7 @@ esp_err_t sdmmc_erase_sectors(sdmmc_card_t* card, size_t start_sector,
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);
ESP_LOGE(TAG, "%s: sdmmc_send_cmd (ERASE_GROUP_START) returned 0x%x", __func__, err);
return err;
}
@ -556,7 +582,7 @@ esp_err_t sdmmc_erase_sectors(sdmmc_card_t* card, size_t start_sector,
err = sdmmc_send_cmd(card, &cmd);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
ESP_LOGE(TAG, "%s: sdmmc_send_cmd (ERASE_GROUP_END) returned 0x%x", __func__, err);
return err;
}
@ -565,15 +591,27 @@ esp_err_t sdmmc_erase_sectors(sdmmc_card_t* card, size_t start_sector,
cmd.flags = SCF_CMD_AC | SCF_RSP_R1B | SCF_WAIT_BUSY;
cmd.opcode = MMC_ERASE;
cmd.arg = cmd38_arg;
// TODO: best way, application to compute timeout value. For this card
// structure should have a place holder for erase_timeout.
cmd.timeout_ms = (SDMMC_ERASE_BLOCK_TIMEOUT_MS + sector_count);
cmd.timeout_ms = sdmmc_get_erase_timeout_ms(card, cmd38_arg, sector_count * card->csd.sector_size / 1024);
err = sdmmc_send_cmd(card, &cmd);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
ESP_LOGE(TAG, "%s: sdmmc_send_cmd (ERASE) returned 0x%x", __func__, err);
return err;
}
if (host_is_spi(card)) {
uint32_t status;
err = sdmmc_send_cmd_send_status(card, &status);
if (err != ESP_OK) {
return err;
}
if (status != 0) {
ESP_LOGE(TAG, "%s: card status indicates an error after erase operation: r2=0x%04x",
__func__, status);
return ESP_ERR_INVALID_RESPONSE;
}
}
return ESP_OK;
}
@ -644,9 +682,10 @@ esp_err_t sdmmc_full_erase(sdmmc_card_t* card)
if (card->is_mmc) {
arg = sdmmc_mmc_can_sanitize(card) == ESP_OK ? SDMMC_MMC_DISCARD_ARG: SDMMC_MMC_TRIM_ARG;
}
err = sdmmc_erase_sectors(card, 0, card->csd.capacity, arg);
err = sdmmc_erase_sectors(card, 0, card->csd.capacity, arg);
if ((err == ESP_OK) && (arg == SDMMC_MMC_DISCARD_ARG)) {
return sdmmc_mmc_sanitize(card, SDMMC_ERASE_BLOCK_TIMEOUT_MS + card->csd.capacity);
uint32_t timeout_ms = sdmmc_get_erase_timeout_ms(card, SDMMC_MMC_DISCARD_ARG, card->csd.capacity * ((uint64_t) card->csd.sector_size) / 1024);
return sdmmc_mmc_sanitize(card, timeout_ms);
}
return err;
}

View File

@ -318,3 +318,12 @@ esp_err_t sdmmc_fix_host_flags(sdmmc_card_t* card)
}
return ESP_OK;
}
uint32_t sdmmc_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb)
{
if (card->is_mmc) {
return sdmmc_mmc_get_erase_timeout_ms(card, arg, erase_size_kb);
} else {
return sdmmc_sd_get_erase_timeout_ms(card, arg, erase_size_kb);
}
}

View File

@ -37,7 +37,9 @@
*/
#define SDMMC_DEFAULT_CMD_TIMEOUT_MS 1000 // Max timeout of ordinary commands
#define SDMMC_WRITE_CMD_TIMEOUT_MS 5000 // Max timeout of write commands
#define SDMMC_ERASE_BLOCK_TIMEOUT_MS 500 // Max timeout of erase per block
#define SDMMC_SD_DISCARD_TIMEOUT 250 // SD erase (discard) timeout
/* Maximum retry/error count for SEND_OP_COND (CMD1).
* These are somewhat arbitrary, values originate from OpenBSD driver.
@ -79,6 +81,7 @@ esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src,
size_t start_block, size_t block_count);
esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
size_t start_block, size_t block_count);
uint32_t sdmmc_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb);
/* SD specific */
esp_err_t sdmmc_check_scr(sdmmc_card_t* card);
@ -86,6 +89,7 @@ esp_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid);
esp_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd);
esp_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr);
esp_err_t sdmmc_decode_ssr(uint32_t *raw_ssr, sdmmc_ssr_t* out_ssr);
uint32_t sdmmc_sd_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb);
/* SDIO specific */
esp_err_t sdmmc_io_reset(sdmmc_card_t* card);
@ -103,6 +107,7 @@ esp_err_t sdmmc_mmc_switch(sdmmc_card_t* card, uint8_t set, uint8_t index, uint8
esp_err_t sdmmc_mmc_decode_cid(int mmc_ver, sdmmc_response_t resp, sdmmc_cid_t* out_cid);
esp_err_t sdmmc_mmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd);
esp_err_t sdmmc_mmc_enable_hs_mode(sdmmc_card_t* card);
uint32_t sdmmc_mmc_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb);
/* Parts of card initialization flow */
esp_err_t sdmmc_init_sd_if_cond(sdmmc_card_t* card);

View File

@ -288,3 +288,13 @@ out:
free(ext_csd);
return err;
}
uint32_t sdmmc_mmc_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb)
{
/* TODO: calculate erase timeout based on ext_csd (trim_timeout) */
uint32_t timeout_ms = SDMMC_SD_DISCARD_TIMEOUT * erase_size_kb / card->csd.sector_size;
timeout_ms = MAX(1000, timeout_ms);
ESP_LOGD(TAG, "%s: erase timeout %u s (erasing %u kB, %ums per sector)",
__func__, timeout_ms / 1000, erase_size_kb, SDMMC_SD_DISCARD_TIMEOUT);
return timeout_ms;
}

View File

@ -96,7 +96,7 @@ esp_err_t sdmmc_init_sd_ssr(sdmmc_card_t* card)
.data = sd_ssr,
.datalen = SD_SSR_SIZE,
.blklen = SD_SSR_SIZE,
.opcode = MMC_SEND_STATUS,
.opcode = SD_APP_SD_STATUS,
.arg = 0,
.flags = SCF_CMD_ADTC | SCF_RSP_R1 | SCF_CMD_READ
};
@ -387,6 +387,16 @@ esp_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr)
return ESP_OK;
}
static const uint32_t s_au_to_size_kb[] = {
0, 16, 32, 64,
128, 256, 512, 1024,
2 * 1024, 4 * 1024,
8 * 1024, 12 * 1024,
16 * 1024, 24 * 1024,
32 * 1024, 64 * 1024
};
_Static_assert(sizeof(s_au_to_size_kb)/sizeof(s_au_to_size_kb[0]) == 16, "invalid number of elements in s_au_to_size_kb");
esp_err_t sdmmc_decode_ssr(uint32_t *raw_ssr, sdmmc_ssr_t* out_ssr)
{
uint32_t ssr[(SD_SSR_SIZE/sizeof(uint32_t))] = { 0 };
@ -399,6 +409,41 @@ esp_err_t sdmmc_decode_ssr(uint32_t *raw_ssr, sdmmc_ssr_t* out_ssr)
out_ssr->cur_bus_width = SSR_DAT_BUS_WIDTH(ssr);
out_ssr->discard_support = SSR_DISCARD_SUPPORT(ssr);
out_ssr->fule_support = SSR_FULE_SUPPORT(ssr);
uint32_t au = SSR_AU_SIZE(ssr);
out_ssr->alloc_unit_kb = s_au_to_size_kb[au];
out_ssr->erase_timeout = SSR_ERASE_TIMEOUT(ssr);
out_ssr->erase_size_au = SSR_ERASE_SIZE(ssr);
out_ssr->erase_offset = SSR_ERASE_OFFSET(ssr);
return ESP_OK;
}
uint32_t sdmmc_sd_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb)
{
if (arg == SDMMC_SD_DISCARD_ARG) {
return SDMMC_SD_DISCARD_TIMEOUT;
} else if (arg == SDMMC_SD_ERASE_ARG) {
if (card->ssr.alloc_unit_kb != 0 &&
card->ssr.erase_size_au != 0 &&
card->ssr.erase_timeout != 0 &&
card->ssr.erase_offset != 0) {
/* Card supports erase timeout estimation. See the erase timeout equation in SD spec. */
uint32_t timeout_sec = card->ssr.erase_offset +
card->ssr.erase_timeout * (erase_size_kb + card->ssr.alloc_unit_kb - 1) /
(card->ssr.erase_size_au * card->ssr.alloc_unit_kb);
ESP_LOGD(TAG, "%s: erase timeout %u s (erasing %u kB, ES=%u, ET=%u, EO=%u, AU=%u kB)",
__func__, timeout_sec, erase_size_kb, card->ssr.erase_size_au,
card->ssr.erase_timeout, card->ssr.erase_offset, card->ssr.alloc_unit_kb);
return timeout_sec * 1000;
} else {
uint32_t timeout_ms = SDMMC_SD_DISCARD_TIMEOUT * erase_size_kb / card->csd.sector_size;
timeout_ms = MAX(1000, timeout_ms);
ESP_LOGD(TAG, "%s: erase timeout %u s (erasing %u kB, %ums per sector)",
__func__, timeout_ms / 1000, erase_size_kb, SDMMC_SD_DISCARD_TIMEOUT);
return timeout_ms;
}
} else {
assert(false && "unexpected SD erase argument");
return 0;
}
}

View File

@ -869,10 +869,6 @@ static void test_sdspi_erase_blocks(size_t start_block, size_t block_count)
float time_er = 1e3f * (t_stop_wr.tv_sec - t_start_er.tv_sec) + 1e-3f * (t_stop_wr.tv_usec - t_start_er.tv_usec);
printf("Erase duration: %.2fms\n", time_er);
// nominal delay before re-init card
vTaskDelay(pdMS_TO_TICKS(1000));
// has to re-init card, after erase operation.
TEST_ESP_OK(sdmmc_card_init(&config, card));
printf("Verifying erase state...\n");
uint8_t erase_mem_byte = 0xFF;
// ensure all the blocks are erased and are up to after erase state.