From ffdbeee9f60c5dd9bffaa6735f3f42f1a9f09e2b Mon Sep 17 00:00:00 2001 From: Vamshi Gajjela Date: Fri, 25 Feb 2022 17:14:53 +0530 Subject: [PATCH] sdmmc: Add erase command-38. Support erase/trim/discard/sanitize options. Erase command (38) for SD cards allows option for erase/dicard/fule operation at block level and for MMC cards supports option for discard/trim at block level. When Sanitize is executed only the portion of data that was unmapped by a Discard command shall be removed by the Sanitize command. Unit test cases added to verify ERASE feature in SD/SDSPI mode. TRIM/DISCARD/SANITIZE tests for eMMC devices. Closes https://github.com/espressif/esp-idf/pull/7635 Closes https://github.com/espressif/esp-idf/issues/7623 --- components/driver/include/driver/sdmmc_defs.h | 36 +- .../driver/include/driver/sdmmc_types.h | 50 +- components/driver/sdmmc_transaction.c | 6 +- components/sdmmc/include/sdmmc_cmd.h | 78 ++++ components/sdmmc/sdmmc_cmd.c | 145 ++++++ components/sdmmc/sdmmc_common.c | 8 +- components/sdmmc/sdmmc_common.h | 9 + components/sdmmc/sdmmc_init.c | 3 + components/sdmmc/sdmmc_mmc.c | 8 +- components/sdmmc/sdmmc_sd.c | 56 ++- components/sdmmc/test/test_sd.c | 434 ++++++++++++++++++ tools/ci/check_copyright_config.yaml | 8 + 12 files changed, 825 insertions(+), 16 deletions(-) diff --git a/components/driver/include/driver/sdmmc_defs.h b/components/driver/include/driver/sdmmc_defs.h index 54e051f354..54ead88842 100644 --- a/components/driver/include/driver/sdmmc_defs.h +++ b/components/driver/include/driver/sdmmc_defs.h @@ -47,12 +47,17 @@ #define MMC_SET_BLOCK_COUNT 23 /* R1 */ #define MMC_WRITE_BLOCK_SINGLE 24 /* R1 */ #define MMC_WRITE_BLOCK_MULTIPLE 25 /* R1 */ +#define MMC_ERASE_GROUP_START 35 /* R1 */ +#define MMC_ERASE_GROUP_END 36 /* R1 */ +#define MMC_ERASE 38 /* R1B */ #define MMC_APP_CMD 55 /* R1 */ /* SD commands */ /* response type */ #define SD_SEND_RELATIVE_ADDR 3 /* R6 */ #define SD_SEND_SWITCH_FUNC 6 /* R1 */ #define SD_SEND_IF_COND 8 /* R7 */ +#define SD_ERASE_GROUP_START 32 /* R1 */ +#define SD_ERASE_GROUP_END 33 /* R1 */ #define SD_READ_OCR 58 /* R3 */ #define SD_CRC_ON_OFF 59 /* R1 */ @@ -141,21 +146,26 @@ #define SD_ARG_BUS_WIDTH_4 2 /* EXT_CSD fields */ +#define EXT_CSD_SANITIZE_START 165 /* WO */ +#define EXT_CSD_ERASED_MEM_CONT 181 /* RO */ #define EXT_CSD_BUS_WIDTH 183 /* WO */ #define EXT_CSD_HS_TIMING 185 /* R/W */ +#define EXT_CSD_POWER_CLASS 187 /* R/W */ +#define EXT_CSD_CMD_SET 191 /* R/W */ #define EXT_CSD_REV 192 /* RO */ #define EXT_CSD_STRUCTURE 194 /* RO */ #define EXT_CSD_CARD_TYPE 196 /* RO */ -#define EXT_CSD_SEC_COUNT 212 /* RO */ -#define EXT_CSD_PWR_CL_26_360 203 /* RO */ -#define EXT_CSD_PWR_CL_52_360 202 /* RO */ -#define EXT_CSD_PWR_CL_26_195 201 /* RO */ #define EXT_CSD_PWR_CL_52_195 200 /* RO */ -#define EXT_CSD_POWER_CLASS 187 /* R/W */ -#define EXT_CSD_CMD_SET 191 /* R/W */ +#define EXT_CSD_PWR_CL_26_195 201 /* RO */ +#define EXT_CSD_PWR_CL_52_360 202 /* RO */ +#define EXT_CSD_PWR_CL_26_360 203 /* RO */ +#define EXT_CSD_SEC_COUNT 212 /* RO */ +#define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */ #define EXT_CSD_S_CMD_SET 504 /* RO */ /* EXT_CSD field definitions */ +#define EXT_CSD_REV_1_6 6 /* Revision 1.6 (for MMC v4.5, v4.51) */ + #define EXT_CSD_CMD_SET_NORMAL (1U << 0) #define EXT_CSD_CMD_SET_SECURE (1U << 1) #define EXT_CSD_CMD_SET_CPSECURE (1U << 2) @@ -186,6 +196,12 @@ #define EXT_CSD_CARD_TYPE_52M_V12 0x0b #define EXT_CSD_CARD_TYPE_52M_V12_18 0x0f +/* EXT_CSD_SEC_FEATURE_SUPPORT */ +#define EXT_CSD_SECURE_ER_EN (uint8_t)(1 << 0) +#define EXT_CSD_SEC_BD_BLK_EN (uint8_t)(1 << 2) +#define EXT_CSD_SEC_GB_CL_EN (uint8_t)(1 << 4) +#define EXT_CSD_SEC_SANITIZE (uint8_t)(1 << 6) + /* EXT_CSD MMC */ #define EXT_CSD_MMC_SIZE 512 @@ -336,6 +352,12 @@ #define SCR_CMD_SUPPORT_CMD20(scr) MMC_RSP_BITS((scr), 32, 1) #define SCR_RESERVED2(scr) MMC_RSP_BITS((scr), 0, 32) +/* 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_DISCARD_SUPPORT(ssr) MMC_RSP_BITS((ssr), 313, 1) +#define SSR_FULE_SUPPORT(ssr) MMC_RSP_BITS((ssr), 312, 1) + /* Max supply current in SWITCH_FUNC response (in mA) */ #define SD_SFUNC_I_MAX(status) (MMC_RSP_BITS((uint32_t *)(status), 496, 16)) @@ -365,6 +387,8 @@ #define SD_ACCESS_MODE_SDR104 3 /* UHS-I, 208 MHz clock */ #define SD_ACCESS_MODE_DDR50 4 /* UHS-I, 50 MHz clock, DDR */ +#define SD_SSR_SIZE 64 /* SD status register */ + /** * @brief Extract up to 32 sequential bits from an array of 32-bit words * diff --git a/components/driver/include/driver/sdmmc_types.h b/components/driver/include/driver/sdmmc_types.h index cbb796fdbb..58ccfd4468 100644 --- a/components/driver/include/driver/sdmmc_types.h +++ b/components/driver/include/driver/sdmmc_types.h @@ -56,17 +56,35 @@ typedef struct { /** * Decoded values from SD Configuration Register + * Note: When new member is added, update reserved bits accordingly */ typedef struct { - int sd_spec; /*!< SD Physical layer specification version, reported by card */ - int bus_width; /*!< bus widths supported by card: BIT(0) — 1-bit bus, BIT(2) — 4-bit bus */ + uint32_t sd_spec: 4; /*!< SD Physical layer specification version, reported by card */ + uint32_t erase_mem_state: 1; /*!< data state on card after erase whether 0 or 1 (card vendor dependent) */ + uint32_t bus_width: 4; /*!< bus widths supported by card: BIT(0) — 1-bit bus, BIT(2) — 4-bit bus */ + uint32_t reserved: 23; /*!< reserved for future expansion */ + uint32_t rsvd_mnf; /*!< reserved for manufacturer usage */ } sdmmc_scr_t; +/** + * Decoded values from SD Status Register + * Note: When new member is added, update reserved bits accordingly + */ +typedef struct { + 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 */ +} sdmmc_ssr_t; + /** * Decoded values of Extended Card Specific Data */ typedef struct { - uint8_t power_class; /*!< Power class used by the card */ + uint8_t rev; /*!< Extended CSD Revision */ + uint8_t power_class; /*!< Power class used by the card */ + uint8_t erase_mem_state; /*!< data state on card after erase whether 0 or 1 (card vendor dependent) */ + uint8_t sec_feature; /*!< secure data management features supported by the card */ } sdmmc_ext_csd_t; /** @@ -120,7 +138,7 @@ typedef struct { #define SCF_WAIT_BUSY 0x2000 /*!< Wait for completion of card busy signal before returning */ /** @endcond */ esp_err_t error; /*!< error returned from transfer */ - int timeout_ms; /*!< response timeout, in milliseconds */ + uint32_t timeout_ms; /*!< response timeout, in milliseconds */ } sdmmc_command_t; /** @@ -173,6 +191,7 @@ typedef struct { }; sdmmc_csd_t csd; /*!< decoded CSD (Card-Specific Data) register value */ sdmmc_scr_t scr; /*!< decoded SCR (SD card Configuration Register) value */ + sdmmc_ssr_t ssr; /*!< decoded SSR (SD Status Register) value */ sdmmc_ext_csd_t ext_csd; /*!< decoded EXT_CSD (Extended Card Specific Data) register value */ uint16_t rca; /*!< RCA (Relative Card Address) */ uint16_t max_freq_khz; /*!< Maximum frequency, in kHz, supported by the card */ @@ -185,5 +204,28 @@ typedef struct { uint32_t reserved : 23; /*!< Reserved for future expansion */ } sdmmc_card_t; +/** + * SD/MMC erase command(38) arguments + * SD: + * ERASE: Erase the write blocks, physical/hard erase. + * + * DISCARD: Card may deallocate the discarded blocks partially or completely. + * After discard operation the previously written data may be partially or + * fully read by the host depending on card implementation. + * + * MMC: + * ERASE: Does TRIM, applies erase operation to write blocks instead of Erase Group. + * + * DISCARD: The Discard function allows the host to identify data that is no + * longer required so that the device can erase the data if necessary during + * background erase events. Applies to write blocks instead of Erase Group + * After discard operation, the original data may be remained partially or + * fully accessible to the host dependent on device. + * + */ +typedef enum { + SDMMC_ERASE_ARG = 0, /*!< Erase operation on SD, Trim operation on MMC */ + SDMMC_DISCARD_ARG = 1, /*!< Discard operation for SD/MMC */ +} sdmmc_erase_arg_t; #endif // _SDMMC_TYPES_H_ diff --git a/components/driver/sdmmc_transaction.c b/components/driver/sdmmc_transaction.c index cb1aeae3a6..e4dc22e465 100644 --- a/components/driver/sdmmc_transaction.c +++ b/components/driver/sdmmc_transaction.c @@ -73,7 +73,7 @@ static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd, static void process_command_response(uint32_t status, sdmmc_command_t* cmd); static void fill_dma_descriptors(size_t num_desc); static size_t get_free_descriptors_count(void); -static bool wait_for_busy_cleared(int timeout_ms); +static bool wait_for_busy_cleared(uint32_t timeout_ms); esp_err_t sdmmc_host_transaction_handler_init(void) { @@ -462,7 +462,7 @@ static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd, return ESP_OK; } -static bool wait_for_busy_cleared(int timeout_ms) +static bool wait_for_busy_cleared(uint32_t timeout_ms) { if (timeout_ms == 0) { return !sdmmc_host_card_busy(); @@ -472,7 +472,7 @@ static bool wait_for_busy_cleared(int timeout_ms) * can only generate Busy Clear Interrupt for data write commands, and waiting * for busy clear is mostly needed for other commands such as MMC_SWITCH. */ - int timeout_ticks = (timeout_ms + portTICK_PERIOD_MS - 1) / portTICK_PERIOD_MS; + uint32_t timeout_ticks = (timeout_ms + portTICK_PERIOD_MS - 1) / portTICK_PERIOD_MS; while (timeout_ticks-- > 0) { if (!sdmmc_host_card_busy()) { return true; diff --git a/components/sdmmc/include/sdmmc_cmd.h b/components/sdmmc/include/sdmmc_cmd.h index 9b7319641e..e821b62495 100644 --- a/components/sdmmc/include/sdmmc_cmd.h +++ b/components/sdmmc/include/sdmmc_cmd.h @@ -80,6 +80,84 @@ esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src, esp_err_t sdmmc_read_sectors(sdmmc_card_t* card, void* dst, size_t start_sector, size_t sector_count); +/** + * Erase given number of sectors from the SD/MMC card + * + * @note When sdmmc_erase_sectors used with cards in SDSPI mode, it was + * observed that card requires re-init after erase operation. + * + * @param card pointer to card information structure previously initialized + * using sdmmc_card_init + * @param start_sector sector where to start erase + * @param sector_count number of sectors to erase + * @param arg erase command (CMD38) argument + * @return + * - ESP_OK on success + * - One of the error codes from SDMMC host controller + */ +esp_err_t sdmmc_erase_sectors(sdmmc_card_t* card, size_t start_sector, + size_t sector_count, sdmmc_erase_arg_t arg); + +/** + * Check if SD/MMC card supports discard + * + * @param card pointer to card information structure previously initialized + * using sdmmc_card_init + * @return + * - ESP_OK if supported by the card/device + * - ESP_FAIL if not supported by the card/device + */ +esp_err_t sdmmc_can_discard(sdmmc_card_t* card); + +/** + * Check if SD/MMC card supports trim + * + * @param card pointer to card information structure previously initialized + * using sdmmc_card_init + * @return + * - ESP_OK if supported by the card/device + * - ESP_FAIL if not supported by the card/device + */ +esp_err_t sdmmc_can_trim(sdmmc_card_t* card); + +/** + * Check if SD/MMC card supports sanitize + * + * @param card pointer to card information structure previously initialized + * using sdmmc_card_init + * @return + * - ESP_OK if supported by the card/device + * - ESP_FAIL if not supported by the card/device + */ +esp_err_t sdmmc_mmc_can_sanitize(sdmmc_card_t* card); + +/** + * Sanitize the data that was unmapped by a Discard command + * + * @note Discard command has to precede sanitize operation. To discard, use + * MMC_DICARD_ARG with sdmmc_erase_sectors argument + * + * @param card pointer to card information structure previously initialized + * using sdmmc_card_init + * @param timeout_ms timeout value in milliseconds required to sanitize the + * selected range of sectors. + * @return + * - ESP_OK on success + * - One of the error codes from SDMMC host controller + */ +esp_err_t sdmmc_mmc_sanitize(sdmmc_card_t* card, uint32_t timeout_ms); + +/** + * Erase complete SD/MMC card + * + * @param card pointer to card information structure previously initialized + * using sdmmc_card_init + * @return + * - ESP_OK on success + * - One of the error codes from SDMMC host controller + */ +esp_err_t sdmmc_full_erase(sdmmc_card_t* card); + /** * Read one byte from an SDIO card using IO_RW_DIRECT (CMD52) * diff --git a/components/sdmmc/sdmmc_cmd.c b/components/sdmmc/sdmmc_cmd.c index 0a39575d17..5eead476a3 100644 --- a/components/sdmmc/sdmmc_cmd.c +++ b/components/sdmmc/sdmmc_cmd.c @@ -501,6 +501,151 @@ esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst, return ESP_OK; } +esp_err_t sdmmc_erase_sectors(sdmmc_card_t* card, size_t start_sector, + size_t sector_count, sdmmc_erase_arg_t arg) +{ + if (start_sector + sector_count > card->csd.capacity) { + return ESP_ERR_INVALID_SIZE; + } + + if (arg == SDMMC_ERASE_ARG) { + arg = card->is_mmc ? SDMMC_MMC_TRIM_ARG : SDMMC_SD_ERASE_ARG; + } else { + arg = card->is_mmc ? SDMMC_MMC_DISCARD_ARG : SDMMC_SD_DISCARD_ARG; + } + /* + * validate the CMD38 argument against card supported features + */ + if ((arg == SDMMC_MMC_TRIM_ARG) && (sdmmc_can_trim(card) != ESP_OK)) { + return ESP_ERR_NOT_SUPPORTED; + } + if (((arg == SDMMC_MMC_DISCARD_ARG) || (arg == SDMMC_SD_DISCARD_ARG)) && + ((sdmmc_can_discard(card) != ESP_OK) || host_is_spi(card))) { + return ESP_ERR_NOT_SUPPORTED; + } + + /* default as block unit address */ + size_t addr_unit_mult = 1; + + if (!(card->ocr & SD_OCR_SDHC_CAP)) { + addr_unit_mult = card->csd.sector_size; + } + + /* prepare command to set the start address */ + sdmmc_command_t cmd = { + .flags = SCF_CMD_AC | SCF_RSP_R1 | SCF_WAIT_BUSY, + .opcode = card->is_mmc ? MMC_ERASE_GROUP_START : + SD_ERASE_GROUP_START, + .arg = (start_sector * addr_unit_mult), + }; + + 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; + } + + /* prepare command to set the end address */ + cmd.opcode = card->is_mmc ? MMC_ERASE_GROUP_END : SD_ERASE_GROUP_END; + cmd.arg = ((start_sector + (sector_count - 1)) * addr_unit_mult); + + err = sdmmc_send_cmd(card, &cmd); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err); + return err; + } + + /* issue erase command */ + memset((void *)&cmd, 0 , sizeof(sdmmc_command_t)); + cmd.flags = SCF_CMD_AC | SCF_RSP_R1B | SCF_WAIT_BUSY; + cmd.opcode = MMC_ERASE; + cmd.arg = 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); + + err = sdmmc_send_cmd(card, &cmd); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err); + return err; + } + return ESP_OK; +} + +esp_err_t sdmmc_can_discard(sdmmc_card_t* card) +{ + if ((card->is_mmc) && (card->ext_csd.rev >= EXT_CSD_REV_1_6)) { + return ESP_OK; + } + // SD card + if (!host_is_spi(card) && (card->ssr.discard_support == 1)) { + return ESP_OK; + } + return ESP_FAIL; +} + +esp_err_t sdmmc_can_trim(sdmmc_card_t* card) +{ + if ((card->is_mmc) && (card->ext_csd.sec_feature & EXT_CSD_SEC_GB_CL_EN)) { + return ESP_OK; + } + return ESP_FAIL; +} + +esp_err_t sdmmc_mmc_can_sanitize(sdmmc_card_t* card) +{ + if ((card->is_mmc) && (card->ext_csd.sec_feature & EXT_CSD_SEC_SANITIZE)) { + return ESP_OK; + } + return ESP_FAIL; +} + +esp_err_t sdmmc_mmc_sanitize(sdmmc_card_t* card, uint32_t timeout_ms) +{ + esp_err_t err; + uint8_t index = EXT_CSD_SANITIZE_START; + uint8_t set = EXT_CSD_CMD_SET_NORMAL; + uint8_t value = 0x01; + + if (sdmmc_mmc_can_sanitize(card) != ESP_OK) { + return ESP_ERR_NOT_SUPPORTED; + } + /* + * A Sanitize operation is initiated by writing a value to the extended + * CSD[165] SANITIZE_START. While the device is performing the sanitize + * operation, the busy line is asserted. + * SWITCH command is used to write the EXT_CSD register. + */ + sdmmc_command_t cmd = { + .opcode = MMC_SWITCH, + .arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8) | set, + .flags = SCF_RSP_R1B | SCF_CMD_AC | SCF_WAIT_BUSY, + .timeout_ms = timeout_ms, + }; + err = sdmmc_send_cmd(card, &cmd); + if (err == ESP_OK) { + //check response bit to see that switch was accepted + if (MMC_R1(cmd.response) & MMC_R1_SWITCH_ERROR) { + err = ESP_ERR_INVALID_RESPONSE; + } + } + return err; +} + +esp_err_t sdmmc_full_erase(sdmmc_card_t* card) +{ + sdmmc_erase_arg_t arg = SDMMC_SD_ERASE_ARG; // erase by default for SD card + esp_err_t err; + 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); + if ((err == ESP_OK) && (arg == SDMMC_MMC_DISCARD_ARG)) { + return sdmmc_mmc_sanitize(card, SDMMC_ERASE_BLOCK_TIMEOUT_MS + card->csd.capacity); + } + return err; +} + esp_err_t sdmmc_get_status(sdmmc_card_t* card) { uint32_t stat; diff --git a/components/sdmmc/sdmmc_common.c b/components/sdmmc/sdmmc_common.c index c28d447581..0d04ddbb26 100644 --- a/components/sdmmc/sdmmc_common.c +++ b/components/sdmmc/sdmmc_common.c @@ -268,6 +268,7 @@ void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card) print_csd = true; } else { type = (card->ocr & SD_OCR_SDHC_CAP) ? "SDHC/SDXC" : "SDSC"; + print_csd = true; } fprintf(stream, "Type: %s\n", type); if (card->max_freq_khz < 1000) { @@ -280,8 +281,13 @@ void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card) if (print_csd) { fprintf(stream, "CSD: ver=%d, sector_size=%d, capacity=%d read_bl_len=%d\n", - card->csd.csd_ver, + (card->is_mmc ? card->csd.csd_ver : card->csd.csd_ver + 1), card->csd.sector_size, card->csd.capacity, card->csd.read_block_len); + if (card->is_mmc) { + fprintf(stream, "EXT CSD: bus_width=%d\n", (1 << card->log_bus_width)); + } else if (!card->is_sdio){ // make sure card is SD + fprintf(stream, "SSR: bus_width=%d\n", (card->ssr.cur_bus_width ? 4 : 1)); + } } if (print_scr) { fprintf(stream, "SCR: sd_spec=%d, bus_width=%d\n", card->scr.sd_spec, card->scr.bus_width); diff --git a/components/sdmmc/sdmmc_common.h b/components/sdmmc/sdmmc_common.h index e07ebd9109..8dc049ebc1 100644 --- a/components/sdmmc/sdmmc_common.h +++ b/components/sdmmc/sdmmc_common.h @@ -37,6 +37,7 @@ */ #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 /* Maximum retry/error count for SEND_OP_COND (CMD1). * These are somewhat arbitrary, values originate from OpenBSD driver. @@ -44,6 +45,12 @@ #define SDMMC_SEND_OP_COND_MAX_RETRIES 100 #define SDMMC_SEND_OP_COND_MAX_ERRORS 3 +/* supported arguments for erase command 38 */ +#define SDMMC_SD_ERASE_ARG 0 +#define SDMMC_SD_DISCARD_ARG 1 +#define SDMMC_MMC_TRIM_ARG 1 +#define SDMMC_MMC_DISCARD_ARG 3 + /* Functions to send individual commands */ esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd); esp_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd); @@ -78,6 +85,7 @@ esp_err_t sdmmc_check_scr(sdmmc_card_t* card); 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); /* SDIO specific */ esp_err_t sdmmc_io_reset(sdmmc_card_t* card); @@ -108,6 +116,7 @@ esp_err_t sdmmc_init_spi_crc(sdmmc_card_t* card); esp_err_t sdmmc_init_io(sdmmc_card_t* card); esp_err_t sdmmc_init_sd_blocklen(sdmmc_card_t* card); esp_err_t sdmmc_init_sd_scr(sdmmc_card_t* card); +esp_err_t sdmmc_init_sd_ssr(sdmmc_card_t* card); esp_err_t sdmmc_init_sd_wait_data_ready(sdmmc_card_t* card); esp_err_t sdmmc_init_mmc_read_ext_csd(sdmmc_card_t* card); esp_err_t sdmmc_init_mmc_read_cid(sdmmc_card_t* card); diff --git a/components/sdmmc/sdmmc_init.c b/components/sdmmc/sdmmc_init.c index 789e6e5c11..0ef6272118 100644 --- a/components/sdmmc/sdmmc_init.c +++ b/components/sdmmc/sdmmc_init.c @@ -112,6 +112,9 @@ esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card) SDMMC_INIT_STEP(always, sdmmc_init_host_bus_width); } + /* SD card: read SD Status register */ + SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_ssr); + /* Switch to the host to use card->max_freq_khz frequency. */ SDMMC_INIT_STEP(always, sdmmc_init_host_frequency); diff --git a/components/sdmmc/sdmmc_mmc.c b/components/sdmmc/sdmmc_mmc.c index 9d19926e36..fa83277315 100644 --- a/components/sdmmc/sdmmc_mmc.c +++ b/components/sdmmc/sdmmc_mmc.c @@ -91,6 +91,11 @@ esp_err_t sdmmc_init_mmc_read_ext_csd(sdmmc_card_t* card) card->csd.capacity = sectors; } + /* erased state of a bit, if 1 byte value read is 0xFF else 0x00 */ + card->ext_csd.erase_mem_state = ext_csd[EXT_CSD_ERASED_MEM_CONT]; + card->ext_csd.rev = ext_csd[EXT_CSD_REV]; + card->ext_csd.sec_feature = ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]; + out: free(ext_csd); return err; @@ -224,8 +229,9 @@ esp_err_t sdmmc_mmc_switch(sdmmc_card_t* card, uint8_t set, uint8_t index, uint8 esp_err_t err = sdmmc_send_cmd(card, &cmd); if (err == ESP_OK) { //check response bit to see that switch was accepted - if (MMC_R1(cmd.response) & MMC_R1_SWITCH_ERROR) + if (MMC_R1(cmd.response) & MMC_R1_SWITCH_ERROR) { err = ESP_ERR_INVALID_RESPONSE; + } } return err; diff --git a/components/sdmmc/sdmmc_sd.c b/components/sdmmc/sdmmc_sd.c index 5840b4b777..03037e5c26 100644 --- a/components/sdmmc/sdmmc_sd.c +++ b/components/sdmmc/sdmmc_sd.c @@ -80,6 +80,43 @@ esp_err_t sdmmc_init_sd_scr(sdmmc_card_t* card) return ESP_OK; } +esp_err_t sdmmc_init_sd_ssr(sdmmc_card_t* card) +{ + esp_err_t err = ESP_OK; + /* Get the contents of SSR register: SD additional information + * ACMD13 to read 512byte SD status information + */ + uint32_t* sd_ssr = heap_caps_calloc(1, SD_SSR_SIZE, MALLOC_CAP_DMA); + if (!sd_ssr) { + ESP_LOGE(TAG, "%s: could not allocate sd_ssr", __func__); + return ESP_ERR_NO_MEM; + } + + sdmmc_command_t cmd = { + .data = sd_ssr, + .datalen = SD_SSR_SIZE, + .blklen = SD_SSR_SIZE, + .opcode = MMC_SEND_STATUS, + .arg = 0, + .flags = SCF_CMD_ADTC | SCF_RSP_R1 | SCF_CMD_READ + }; + + // read SD status register + err = sdmmc_send_app_cmd(card, &cmd); + if (err != ESP_OK) { + free(sd_ssr); + ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err); + return err; + } + + err = sdmmc_decode_ssr(sd_ssr, &card->ssr); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: error sdmmc_decode_scr returned 0x%x", __func__, err); + } + free(sd_ssr); + return err; +} + esp_err_t sdmmc_init_sd_bus_width(sdmmc_card_t* card) { int width = 1; @@ -265,7 +302,7 @@ esp_err_t sdmmc_check_scr(sdmmc_card_t* card) * and compare the result with the previous one. Use this simple check as * an indicator of potential signal integrity issues. */ - sdmmc_scr_t scr_tmp; + sdmmc_scr_t scr_tmp = { 0 }; esp_err_t err = sdmmc_send_cmd_send_scr(card, &scr_tmp); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: send_scr returned 0x%x", __func__, err); @@ -345,6 +382,23 @@ esp_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr) return ESP_ERR_NOT_SUPPORTED; } out_scr->sd_spec = SCR_SD_SPEC(resp); + out_scr->erase_mem_state = SCR_DATA_STAT_AFTER_ERASE(resp); out_scr->bus_width = SCR_SD_BUS_WIDTHS(resp); return ESP_OK; } + +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 }; + size_t j = (SD_SSR_SIZE/sizeof(uint32_t) - 1); + + for(size_t i = 0; i < (SD_SSR_SIZE/sizeof(uint32_t)); i++) { + ssr[j - i] = __builtin_bswap32(raw_ssr[i]); + } + + 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); + + return ESP_OK; +} diff --git a/components/sdmmc/test/test_sd.c b/components/sdmmc/test/test_sd.c index ca3f6934d7..25070abd04 100644 --- a/components/sdmmc/test/test_sd.c +++ b/components/sdmmc/test/test_sd.c @@ -694,3 +694,437 @@ TEST_CASE("WP input works in SPI mode", "[sd][test_env=UT_T1_SPIMODE]") sd_test_board_power_off(); } #endif //WITH_SDSPI_TEST + +#if WITH_SD_TEST || WITH_EMMC_TEST + +#define PATTERN_SEED 0x12345678 +#define FLAG_ERASE_TEST_ADJACENT (1 << 0) +#define FLAG_VERIFY_ERASE_STATE (1 << 1) +bool do_sanitize_flag = false; +static void ensure_sector_written(sdmmc_card_t* card, size_t sector, + uint8_t *pattern_buf, uint8_t *temp_buf) +{ + size_t block_size = card->csd.sector_size; + TEST_ESP_OK(sdmmc_write_sectors(card, pattern_buf, sector, 1)); + memset((void *)temp_buf, 0x00, block_size); + TEST_ESP_OK(sdmmc_read_sectors(card, temp_buf, sector, 1)); + check_buffer(PATTERN_SEED, temp_buf, block_size / sizeof(uint32_t)); +} + +static void ensure_sector_intact(sdmmc_card_t* card, size_t sector, + uint8_t *pattern_buf, uint8_t *temp_buf) +{ + size_t block_size = card->csd.sector_size; + memset((void *)temp_buf, 0x00, block_size); + TEST_ESP_OK(sdmmc_read_sectors(card, temp_buf, sector, 1)); + check_buffer(PATTERN_SEED, temp_buf, block_size / sizeof(uint32_t)); +} + +static int32_t ensure_sector_erase(sdmmc_card_t* card, size_t sector, + uint8_t *pattern_buf, uint8_t *temp_buf) +{ + size_t block_size = card->csd.sector_size; + memset((void *)temp_buf, 0, block_size); + TEST_ESP_OK(sdmmc_read_sectors(card, temp_buf, sector, 1)); + return memcmp(pattern_buf, temp_buf, block_size); +} + +static void do_single_erase_test(sdmmc_card_t* card, size_t start_block, + size_t block_count, uint8_t flags, sdmmc_erase_arg_t arg) +{ + size_t block_size = card->csd.sector_size; + uint8_t *temp_buf = NULL; + uint8_t *pattern_buf = NULL; + size_t end_block = (start_block + block_count - 1); + + /* + * To ensure erase is successful/valid + * selected blocks after erase should have erase state data pattern + * data of blocks adjacent to selected region should remain intact + */ + TEST_ESP_OK((start_block + block_count) > card->csd.capacity); + + pattern_buf = (uint8_t *)heap_caps_malloc(block_size, MALLOC_CAP_DMA); + TEST_ASSERT_NOT_NULL(pattern_buf); + temp_buf = (uint8_t *)heap_caps_malloc(block_size, MALLOC_CAP_DMA); + TEST_ASSERT_NOT_NULL(temp_buf); + + // create pattern buffer + fill_buffer(PATTERN_SEED, pattern_buf, block_size / sizeof(uint32_t)); + + // check if it's not the first block of device & write/read/verify pattern + if ((flags & FLAG_ERASE_TEST_ADJACENT) && start_block) { + ensure_sector_written(card, (start_block - 1), pattern_buf, temp_buf); + } + + ensure_sector_written(card, start_block, pattern_buf, temp_buf); + + // check if it's not the last block of device & write/read/verify pattern + if ((flags & FLAG_ERASE_TEST_ADJACENT) && (end_block < (card->csd.capacity - 1))) { + ensure_sector_written(card, (end_block + 1), pattern_buf, temp_buf); + } + + // when block count is 1, start and end block is same, hence skip + if (block_count != 1) { + ensure_sector_written(card, end_block, pattern_buf, temp_buf); + } + + // fill pattern to (start_block + end_block)/2 in the erase range + if(block_count > 2) { + ensure_sector_written(card, (start_block + end_block)/2, pattern_buf, temp_buf); + } + + float total_size = (block_count/1024.0f) * block_size; + printf(" %10d | %10d | %8.1f ", start_block, block_count, total_size); + fflush(stdout); + + // erase the blocks + struct timeval t_start_er; + gettimeofday(&t_start_er, NULL); + TEST_ESP_OK(sdmmc_erase_sectors(card, start_block, block_count, arg)); + if (do_sanitize_flag) { + TEST_ESP_OK(sdmmc_mmc_sanitize(card, block_count * 500)); + } + struct timeval t_stop_wr; + gettimeofday(&t_stop_wr, NULL); + 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(" | %8.2f\n", time_er); + + // ensure adjacent blocks are not affected + // block before start_block + if ((flags & FLAG_ERASE_TEST_ADJACENT) && start_block) { + ensure_sector_intact(card, (start_block - 1), pattern_buf, temp_buf); + } + + // block after end_block + if ((flags & FLAG_ERASE_TEST_ADJACENT) && (end_block < (card->csd.capacity - 1))) { + ensure_sector_intact(card, (end_block + 1), pattern_buf, temp_buf); + } + + uint8_t erase_mem_byte = 0xFF; + // ensure all the blocks are erased and are up to after erase state. + if (!card->is_mmc) { + erase_mem_byte = card->scr.erase_mem_state ? 0xFF : 0x00; + } else { + erase_mem_byte = card->ext_csd.erase_mem_state ? 0xFF : 0x00; + } + + memset((void *)pattern_buf, erase_mem_byte, block_size); + + // as it is block by block comparison, a time taking process. Really long + // when you do erase and verify on complete device. + if (flags & FLAG_VERIFY_ERASE_STATE) { + for (size_t i = 0; i < block_count; i++) { + if (ensure_sector_erase(card, (start_block + i), pattern_buf, temp_buf)) { + printf("Error: Sector %d erase\n", (start_block + i)); + break; + } + } + } + + free(temp_buf); + free(pattern_buf); +} +#endif // WITH_SD_TEST || WITH_EMMC_TEST + +#if WITH_SDSPI_TEST +static void test_sdspi_erase_blocks(size_t start_block, size_t block_count) +{ + sd_test_board_power_on(); + sdmmc_host_t config = SDSPI_HOST_DEFAULT(); + sdspi_dev_handle_t handle; + sdspi_device_config_t dev_config = SDSPI_DEVICE_CONFIG_DEFAULT(); + dev_config.host_id = config.slot; + dev_config.gpio_cs = SDSPI_TEST_CS_PIN; + test_sdspi_init_bus(dev_config.host_id, SDSPI_TEST_MOSI_PIN, SDSPI_TEST_MISO_PIN, SDSPI_TEST_SCLK_PIN, SPI_DMA_CH_AUTO); + TEST_ESP_OK(sdspi_host_init()); + TEST_ESP_OK(sdspi_host_init_device(&dev_config, &handle)); + + // This test can only run under 20MHz on ESP32, because the runner connects the card to + // non-IOMUX pins of HSPI. + + sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t)); + TEST_ASSERT_NOT_NULL(card); + TEST_ESP_OK(sdmmc_card_init(&config, card)); + sdmmc_card_print_info(stdout, card); + printf("block size %d capacity %d\n", card->csd.sector_size, card->csd.capacity); + printf("Erasing sectors %d-%d\n", start_block, (start_block + block_count -1)); + size_t block_size = card->csd.sector_size; + uint8_t *pattern_buf = (uint8_t *)heap_caps_malloc(block_size, MALLOC_CAP_DMA); + TEST_ASSERT_NOT_NULL(pattern_buf); + uint8_t *temp_buf = (uint8_t *)heap_caps_malloc(block_size, MALLOC_CAP_DMA); + TEST_ASSERT_NOT_NULL(temp_buf); + + struct timeval t_start_er; + gettimeofday(&t_start_er, NULL); + TEST_ESP_OK(sdmmc_erase_sectors(card, start_block, block_count, SDMMC_ERASE_ARG)); + struct timeval t_stop_wr; + gettimeofday(&t_stop_wr, NULL); + 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. + if (!card->is_mmc) { + erase_mem_byte = card->scr.erase_mem_state ? 0xFF : 0x00; + } else { + erase_mem_byte = card->ext_csd.erase_mem_state ? 0xFF : 0x00; + } + + memset((void *)pattern_buf, erase_mem_byte, block_size); + + size_t i; + for (i = 0; i < block_count; i++) { + memset((void *)temp_buf, 0, block_size); + TEST_ESP_OK(sdmmc_read_sectors(card, temp_buf, (start_block + i), 1)); + if (memcmp(pattern_buf, temp_buf, block_size)) { + printf("Error: Sector %d erase\n", (start_block + i)); + break; + } + } + if (i == block_count) { + printf("Sectors erase success\n"); + } + TEST_ESP_OK(sdspi_host_deinit()); + test_sdspi_deinit_bus(dev_config.host_id); + free(card); + free(temp_buf); + free(pattern_buf); + sd_test_board_power_off(); +} + +TEST_CASE("SDMMC erase (SPI mode)", "[sdspi][test_env=UT_T1_SPIMODE]") +{ + test_sdspi_erase_blocks(0, 16); +} +#endif // WITH_SDSPI_TEST + +#if WITH_SD_TEST +static void test_sd_erase_blocks(sdmmc_card_t* card) +{ + sdmmc_card_print_info(stdout, card); + printf("block size %d capacity %d\n", card->csd.sector_size, card->csd.capacity); + printf(" sector | count | size(kB) | er_time(ms) \n"); + /* + * bit-0: verify adjacent blocks of given range + * bit-1: verify erase state of blocks in range + */ + uint8_t flags = 0; + sdmmc_erase_arg_t arg = SDMMC_ERASE_ARG; + + //check for adjacent blocks and erase state of blocks + flags |= (uint8_t)FLAG_ERASE_TEST_ADJACENT | (uint8_t)FLAG_VERIFY_ERASE_STATE; + do_single_erase_test(card, 1, 16, flags, arg); + do_single_erase_test(card, 1, 13, flags, arg); + do_single_erase_test(card, 16, 32, flags, arg); + do_single_erase_test(card, 48, 64, flags, arg); + do_single_erase_test(card, 128, 128, flags, arg); + do_single_erase_test(card, card->csd.capacity - 64, 32, flags, arg); + do_single_erase_test(card, card->csd.capacity - 64, 64, flags, arg); + // single sector erase is failing on different make cards + do_single_erase_test(card, card->csd.capacity - 8, 1, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 1, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 4, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 8, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 16, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 32, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 64, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 128, flags, arg); +#ifdef SDMMC_FULL_ERASE_TEST + /* + * check for adjacent blocks, do not check erase state of blocks as it is + * time taking process to verify all the blocks. + */ + flags &= ~(uint8_t)FLAG_VERIFY_ERASE_STATE; //comment this line to verify after-erase state + // erase complete card + do_single_erase_test(card, 0, card->csd.capacity, flags, arg); +#endif //SDMMC_FULL_ERASE_TEST +} + +TEST_CASE("SDMMC erase test (SD slot 1, 1 line)", "[sd][test_env=UT_T1_SDMODE]") +{ + sd_test_board_power_on(); + sd_test_rw_blocks(1, 1, test_sd_erase_blocks); + sd_test_board_power_off(); +} + +TEST_CASE("SDMMC erase test (SD slot 1, 4 line)", "[sd][test_env=UT_T1_SDMODE]") +{ + sd_test_board_power_on(); + sd_test_rw_blocks(1, 4, test_sd_erase_blocks); + sd_test_board_power_off(); +} +#endif //WITH_SD_TEST + +#if WITH_EMMC_TEST +static void test_mmc_sanitize_blocks(sdmmc_card_t* card) +{ + /* MMC dicard applies to write blocks */ + sdmmc_card_print_info(stdout, card); + printf("block size %d capacity %d\n", card->csd.sector_size, card->csd.capacity); + + if (sdmmc_mmc_can_sanitize(card)) { + printf("Card/device do not support sanitize\n"); + return; + } + printf(" sector | count | size(kB) | er_time(ms) \n"); + /* + * bit-0: verify adjacent blocks of given range + * bit-1: verify erase state of blocks in range + */ + uint8_t flags = 0; + sdmmc_erase_arg_t arg = SDMMC_DISCARD_ARG; + do_sanitize_flag = true; + + /* + * Check for adjacent blocks only. + * After discard operation, the original data may be remained partially or + * fully accessible to the host dependent on device. Hence do not verify + * the erased state of the blocks. + * + * Note: After sanitize blocks has to be in erased state + */ + flags |= (uint8_t)FLAG_ERASE_TEST_ADJACENT | (uint8_t)FLAG_VERIFY_ERASE_STATE; + do_single_erase_test(card, 1, 16, flags, arg); + do_single_erase_test(card, 1, 13, flags, arg); + do_single_erase_test(card, 16, 32, flags, arg); + do_single_erase_test(card, 48, 64, flags, arg); + do_single_erase_test(card, 128, 128, flags, arg); + do_single_erase_test(card, card->csd.capacity - 64, 32, flags, arg); + do_single_erase_test(card, card->csd.capacity - 64, 64, flags, arg); + do_single_erase_test(card, card->csd.capacity - 8, 1, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 1, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 4, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 8, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 16, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 32, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 64, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 128, flags, arg); + do_sanitize_flag = false; +} + +static void test_mmc_discard_blocks(sdmmc_card_t* card) +{ + /* MMC dicard applies to write blocks */ + sdmmc_card_print_info(stdout, card); + printf("block size %d capacity %d\n", card->csd.sector_size, card->csd.capacity); + printf(" sector | count | size(kB) | er_time(ms) \n"); + /* + * bit-0: verify adjacent blocks of given range + * bit-1: verify erase state of blocks in range + */ + uint8_t flags = 0; + sdmmc_erase_arg_t arg = SDMMC_DISCARD_ARG; + + /* + * Check for adjacent blocks only. + * After discard operation, the original data may be remained partially or + * fully accessible to the host dependent on device. Hence do not verify + * the erased state of the blocks. + */ + flags |= (uint8_t)FLAG_ERASE_TEST_ADJACENT; + do_single_erase_test(card, 1, 16, flags, arg); + do_single_erase_test(card, 1, 13, flags, arg); + do_single_erase_test(card, 16, 32, flags, arg); + do_single_erase_test(card, 48, 64, flags, arg); + do_single_erase_test(card, 128, 128, flags, arg); + do_single_erase_test(card, card->csd.capacity - 64, 32, flags, arg); + do_single_erase_test(card, card->csd.capacity - 64, 64, flags, arg); + do_single_erase_test(card, card->csd.capacity - 8, 1, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 1, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 4, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 8, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 16, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 32, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 64, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 128, flags, arg); +} + +static void test_mmc_trim_blocks(sdmmc_card_t* card) +{ + /* MMC trim applies to write blocks */ + sdmmc_card_print_info(stdout, card); + printf("block size %d capacity %d\n", card->csd.sector_size, card->csd.capacity); + printf(" sector | count | size(kB) | er_time(ms) \n"); + /* + * bit-0: verify adjacent blocks of given range + * bit-1: verify erase state of blocks in range + */ + uint8_t flags = 0; + sdmmc_erase_arg_t arg = SDMMC_ERASE_ARG; + + //check for adjacent blocks and erase state of blocks + flags |= (uint8_t)FLAG_ERASE_TEST_ADJACENT | (uint8_t)FLAG_VERIFY_ERASE_STATE; + do_single_erase_test(card, 1, 16, flags, arg); + do_single_erase_test(card, 1, 13, flags, arg); + do_single_erase_test(card, 16, 32, flags, arg); + do_single_erase_test(card, 48, 64, flags, arg); + do_single_erase_test(card, 128, 128, flags, arg); + do_single_erase_test(card, card->csd.capacity - 64, 32, flags, arg); + do_single_erase_test(card, card->csd.capacity - 64, 64, flags, arg); + do_single_erase_test(card, card->csd.capacity - 8, 1, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 1, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 4, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 8, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 16, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 32, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 64, flags, arg); + do_single_erase_test(card, card->csd.capacity/2, 128, flags, arg); +#ifdef SDMMC_FULL_ERASE_TEST + /* + * check for adjacent blocks, do not check erase state of blocks as it is + * time taking process to verify all the blocks. + */ + flags &= ~(uint8_t)FLAG_VERIFY_ERASE_STATE; //comment this line to verify after erase state + // erase complete card + do_single_erase_test(card, 0, card->csd.capacity, flags, arg); +#endif //SDMMC_FULL_ERASE_TEST +} + +TEST_CASE("SDMMC trim test (eMMC slot 0, 4 line)", "[sd][test_env=EMMC]") +{ + sd_test_board_power_on(); + sd_test_rw_blocks(0, 4, test_mmc_trim_blocks); + sd_test_board_power_off(); +} + +TEST_CASE("SDMMC trim test (eMMC slot 0, 8 line)", "[sd][test_env=EMMC]") +{ + sd_test_board_power_on(); + sd_test_rw_blocks(0, 8, test_mmc_trim_blocks); + sd_test_board_power_off(); +} + +TEST_CASE("SDMMC discard test (eMMC slot 0, 4 line)", "[sd][test_env=EMMC]") +{ + sd_test_board_power_on(); + sd_test_rw_blocks(0, 4, test_mmc_discard_blocks); + sd_test_board_power_off(); +} + +TEST_CASE("SDMMC discard test (eMMC slot 0, 8 line)", "[sd][test_env=EMMC]") +{ + sd_test_board_power_on(); + sd_test_rw_blocks(0, 8, test_mmc_discard_blocks); + sd_test_board_power_off(); +} + +TEST_CASE("SDMMC sanitize test (eMMC slot 0, 4 line)", "[sd][test_env=EMMC]") +{ + sd_test_board_power_on(); + sd_test_rw_blocks(0, 4, test_mmc_sanitize_blocks); + sd_test_board_power_off(); +} + +TEST_CASE("SDMMC sanitize test (eMMC slot 0, 8 line)", "[sd][test_env=EMMC]") +{ + sd_test_board_power_on(); + sd_test_rw_blocks(0, 8, test_mmc_sanitize_blocks); + sd_test_board_power_off(); +} +#endif //WITH_EMMC_TEST diff --git a/tools/ci/check_copyright_config.yaml b/tools/ci/check_copyright_config.yaml index 2b252d3d1a..b3a98abb44 100644 --- a/tools/ci/check_copyright_config.yaml +++ b/tools/ci/check_copyright_config.yaml @@ -104,6 +104,14 @@ spiffs: - MIT - Apache-2.0 +sdmmc: + include: + - 'components/driver/include/driver/' + - 'components/sdmmc/' + allowed_licenses: + - Apache-2.0 + - ISC + # files matching this section do not perform the check # file patterns starting with ! are negated, meaning files matching them won't match the section. ignore: