From 17b7981e2e56fd5aebeded090c863b980bd76000 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Sun, 10 Apr 2022 21:19:30 +0200 Subject: [PATCH 1/9] driver: sdmmc: add SPI mode R2 response definitions --- components/driver/include/driver/sdmmc_defs.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/components/driver/include/driver/sdmmc_defs.h b/components/driver/include/driver/sdmmc_defs.h index 1e336c2d72..9b9c5b8023 100644 --- a/components/driver/include/driver/sdmmc_defs.h +++ b/components/driver/include/driver/sdmmc_defs.h @@ -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]) From f1e3536f3c5ef1483c226c8543a28dd0977072d9 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Sun, 10 Apr 2022 21:20:04 +0200 Subject: [PATCH 2/9] driver: sdspi: fix decoding of R2 response --- components/driver/sdspi_transaction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/driver/sdspi_transaction.c b/components/driver/sdspi_transaction.c index 0d969dae13..3fdeb73a4e 100644 --- a/components/driver/sdspi_transaction.c +++ b/components/driver/sdspi_transaction.c @@ -156,7 +156,7 @@ esp_err_t sdspi_host_do_transaction(int slot, sdmmc_command_t *cmdinfo) 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]); From da47bebdd8036465c4fb1a9db5a92dc1279d583b Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Sun, 10 Apr 2022 21:21:05 +0200 Subject: [PATCH 3/9] sdmmc: sdmmc_init_sd_ssr: use correct command name Numerical value of MMC_SEND_STATUS is the same as SD_APP_SD_STATUS, so there is no functional change. Just making this consistent with the sdmmc_send_app_cmd call later on. --- components/sdmmc/sdmmc_sd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/sdmmc/sdmmc_sd.c b/components/sdmmc/sdmmc_sd.c index 03037e5c26..84147fc74d 100644 --- a/components/sdmmc/sdmmc_sd.c +++ b/components/sdmmc/sdmmc_sd.c @@ -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 }; From c4c7e7c82b0d592587b40bd81ba3629147ac5f25 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Sun, 10 Apr 2022 21:22:16 +0200 Subject: [PATCH 4/9] sdmmc: support sdmmc_send_cmd_send_status in SPI mode SPI mode uses different response format (2-byte R2) compared to SD mode. --- components/sdmmc/sdmmc_cmd.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/sdmmc/sdmmc_cmd.c b/components/sdmmc/sdmmc_cmd.c index 706badb1ae..9b3c7280b8 100644 --- a/components/sdmmc/sdmmc_cmd.c +++ b/components/sdmmc/sdmmc_cmd.c @@ -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; } From bb11f49c741cf5cf045ef915bd526de9699c863d Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Sun, 10 Apr 2022 21:23:57 +0200 Subject: [PATCH 5/9] sdmmc: check for errors reported by card in sdmmc_write_sectors_dma During write operation (CMD24 or CMD25), the card can report some of the errors in the 1-byte response tokens. Other types of errors are not reported, the host has to get them by issuing CMD13. This commit adds CMD13 request at the end of write operations and reports error to the user if the card status isn't zero. --- components/sdmmc/sdmmc_cmd.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/components/sdmmc/sdmmc_cmd.c b/components/sdmmc/sdmmc_cmd.c index 9b3c7280b8..6aaf7098f0 100644 --- a/components/sdmmc/sdmmc_cmd.c +++ b/components/sdmmc/sdmmc_cmd.c @@ -418,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); @@ -428,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; } From 3ad98984e9580c32da30e6525ff6e00a196c0a5f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Sun, 10 Apr 2022 21:45:01 +0200 Subject: [PATCH 6/9] driver: sdspi: add support for R1b response Same as R1 but with busy indication polling --- components/driver/sdspi_host.c | 12 ++++++++++-- components/driver/sdspi_private.h | 15 ++++++++------- components/driver/sdspi_transaction.c | 4 +++- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/components/driver/sdspi_host.c b/components/driver/sdspi_host.c index 5a4424b0bd..fd9749a33d 100644 --- a/components/driver/sdspi_host.c +++ b/components/driver/sdspi_host.c @@ -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; } diff --git a/components/driver/sdspi_private.h b/components/driver/sdspi_private.h index 2acf853bd1..e78756b4c2 100644 --- a/components/driver/sdspi_private.h +++ b/components/driver/sdspi_private.h @@ -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 diff --git a/components/driver/sdspi_transaction.c b/components/driver/sdspi_transaction.c index 3fdeb73a4e..583c6cfc35 100644 --- a/components/driver/sdspi_transaction.c +++ b/components/driver/sdspi_transaction.c @@ -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,7 +154,7 @@ 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) { From 36e3043306e74b545cdc5abb4395badfde28644e Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Sun, 10 Apr 2022 21:46:08 +0200 Subject: [PATCH 7/9] sdmmc: check card status in SPI mode after sdmmc_erase_sectors Same as for write operation, some errors are reported only via CMD13. Without the R1b response support in sdspi driver, this check would fail. Now that R1b support is implemented, erase command response is zero (success) on all cards under test. Also remove the now-unnecessary card reset after erase in the test case. --- components/sdmmc/sdmmc_cmd.c | 14 ++++++++++++++ components/sdmmc/test/test_sd.c | 4 ---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/components/sdmmc/sdmmc_cmd.c b/components/sdmmc/sdmmc_cmd.c index 6aaf7098f0..9b08c3751b 100644 --- a/components/sdmmc/sdmmc_cmd.c +++ b/components/sdmmc/sdmmc_cmd.c @@ -600,6 +600,20 @@ esp_err_t sdmmc_erase_sectors(sdmmc_card_t* card, size_t start_sector, ESP_LOGE(TAG, "%s: sdmmc_send_cmd 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; } diff --git a/components/sdmmc/test/test_sd.c b/components/sdmmc/test/test_sd.c index 0feaf0f231..1054efe333 100644 --- a/components/sdmmc/test/test_sd.c +++ b/components/sdmmc/test/test_sd.c @@ -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. From a28828a6f4e6c45038274f69502b746b810062c2 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Sun, 10 Apr 2022 21:47:39 +0200 Subject: [PATCH 8/9] fatfs: re-enable TRIM support for SPI mode Previously this didn't work because MMC_ERASE implementation for SPI didn't wait for the busy status indication. --- components/fatfs/diskio/diskio_sdmmc.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/components/fatfs/diskio/diskio_sdmmc.c b/components/fatfs/diskio/diskio_sdmmc.c index 19f6d5698c..97578c9f0e 100644 --- a/components/fatfs/diskio/diskio_sdmmc.c +++ b/components/fatfs/diskio/diskio_sdmmc.c @@ -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 From 79659e3096d9d4e85ffc4bf302ca52ea04586c3f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 30 May 2022 15:37:40 +0200 Subject: [PATCH 9/9] sdmmc: better calculation of erase timeout Previous version of the code used a fixed constant (500 ms) for the erase timeout and added 1 ms for each sector erased. This commit improves timeouts calculation: - For SD cards, check if erase timeout information is present in the SSR register. If yes, use it for erase timeout calculation. Otherwise assume 250ms per erase block, same as Linux does. - For eMMC assume 250ms per erase block (but no less than 1 second). This has to be improved later to use the erase timeout info in the extended CSD register. --- components/driver/include/driver/sdmmc_defs.h | 3 ++ .../driver/include/driver/sdmmc_types.h | 6 ++- components/sdmmc/sdmmc_cmd.c | 15 +++---- components/sdmmc/sdmmc_common.c | 9 ++++ components/sdmmc/sdmmc_common.h | 7 ++- components/sdmmc/sdmmc_mmc.c | 10 +++++ components/sdmmc/sdmmc_sd.c | 45 +++++++++++++++++++ 7 files changed, 85 insertions(+), 10 deletions(-) diff --git a/components/driver/include/driver/sdmmc_defs.h b/components/driver/include/driver/sdmmc_defs.h index 9b9c5b8023..04ba136d21 100644 --- a/components/driver/include/driver/sdmmc_defs.h +++ b/components/driver/include/driver/sdmmc_defs.h @@ -372,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) diff --git a/components/driver/include/driver/sdmmc_types.h b/components/driver/include/driver/sdmmc_types.h index 58ccfd4468..374790ef4a 100644 --- a/components/driver/include/driver/sdmmc_types.h +++ b/components/driver/include/driver/sdmmc_types.h @@ -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; /** diff --git a/components/sdmmc/sdmmc_cmd.c b/components/sdmmc/sdmmc_cmd.c index 9b08c3751b..139d0ed0cd 100644 --- a/components/sdmmc/sdmmc_cmd.c +++ b/components/sdmmc/sdmmc_cmd.c @@ -572,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; } @@ -582,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; } @@ -591,13 +591,11 @@ 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; } @@ -684,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; } diff --git a/components/sdmmc/sdmmc_common.c b/components/sdmmc/sdmmc_common.c index 0d04ddbb26..341a0f4a9c 100644 --- a/components/sdmmc/sdmmc_common.c +++ b/components/sdmmc/sdmmc_common.c @@ -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); + } +} diff --git a/components/sdmmc/sdmmc_common.h b/components/sdmmc/sdmmc_common.h index a6d7dbbf29..759151e14c 100644 --- a/components/sdmmc/sdmmc_common.h +++ b/components/sdmmc/sdmmc_common.h @@ -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); diff --git a/components/sdmmc/sdmmc_mmc.c b/components/sdmmc/sdmmc_mmc.c index 82c978aea6..4f726ea90a 100644 --- a/components/sdmmc/sdmmc_mmc.c +++ b/components/sdmmc/sdmmc_mmc.c @@ -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; +} diff --git a/components/sdmmc/sdmmc_sd.c b/components/sdmmc/sdmmc_sd.c index 84147fc74d..15bc866357 100644 --- a/components/sdmmc/sdmmc_sd.c +++ b/components/sdmmc/sdmmc_sd.c @@ -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; + } +}