Merge branch 'fix/sdmmc_send_acmd22_after_multiple_write_cmd_v5.4' into 'release/v5.4'

fix(sdmmc): Send ACMD22 if CMD25 fails (v5.4)

See merge request espressif/esp-idf!34860
This commit is contained in:
Martin Vychodil 2024-11-28 02:11:25 +08:00
commit 60c561792c
11 changed files with 208 additions and 62 deletions

View File

@ -9,7 +9,8 @@ if(CONFIG_SOC_SDMMC_HOST_SUPPORTED)
"sdmmc_test_erase_sd.c"
"sdmmc_test_trim_sd.c"
"sdmmc_test_discard_sd.c"
"sdmmc_test_sanitize_sd.c")
"sdmmc_test_sanitize_sd.c"
"sdmmc_test_various_cmds.c")
endif()
set(priv_requires "sdmmc"

View File

@ -0,0 +1,73 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include <stdlib.h>
#include <string.h>
#include "unity.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/sdmmc_defs.h"
#include "driver/sdmmc_host.h"
#include "sdmmc_cmd.h"
#include "esp_private/sdmmc_common.h"
#include "sdmmc_test_begin_end_sd.h"
#if !SOC_SDMMC_HOST_SUPPORTED
#error "Targets with SDMMC host supported only"
#endif // !SOC_SDMMC_HOST_SUPPORTED
static const char* TAG = "sdmmc_test_various_cmds";
static void sdmmc_write_sectors_cmd25_error_test_acmd22(sdmmc_card_t* card, uint32_t write_size)
{
esp_err_t err = ESP_OK;
size_t block_size = (size_t)card->csd.sector_size;
size_t block_count = write_size / block_size;
void* buf = heap_caps_calloc(1, write_size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
TEST_ASSERT_NOT_NULL(buf);
if (!host_is_spi(card)) {
err = sdmmc_wait_for_idle(card, 0); // wait for the card to be idle (in transfer state)
if (err != ESP_OK) {
free(buf);
}
TEST_ESP_OK(err);
}
// Try to write to the card
err = sdmmc_write_sectors(card, buf, 0, block_count);
free(buf);
TEST_ESP_OK(err);
// Check if the number of written blocks is equal to the number ACMD22 returns
// ACMD22 is usually only called if CMD25 fails but here we call it anyway to test it
size_t sucessfully_written_blocks;
err = sdmmc_send_cmd_num_of_written_blocks(card, &sucessfully_written_blocks);
TEST_ESP_OK(err);
TEST_ASSERT_EQUAL_size_t(sucessfully_written_blocks, block_count);
ESP_LOGI(TAG, "%s: ACMD22 successfully written %zu blocks out of %zu", __func__, sucessfully_written_blocks, block_count);
}
static void do_one_mmc_acmd22_test(int slot, int width, int freq_khz, int ddr)
{
sdmmc_card_t card;
sdmmc_test_sd_skip_if_board_incompatible(slot, width, freq_khz, ddr);
sdmmc_test_sd_begin(slot, width, freq_khz, ddr, &card);
sdmmc_card_print_info(stdout, &card);
sdmmc_write_sectors_cmd25_error_test_acmd22(&card, 4096 * 4);
sdmmc_test_sd_end(&card);
}
TEST_CASE("send ACMD22 after writing multiple blocks to check real number of successfully written blocks, slot 0, 1-bit", "[sdmmc]")
{
do_one_mmc_acmd22_test(SLOT_0, 1, SDMMC_FREQ_HIGHSPEED, NO_DDR);
}
TEST_CASE("send ACMD22 after writing multiple blocks to check real number of successfully written blocks, slot 1, 1-bit", "[sdmmc]")
{
do_one_mmc_acmd22_test(SLOT_1, 1, SDMMC_FREQ_HIGHSPEED, NO_DDR);
}

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
* Adaptations to ESP-IDF Copyright (c) 2016-2024 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -30,6 +30,10 @@
#include "soc/soc_caps.h"
#include "esp_dma_utils.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SDMMC_GO_IDLE_DELAY_MS 20
#define SDMMC_IO_SEND_OP_COND_DELAY_MS 10
@ -82,6 +86,7 @@ esp_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width);
esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status);
esp_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable);
esp_err_t sdmmc_send_cmd_voltage_switch(sdmmc_card_t* card);
esp_err_t sdmmc_send_cmd_num_of_written_blocks(sdmmc_card_t* card, size_t* out_num_blocks);
/* Higher level functions */
esp_err_t sdmmc_enter_higher_speed_mode(sdmmc_card_t* card);
@ -166,10 +171,22 @@ static inline uint32_t get_host_ocr(float voltage)
return SD_OCR_VOL_MASK;
}
static inline bool sdmmc_ready_for_data(uint32_t status)
{
return (status & MMC_R1_READY_FOR_DATA) && (MMC_R1_CURRENT_STATE_STATUS(status) == MMC_R1_CURRENT_STATE_TRAN);
}
void sdmmc_flip_byte_order(uint32_t* response, size_t size);
esp_err_t sdmmc_fix_host_flags(sdmmc_card_t* card);
// Use only with SDMMC mode (not SDSPI)
esp_err_t sdmmc_wait_for_idle(sdmmc_card_t* card, uint32_t status);
//Currently only SDIO support using this buffer. And only 512 block size is supported.
#define SDMMC_IO_BLOCK_SIZE 512
esp_err_t sdmmc_allocate_aligned_buf(sdmmc_card_t* card);
#ifdef __cplusplus
}
#endif

View File

@ -69,6 +69,7 @@ extern "C" {
/* SD application commands */ /* response type */
#define SD_APP_SET_BUS_WIDTH 6 /* R1 */
#define SD_APP_SD_STATUS 13 /* R2 */
#define SD_APP_SEND_NUM_WR_BLOCKS 22 /* R1 */
#define SD_APP_OP_COND 41 /* R3 */
#define SD_APP_SEND_SCR 51 /* R1 */
@ -261,6 +262,7 @@ extern "C" {
#define MMC_CSD_CAPACITY(resp) ((MMC_CSD_C_SIZE((resp))+1) << \
(MMC_CSD_C_SIZE_MULT((resp))+2))
#define MMC_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3)
#define MMC_CSD_WRITE_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 21, 1)
/* MMC v1 R2 response (CID) */
#define MMC_CID_MID_V1(resp) MMC_RSP_BITS((resp), 104, 24)

View File

@ -5,8 +5,7 @@
*/
#include <inttypes.h>
#include "esp_timer.h"
#include "sdmmc_common.h"
#include "esp_private/sdmmc_common.h"
static const char* TAG = "sdmmc_cmd";
@ -409,6 +408,43 @@ esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status)
return ESP_OK;
}
esp_err_t sdmmc_send_cmd_num_of_written_blocks(sdmmc_card_t* card, size_t* out_num_blocks)
{
size_t datalen = sizeof(uint32_t);
esp_err_t err = ESP_OK;
void* buf = NULL;
esp_dma_mem_info_t dma_mem_info;
card->host.get_dma_info(card->host.slot, &dma_mem_info);
size_t actual_size = 0;
err = esp_dma_capable_malloc(datalen, &dma_mem_info, &buf, &actual_size);
if (err != ESP_OK) {
return err;
}
sdmmc_command_t cmd = {
.data = buf,
.datalen = datalen,
.buflen = actual_size,
.blklen = datalen,
.flags = SCF_CMD_ADTC | SCF_RSP_R1 | SCF_CMD_READ,
.opcode = SD_APP_SEND_NUM_WR_BLOCKS
};
err = sdmmc_send_app_cmd(card, &cmd);
if (err != ESP_OK) {
free(buf);
ESP_LOGE(TAG, "%s: sdmmc_send_app_cmd returned 0x%x, failed to get number of written write blocks", __func__, err);
return err;
}
size_t result = __builtin_bswap32(*(uint32_t*)buf);
if (out_num_blocks) {
*out_num_blocks = result;
}
free(buf);
return err;
}
esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src,
size_t start_block, size_t block_count)
{
@ -459,11 +495,6 @@ esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src,
return err;
}
static bool sdmmc_ready_for_data(uint32_t status)
{
return (status & MMC_R1_READY_FOR_DATA) && (MMC_R1_CURRENT_STATE_STATUS(status) == MMC_R1_CURRENT_STATE_TRAN);
}
esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src,
size_t start_block, size_t block_count, size_t buffer_len)
{
@ -495,6 +526,19 @@ esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src,
esp_err_t err_cmd13 = sdmmc_send_cmd_send_status(card, &status);
if (err != ESP_OK) {
if (cmd.opcode == MMC_WRITE_BLOCK_MULTIPLE) {
if (!host_is_spi(card)) {
sdmmc_wait_for_idle(card, status); // wait for the card to be idle (in transfer state)
} else {
vTaskDelay(1); // when the host is in spi mode
}
size_t successfully_written_blocks = 0;
if (sdmmc_send_cmd_num_of_written_blocks(card, &successfully_written_blocks) == ESP_OK) {
ESP_LOGD(TAG, "%s: successfully wrote %zu blocks out of %zu", __func__, successfully_written_blocks, block_count);
} else {
ESP_LOGE(TAG, "%s: sdmmc_send_cmd_num_of_written_blocks returned 0x%x", __func__, err);
}
}
if (err_cmd13 == ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x, status 0x%" PRIx32, __func__, err, status);
} else {
@ -503,30 +547,19 @@ esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src,
return err;
}
size_t count = 0;
int64_t yield_delay_us = 100 * 1000; // initially 100ms
int64_t t0 = esp_timer_get_time();
int64_t t1 = 0;
/* SD mode: wait for the card to become idle based on R1 status */
while (!host_is_spi(card) && !sdmmc_ready_for_data(status)) {
t1 = esp_timer_get_time();
if (t1 - t0 > SDMMC_READY_FOR_DATA_TIMEOUT_US) {
ESP_LOGE(TAG, "write sectors dma - timeout");
return ESP_ERR_TIMEOUT;
}
if (t1 - t0 > yield_delay_us) {
yield_delay_us *= 2;
vTaskDelay(1);
}
err = sdmmc_send_cmd_send_status(card, &status);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_send_cmd_send_status returned 0x%x", __func__, err);
return err;
}
if (++count % 16 == 0) {
ESP_LOGV(TAG, "waiting for card to become ready (%" PRIu32 ")", (uint32_t) count);
if (!host_is_spi(card)) {
switch (sdmmc_wait_for_idle(card, status)) {
case ESP_OK:
break;
case ESP_ERR_TIMEOUT:
ESP_LOGE(TAG, "%s: sdmmc_wait_for_idle timeout", __func__);
return ESP_ERR_TIMEOUT;
default:
return err;
}
}
/* 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
@ -633,28 +666,16 @@ esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
return err;
}
size_t count = 0;
int64_t yield_delay_us = 100 * 1000; // initially 100ms
int64_t t0 = esp_timer_get_time();
int64_t t1 = 0;
/* SD mode: wait for the card to become idle based on R1 status */
while (!host_is_spi(card) && !sdmmc_ready_for_data(status)) {
t1 = esp_timer_get_time();
if (t1 - t0 > SDMMC_READY_FOR_DATA_TIMEOUT_US) {
ESP_LOGE(TAG, "read sectors dma - timeout");
return ESP_ERR_TIMEOUT;
}
if (t1 - t0 > yield_delay_us) {
yield_delay_us *= 2;
vTaskDelay(1);
}
err = sdmmc_send_cmd_send_status(card, &status);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_send_cmd_send_status returned 0x%x", __func__, err);
return err;
}
if (++count % 16 == 0) {
ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
if (!host_is_spi(card)) {
switch (sdmmc_wait_for_idle(card, status)) {
case ESP_OK:
break;
case ESP_ERR_TIMEOUT:
ESP_LOGE(TAG, "%s: sdmmc_wait_for_idle timeout", __func__);
return ESP_ERR_TIMEOUT;
default:
return err;
}
}
return ESP_OK;

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
* Adaptations to ESP-IDF Copyright (c) 2016-2024 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -16,7 +16,9 @@
*/
#include <inttypes.h>
#include "sdmmc_common.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "esp_private/sdmmc_common.h"
static const char* TAG = "sdmmc_common";
@ -415,3 +417,33 @@ uint32_t sdmmc_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t er
return sdmmc_sd_get_erase_timeout_ms(card, arg, erase_size_kb);
}
}
esp_err_t sdmmc_wait_for_idle(sdmmc_card_t* card, uint32_t status)
{
assert(!host_is_spi(card));
esp_err_t err = ESP_OK;
size_t count = 0;
int64_t yield_delay_us = 100 * 1000; // initially 100ms
int64_t t0 = esp_timer_get_time();
int64_t t1 = 0;
/* SD mode: wait for the card to become idle based on R1 status */
while (!sdmmc_ready_for_data(status)) {
t1 = esp_timer_get_time();
if (t1 - t0 > SDMMC_READY_FOR_DATA_TIMEOUT_US) {
return ESP_ERR_TIMEOUT;
}
if (t1 - t0 > yield_delay_us) {
yield_delay_us *= 2;
vTaskDelay(1);
}
err = sdmmc_send_cmd_send_status(card, &status);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_send_cmd_send_status returned 0x%x", __func__, err);
return err;
}
if (++count % 16 == 0) {
ESP_LOGV(TAG, "waiting for card to become ready (%" PRIu32 ")", (uint32_t) count);
}
}
return err;
}

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
* Adaptations to ESP-IDF Copyright (c) 2016-2024 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -15,7 +15,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "sdmmc_common.h"
#include "esp_private/sdmmc_common.h"
#include "sd_pwr_ctrl_by_on_chip_ldo.h"
#include "sd_pwr_ctrl.h"

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
* Adaptations to ESP-IDF Copyright (c) 2016-2024 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -16,7 +16,7 @@
*/
#include <inttypes.h>
#include "sdmmc_common.h"
#include "esp_private/sdmmc_common.h"
#include "esp_attr.h"
#include "esp_compiler.h"

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
* Adaptations to ESP-IDF Copyright (c) 2016-2024 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -17,7 +17,7 @@
#include <inttypes.h>
#include <unistd.h>
#include "sdmmc_common.h"
#include "esp_private/sdmmc_common.h"
static const char* TAG = "sdmmc_mmc";

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
* Adaptations to ESP-IDF Copyright (c) 2016-2024 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -19,7 +19,7 @@
#include "esp_check.h"
#include "esp_timer.h"
#include "esp_cache.h"
#include "sdmmc_common.h"
#include "esp_private/sdmmc_common.h"
static const char* TAG = "sdmmc_sd";

View File

@ -503,8 +503,8 @@ components/protocomm/include/transports/protocomm_console.h
components/protocomm/include/transports/protocomm_httpd.h
components/riscv/include/riscv/csr.h
components/riscv/include/riscv/encoding.h
components/sdmmc/include/esp_private/sdmmc_common.h
components/sdmmc/sdmmc_common.c
components/sdmmc/sdmmc_common.h
components/sdmmc/sdmmc_init.c
components/sdmmc/sdmmc_io.c
components/sdmmc/sdmmc_mmc.c