mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
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:
commit
16a4ee7c36
@ -119,6 +119,20 @@
|
|||||||
|
|
||||||
#define SDIO_R1_FUNC_NUM_ERR (1<<4)
|
#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) */
|
/* 48-bit response decoding (32 bits w/o CRC) */
|
||||||
#define MMC_R1(resp) ((resp)[0])
|
#define MMC_R1(resp) ((resp)[0])
|
||||||
#define MMC_R3(resp) ((resp)[0])
|
#define MMC_R3(resp) ((resp)[0])
|
||||||
@ -358,6 +372,9 @@
|
|||||||
/* SSR (SD Status Register) */
|
/* SSR (SD Status Register) */
|
||||||
#define SSR_DAT_BUS_WIDTH(ssr) MMC_RSP_BITS((ssr), 510, 2)
|
#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_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_DISCARD_SUPPORT(ssr) MMC_RSP_BITS((ssr), 313, 1)
|
||||||
#define SSR_FULE_SUPPORT(ssr) MMC_RSP_BITS((ssr), 312, 1)
|
#define SSR_FULE_SUPPORT(ssr) MMC_RSP_BITS((ssr), 312, 1)
|
||||||
|
|
||||||
|
@ -71,10 +71,14 @@ typedef struct {
|
|||||||
* Note: When new member is added, update reserved bits accordingly
|
* Note: When new member is added, update reserved bits accordingly
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
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 cur_bus_width: 2; /*!< SD current bus width */
|
||||||
uint32_t discard_support: 1; /*!< SD discard feature support */
|
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 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;
|
} sdmmc_ssr_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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;
|
size_t cmd_size = SDSPI_CMD_R1_SIZE;
|
||||||
if ((flags & SDSPI_CMD_FLAG_RSP_R1) ||
|
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;
|
cmd_size = SDSPI_CMD_R1_SIZE;
|
||||||
} else if (flags & SDSPI_CMD_FLAG_RSP_R2) {
|
} else if (flags & SDSPI_CMD_FLAG_RSP_R2) {
|
||||||
cmd_size = SDSPI_CMD_R2_SIZE;
|
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);
|
ret = shift_cmd_response(cmd, cmd_size);
|
||||||
if (ret != ESP_OK) return ESP_ERR_TIMEOUT;
|
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;
|
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
|
// card to process it
|
||||||
sdspi_hw_cmd_t stop_cmd;
|
sdspi_hw_cmd_t stop_cmd;
|
||||||
make_hw_cmd(MMC_STOP_TRANSMISSION, 0, cmd->timeout_ms, &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) {
|
if (ret != ESP_OK) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -85,13 +85,14 @@ typedef struct {
|
|||||||
#define SDSPI_CMD_FLAG_DATA BIT(0) //!< Command has data transfer
|
#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_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_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_R1B BIT(3) //!< Response format R1 (1 byte), with busy polling
|
||||||
#define SDSPI_CMD_FLAG_RSP_R3 BIT(4) //!< Response format R3 (5 bytes)
|
#define SDSPI_CMD_FLAG_RSP_R2 BIT(4) //!< Response format R2 (2 bytes)
|
||||||
#define SDSPI_CMD_FLAG_RSP_R4 BIT(5) //!< Response format R4 (5 bytes)
|
#define SDSPI_CMD_FLAG_RSP_R3 BIT(5) //!< Response format R3 (5 bytes)
|
||||||
#define SDSPI_CMD_FLAG_RSP_R5 BIT(6) //!< Response format R5 (2 bytes)
|
#define SDSPI_CMD_FLAG_RSP_R4 BIT(6) //!< Response format R4 (5 bytes)
|
||||||
#define SDSPI_CMD_FLAG_RSP_R7 BIT(7) //!< Response format R7 (5 bytes)
|
#define SDSPI_CMD_FLAG_RSP_R5 BIT(7) //!< Response format R5 (2 bytes)
|
||||||
#define SDSPI_CMD_FLAG_NORSP BIT(8) //!< Don't expect response (used when sending CMD0 first time).
|
#define SDSPI_CMD_FLAG_RSP_R7 BIT(8) //!< Response format R7 (5 bytes)
|
||||||
#define SDSPI_CMD_FLAG_MULTI_BLK BIT(9) //!< For the write multiblock commands, the start token should be different
|
#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
|
#define SDSPI_MAX_DATA_LEN 512 //!< Max size of single block transfer
|
||||||
|
|
||||||
|
@ -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;
|
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
|
// 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;
|
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 {
|
} else {
|
||||||
flags |= SDSPI_CMD_FLAG_RSP_R1;
|
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) {
|
if (ret == ESP_OK) {
|
||||||
ESP_LOGV(TAG, "r1 = 0x%02x hw_cmd.r[0]=0x%08x", hw_cmd.r1, hw_cmd.response[0]);
|
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
|
// 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;
|
cmdinfo->response[0] = hw_cmd.r1;
|
||||||
r1_response_to_err(hw_cmd.r1, cmdinfo->opcode, &ret);
|
r1_response_to_err(hw_cmd.r1, cmdinfo->opcode, &ret);
|
||||||
} else if (flags & SDSPI_CMD_FLAG_RSP_R2) {
|
} 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)) {
|
} else if (flags & (SDSPI_CMD_FLAG_RSP_R3 | SDSPI_CMD_FLAG_RSP_R7)) {
|
||||||
r1_response_to_err(hw_cmd.r1, cmdinfo->opcode, &ret);
|
r1_response_to_err(hw_cmd.r1, cmdinfo->opcode, &ret);
|
||||||
cmdinfo->response[0] = __builtin_bswap32(hw_cmd.response[0]);
|
cmdinfo->response[0] = __builtin_bswap32(hw_cmd.response[0]);
|
||||||
|
@ -101,13 +101,6 @@ DRESULT ff_sdmmc_ioctl (BYTE pdrv, BYTE cmd, void* buff)
|
|||||||
return RES_ERROR;
|
return RES_ERROR;
|
||||||
#if FF_USE_TRIM
|
#if FF_USE_TRIM
|
||||||
case CTRL_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
|
return ff_sdmmc_trim (pdrv, *((DWORD*)buff), //start_sector
|
||||||
(*((DWORD*)buff + 1) - *((DWORD*)buff) + 1)); //sector_count
|
(*((DWORD*)buff + 1) - *((DWORD*)buff) + 1)); //sector_count
|
||||||
#endif //FF_USE_TRIM
|
#endif //FF_USE_TRIM
|
||||||
|
@ -347,7 +347,11 @@ esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
if (out_status) {
|
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;
|
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;
|
uint32_t status = 0;
|
||||||
size_t count = 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)) {
|
while (!host_is_spi(card) && !(status & MMC_R1_READY_FOR_DATA)) {
|
||||||
// TODO: add some timeout here
|
// TODO: add some timeout here
|
||||||
err = sdmmc_send_cmd_send_status(card, &status);
|
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);
|
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;
|
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);
|
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||||
if (err != ESP_OK) {
|
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;
|
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);
|
err = sdmmc_send_cmd(card, &cmd);
|
||||||
if (err != ESP_OK) {
|
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;
|
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.flags = SCF_CMD_AC | SCF_RSP_R1B | SCF_WAIT_BUSY;
|
||||||
cmd.opcode = MMC_ERASE;
|
cmd.opcode = MMC_ERASE;
|
||||||
cmd.arg = cmd38_arg;
|
cmd.arg = cmd38_arg;
|
||||||
// TODO: best way, application to compute timeout value. For this card
|
cmd.timeout_ms = sdmmc_get_erase_timeout_ms(card, cmd38_arg, sector_count * card->csd.sector_size / 1024);
|
||||||
// structure should have a place holder for erase_timeout.
|
|
||||||
cmd.timeout_ms = (SDMMC_ERASE_BLOCK_TIMEOUT_MS + sector_count);
|
|
||||||
|
|
||||||
err = sdmmc_send_cmd(card, &cmd);
|
err = sdmmc_send_cmd(card, &cmd);
|
||||||
if (err != ESP_OK) {
|
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;
|
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;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -644,9 +682,10 @@ esp_err_t sdmmc_full_erase(sdmmc_card_t* card)
|
|||||||
if (card->is_mmc) {
|
if (card->is_mmc) {
|
||||||
arg = sdmmc_mmc_can_sanitize(card) == ESP_OK ? SDMMC_MMC_DISCARD_ARG: SDMMC_MMC_TRIM_ARG;
|
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)) {
|
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;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -318,3 +318,12 @@ esp_err_t sdmmc_fix_host_flags(sdmmc_card_t* card)
|
|||||||
}
|
}
|
||||||
return ESP_OK;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -37,7 +37,9 @@
|
|||||||
*/
|
*/
|
||||||
#define SDMMC_DEFAULT_CMD_TIMEOUT_MS 1000 // Max timeout of ordinary commands
|
#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_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).
|
/* Maximum retry/error count for SEND_OP_COND (CMD1).
|
||||||
* These are somewhat arbitrary, values originate from OpenBSD driver.
|
* 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);
|
size_t start_block, size_t block_count);
|
||||||
esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
|
esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
|
||||||
size_t start_block, size_t block_count);
|
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 */
|
/* SD specific */
|
||||||
esp_err_t sdmmc_check_scr(sdmmc_card_t* card);
|
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_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_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);
|
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 */
|
/* SDIO specific */
|
||||||
esp_err_t sdmmc_io_reset(sdmmc_card_t* card);
|
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_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_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd);
|
||||||
esp_err_t sdmmc_mmc_enable_hs_mode(sdmmc_card_t* card);
|
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 */
|
/* Parts of card initialization flow */
|
||||||
esp_err_t sdmmc_init_sd_if_cond(sdmmc_card_t* card);
|
esp_err_t sdmmc_init_sd_if_cond(sdmmc_card_t* card);
|
||||||
|
@ -288,3 +288,13 @@ out:
|
|||||||
free(ext_csd);
|
free(ext_csd);
|
||||||
return err;
|
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;
|
||||||
|
}
|
||||||
|
@ -96,7 +96,7 @@ esp_err_t sdmmc_init_sd_ssr(sdmmc_card_t* card)
|
|||||||
.data = sd_ssr,
|
.data = sd_ssr,
|
||||||
.datalen = SD_SSR_SIZE,
|
.datalen = SD_SSR_SIZE,
|
||||||
.blklen = SD_SSR_SIZE,
|
.blklen = SD_SSR_SIZE,
|
||||||
.opcode = MMC_SEND_STATUS,
|
.opcode = SD_APP_SD_STATUS,
|
||||||
.arg = 0,
|
.arg = 0,
|
||||||
.flags = SCF_CMD_ADTC | SCF_RSP_R1 | SCF_CMD_READ
|
.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;
|
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)
|
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 };
|
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->cur_bus_width = SSR_DAT_BUS_WIDTH(ssr);
|
||||||
out_ssr->discard_support = SSR_DISCARD_SUPPORT(ssr);
|
out_ssr->discard_support = SSR_DISCARD_SUPPORT(ssr);
|
||||||
out_ssr->fule_support = SSR_FULE_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;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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);
|
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);
|
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");
|
printf("Verifying erase state...\n");
|
||||||
uint8_t erase_mem_byte = 0xFF;
|
uint8_t erase_mem_byte = 0xFF;
|
||||||
// ensure all the blocks are erased and are up to after erase state.
|
// ensure all the blocks are erased and are up to after erase state.
|
||||||
|
Loading…
Reference in New Issue
Block a user