From 3cf23ff77d958e211114199324232ba80ed14012 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 13 Oct 2017 08:16:07 +0800 Subject: [PATCH] sdmmc: allow command timeouts to be configured Previously the timeout was set to the same value (1000ms) for all kinds of commands. In some cases, such as with slow cards, write commands failed to complete in time. This change makes command timeouts configurable via sdmmc_host_t structure, and also makes default timeouts different for ordinary commands and write commands. Closes https://github.com/espressif/esp-idf/issues/1093 Ref TW15774. --- .../driver/include/driver/sdmmc_types.h | 2 ++ components/driver/sdmmc_transaction.c | 9 +------- components/sdmmc/sdmmc_cmd.c | 21 ++++++++++++++++--- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/components/driver/include/driver/sdmmc_types.h b/components/driver/include/driver/sdmmc_types.h index 4bc1e05e5..835eaa3fb 100644 --- a/components/driver/include/driver/sdmmc_types.h +++ b/components/driver/include/driver/sdmmc_types.h @@ -102,6 +102,7 @@ typedef struct { #define SCF_RSP_R6 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) #define SCF_RSP_R7 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) esp_err_t error; /*!< error returned from transfer */ + int timeout_ms; /*!< response timeout, in milliseconds */ } sdmmc_command_t; /** @@ -127,6 +128,7 @@ typedef struct { esp_err_t (*set_card_clk)(int slot, uint32_t freq_khz); /*!< host function to set card clock frequency */ esp_err_t (*do_transaction)(int slot, sdmmc_command_t* cmdinfo); /*!< host function to do a transaction */ esp_err_t (*deinit)(void); /*!< host function to deinitialize the driver */ + int command_timeout_ms; /*!< timeout, in milliseconds, of a single command. Set to 0 to use the default value. */ } sdmmc_host_t; /** diff --git a/components/driver/sdmmc_transaction.c b/components/driver/sdmmc_transaction.c index 8f06be88e..a0ff102db 100644 --- a/components/driver/sdmmc_transaction.c +++ b/components/driver/sdmmc_transaction.c @@ -33,13 +33,6 @@ */ #define SDMMC_DMA_DESC_CNT 4 -/* Max delay value is mostly useful for cases when CD pin is not used, and - * the card is removed. In this case, SDMMC peripheral may not always return - * CMD_DONE / DATA_DONE interrupts after signaling the error. This delay works - * as a safety net in such cases. - */ -#define SDMMC_MAX_EVT_WAIT_DELAY_MS 1000 - static const char* TAG = "sdmmc_req"; typedef enum { @@ -206,7 +199,7 @@ static esp_err_t handle_idle_state_events() static esp_err_t handle_event(sdmmc_command_t* cmd, sdmmc_req_state_t* state) { sdmmc_event_t evt; - esp_err_t err = sdmmc_host_wait_for_event(SDMMC_MAX_EVT_WAIT_DELAY_MS / portTICK_PERIOD_MS, &evt); + esp_err_t err = sdmmc_host_wait_for_event(cmd->timeout_ms / portTICK_PERIOD_MS, &evt); if (err != ESP_OK) { ESP_LOGE(TAG, "sdmmc_host_wait_for_event returned 0x%x", err); if (err == ESP_ERR_TIMEOUT) { diff --git a/components/sdmmc/sdmmc_cmd.c b/components/sdmmc/sdmmc_cmd.c index 3b962d432..a55440225 100644 --- a/components/sdmmc/sdmmc_cmd.c +++ b/components/sdmmc/sdmmc_cmd.c @@ -28,6 +28,14 @@ #define SDMMC_GO_IDLE_DELAY_MS 20 +/* These delay values are mostly useful for cases when CD pin is not used, and + * the card is removed. In this case, SDMMC peripheral may not always return + * CMD_DONE / DATA_DONE interrupts after signaling the error. These timeouts work + * as a safety net in such cases. + */ +#define SDMMC_DEFAULT_CMD_TIMEOUT_MS 1000 // Max timeout of ordinary commands +#define SDMMC_WRITE_CMD_TIMEOUT_MS 5000 // Max timeout of write commands + static const char* TAG = "sdmmc_cmd"; static esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd); @@ -344,9 +352,15 @@ void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card) static esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd) { + if (card->host.command_timeout_ms != 0) { + cmd->timeout_ms = card->host.command_timeout_ms; + } else if (cmd->timeout_ms == 0) { + cmd->timeout_ms = SDMMC_DEFAULT_CMD_TIMEOUT_MS; + } + int slot = card->host.slot; - ESP_LOGV(TAG, "sending cmd slot=%d op=%d arg=%x flags=%x data=%p blklen=%d datalen=%d", - slot, cmd->opcode, cmd->arg, cmd->flags, cmd->data, cmd->blklen, cmd->datalen); + ESP_LOGV(TAG, "sending cmd slot=%d op=%d arg=%x flags=%x data=%p blklen=%d datalen=%d timeout=%d", + slot, cmd->opcode, cmd->arg, cmd->flags, cmd->data, cmd->blklen, cmd->datalen, cmd->timeout_ms); esp_err_t err = (*card->host.do_transaction)(slot, cmd); if (err != 0) { ESP_LOGD(TAG, "sdmmc_req_run returned 0x%x", err); @@ -758,7 +772,8 @@ static esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src, .flags = SCF_CMD_ADTC | SCF_RSP_R1, .blklen = block_size, .data = (void*) src, - .datalen = block_count * block_size + .datalen = block_count * block_size, + .timeout_ms = SDMMC_WRITE_CMD_TIMEOUT_MS }; if (block_count == 1) { cmd.opcode = MMC_WRITE_BLOCK_SINGLE;