[SDMMC Mount] fix infinite loop when SD card is not responsive

Closes: https://github.com/espressif/esp-idf/pull/10532
This commit is contained in:
Chip Weinberger
2023-01-12 03:04:42 -08:00
committed by Adam Múdry
parent cbd210b431
commit 6ff1059da7
2 changed files with 53 additions and 10 deletions

View File

@@ -25,6 +25,8 @@
#define SDMMC_EVENT_QUEUE_LENGTH 32 #define SDMMC_EVENT_QUEUE_LENGTH 32
#define SDMMC_TIMEOUT_MS 1000
static void sdmmc_isr(void* arg); static void sdmmc_isr(void* arg);
static void sdmmc_host_dma_init(void); static void sdmmc_host_dma_init(void);
@@ -68,16 +70,22 @@ static void configure_pin_iomux(uint8_t gpio_num);
static esp_err_t sdmmc_host_pullup_en_internal(int slot, int width); static esp_err_t sdmmc_host_pullup_en_internal(int slot, int width);
void sdmmc_host_reset(void) esp_err_t sdmmc_host_reset(void)
{ {
// Set reset bits // Set reset bits
SDMMC.ctrl.controller_reset = 1; SDMMC.ctrl.controller_reset = 1;
SDMMC.ctrl.dma_reset = 1; SDMMC.ctrl.dma_reset = 1;
SDMMC.ctrl.fifo_reset = 1; SDMMC.ctrl.fifo_reset = 1;
// Wait for the reset bits to be cleared by hardware // Wait for the reset bits to be cleared by hardware
int t0 = esp_timer_get_time();
while (SDMMC.ctrl.controller_reset || SDMMC.ctrl.fifo_reset || SDMMC.ctrl.dma_reset) { while (SDMMC.ctrl.controller_reset || SDMMC.ctrl.fifo_reset || SDMMC.ctrl.dma_reset) {
; if (esp_timer_get_time() - t0 > SDMMC_TIMEOUT_MS) {
return ESP_ERR_TIMEOUT;
}
} }
return ESP_OK;
} }
/* We have two clock divider stages: /* We have two clock divider stages:
@@ -172,7 +180,7 @@ static void sdmmc_host_input_clk_disable(void)
SDMMC.clock.val = 0; SDMMC.clock.val = 0;
} }
static void sdmmc_host_clock_update_command(int slot) static esp_err_t sdmmc_host_clock_update_command(int slot)
{ {
// Clock update command (not a real command; just updates CIU registers) // Clock update command (not a real command; just updates CIU registers)
sdmmc_hw_cmd_t cmd_val = { sdmmc_hw_cmd_t cmd_val = {
@@ -182,8 +190,20 @@ static void sdmmc_host_clock_update_command(int slot)
}; };
bool repeat = true; bool repeat = true;
while(repeat) { while(repeat) {
sdmmc_host_start_command(slot, cmd_val, 0);
esp_err_t err = sdmmc_host_start_command(slot, cmd_val, 0);
if (err != ESP_OK) {
ESP_LOGE(TAG, "sdmmc_host_start_command() failed");
return err;
}
int t0 = esp_timer_get_time();
while (true) { while (true) {
if (esp_timer_get_time() - t0 > SDMMC_TIMEOUT_MS) {
return ESP_ERR_TIMEOUT;
}
// Sending clock update command to the CIU can generate HLE error. // Sending clock update command to the CIU can generate HLE error.
// According to the manual, this is okay and we must retry the command. // According to the manual, this is okay and we must retry the command.
if (SDMMC.rintsts.hle) { if (SDMMC.rintsts.hle) {
@@ -199,6 +219,8 @@ static void sdmmc_host_clock_update_command(int slot)
} }
} }
} }
return ESP_OK;
} }
void sdmmc_host_get_clk_dividers(const uint32_t freq_khz, int *host_div, int *card_div) void sdmmc_host_get_clk_dividers(const uint32_t freq_khz, int *host_div, int *card_div)
@@ -245,7 +267,11 @@ esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz)
// Disable clock first // Disable clock first
SDMMC.clkena.cclk_enable &= ~BIT(slot); SDMMC.clkena.cclk_enable &= ~BIT(slot);
sdmmc_host_clock_update_command(slot); esp_err_t err = sdmmc_host_clock_update_command(slot);
if (err != ESP_OK) {
ESP_LOGE(TAG, "disable clk failed");
return err;
}
int host_div = 0; /* clock divider of the host (SDMMC.clock) */ int host_div = 0; /* clock divider of the host (SDMMC.clock) */
int card_div = 0; /* 1/2 of card clock divider (SDMMC.clkdiv) */ int card_div = 0; /* 1/2 of card clock divider (SDMMC.clkdiv) */
@@ -266,12 +292,20 @@ esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz)
break; break;
} }
sdmmc_host_set_clk_div(host_div); sdmmc_host_set_clk_div(host_div);
sdmmc_host_clock_update_command(slot); err = sdmmc_host_clock_update_command(slot);
if (err != ESP_OK) {
ESP_LOGE(TAG, "set clk div failed");
return err;
}
// Re-enable clocks // Re-enable clocks
SDMMC.clkena.cclk_enable |= BIT(slot); SDMMC.clkena.cclk_enable |= BIT(slot);
SDMMC.clkena.cclk_low_power |= BIT(slot); SDMMC.clkena.cclk_low_power |= BIT(slot);
sdmmc_host_clock_update_command(slot); err = sdmmc_host_clock_update_command(slot);
if (err != ESP_OK) {
ESP_LOGE(TAG, "re-enable clk failed");
return err;
}
// set data timeout // set data timeout
const uint32_t data_timeout_ms = 100; const uint32_t data_timeout_ms = 100;
@@ -315,8 +349,11 @@ esp_err_t sdmmc_host_start_command(int slot, sdmmc_hw_cmd_t cmd, uint32_t arg) {
/* Outputs should be synchronized to cclk_out */ /* Outputs should be synchronized to cclk_out */
cmd.use_hold_reg = 1; cmd.use_hold_reg = 1;
int t0 = esp_timer_get_time();
while (SDMMC.cmd.start_command == 1) { while (SDMMC.cmd.start_command == 1) {
; if (esp_timer_get_time() - t0 > SDMMC_TIMEOUT_MS) {
return ESP_ERR_TIMEOUT;
}
} }
SDMMC.cmdarg = arg; SDMMC.cmdarg = arg;
cmd.card_num = slot; cmd.card_num = slot;
@@ -338,7 +375,12 @@ esp_err_t sdmmc_host_init(void)
sdmmc_host_set_clk_div(2); sdmmc_host_set_clk_div(2);
// Reset // Reset
sdmmc_host_reset(); esp_err_t err = sdmmc_host_reset();
if (err != ESP_OK) {
ESP_LOGE(TAG, "sdmmc_host_reset() failed");
return err;
}
ESP_LOGD(TAG, "peripheral version %"PRIx32", hardware config %08"PRIx32, SDMMC.verid, SDMMC.hcon); ESP_LOGD(TAG, "peripheral version %"PRIx32", hardware config %08"PRIx32, SDMMC.verid, SDMMC.hcon);
// Clear interrupt status and set interrupt mask to known state // Clear interrupt status and set interrupt mask to known state
@@ -545,6 +587,7 @@ esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t* slot_config)
// By default, set probing frequency (400kHz) and 1-bit bus // By default, set probing frequency (400kHz) and 1-bit bus
esp_err_t ret = sdmmc_host_set_card_clk(slot, 400); esp_err_t ret = sdmmc_host_set_card_clk(slot, 400);
if (ret != ESP_OK) { if (ret != ESP_OK) {
ESP_LOGE(TAG, "sdmmc_host_set_card_clk() 400kHz failed");
return ret; return ret;
} }
ret = sdmmc_host_set_bus_width(slot, 1); ret = sdmmc_host_set_bus_width(slot, 1);

View File

@@ -18,7 +18,7 @@ typedef struct {
uint32_t dma_status; ///< masked DMA interrupt status uint32_t dma_status; ///< masked DMA interrupt status
} sdmmc_event_t; } sdmmc_event_t;
void sdmmc_host_reset(void); esp_err_t sdmmc_host_reset(void);
esp_err_t sdmmc_host_start_command(int slot, sdmmc_hw_cmd_t cmd, uint32_t arg); esp_err_t sdmmc_host_start_command(int slot, sdmmc_hw_cmd_t cmd, uint32_t arg);