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.
This commit is contained in:
Ivan Grokhotkov
2022-05-30 15:39:02 +02:00
parent a28828a6f4
commit 79659e3096
7 changed files with 85 additions and 10 deletions
+45
View File
@@ -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;
}
}