sdmmc: wait for command done event even if data transfer is over

This fixes errors logged on the console: sdmmc_req:
handle_idle_state_events unhandled: 00000004 00000000

The issue happens if "data done" event occurs before "command done".
State machine code did not check *which* event occurred in
SENDING_CMD state, and went to IDLE or SENDING_DATA state on any
non-error event. In this case, we can't process "data done" event
until command has completed. This change introduces "unhandled event"
mask, which is carried over from one run of process_events to the
other. This allows waiting for the "command done" event to complete,
and then process "data done" event.

Ref TW17126.
This commit is contained in:
Ivan Grokhotkov 2018-04-20 17:48:34 +08:00 committed by Angus Gratton
parent cf81db40a2
commit 9379d7b9f9

View file

@ -74,8 +74,10 @@ static esp_pm_lock_handle_t s_pm_lock;
static esp_err_t handle_idle_state_events();
static sdmmc_hw_cmd_t make_hw_cmd(sdmmc_command_t* cmd);
static esp_err_t handle_event(sdmmc_command_t* cmd, sdmmc_req_state_t* pstate);
static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd, sdmmc_req_state_t* pstate);
static esp_err_t handle_event(sdmmc_command_t* cmd, sdmmc_req_state_t* state,
sdmmc_event_t* unhandled_events);
static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd,
sdmmc_req_state_t* pstate, sdmmc_event_t* unhandled_events);
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();
@ -157,8 +159,9 @@ esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo)
// process events until transfer is complete
cmdinfo->error = ESP_OK;
sdmmc_req_state_t state = SDMMC_SENDING_CMD;
sdmmc_event_t unhandled_events = { 0 };
while (state != SDMMC_IDLE) {
ret = handle_event(cmdinfo, &state);
ret = handle_event(cmdinfo, &state, &unhandled_events);
if (ret != ESP_OK) {
break;
}
@ -247,10 +250,11 @@ static esp_err_t handle_idle_state_events()
}
static esp_err_t handle_event(sdmmc_command_t* cmd, sdmmc_req_state_t* state)
static esp_err_t handle_event(sdmmc_command_t* cmd, sdmmc_req_state_t* state,
sdmmc_event_t* unhandled_events)
{
sdmmc_event_t evt;
esp_err_t err = sdmmc_host_wait_for_event(cmd->timeout_ms / portTICK_PERIOD_MS, &evt);
sdmmc_event_t event;
esp_err_t err = sdmmc_host_wait_for_event(cmd->timeout_ms / portTICK_PERIOD_MS, &event);
if (err != ESP_OK) {
ESP_LOGE(TAG, "sdmmc_host_wait_for_event returned 0x%x", err);
if (err == ESP_ERR_TIMEOUT) {
@ -258,8 +262,13 @@ static esp_err_t handle_event(sdmmc_command_t* cmd, sdmmc_req_state_t* state)
}
return err;
}
ESP_LOGV(TAG, "sdmmc_handle_event: evt %08x %08x", evt.sdmmc_status, evt.dma_status);
process_events(evt, cmd, state);
ESP_LOGV(TAG, "sdmmc_handle_event: event %08x %08x, unhandled %08x %08x",
event.sdmmc_status, event.dma_status,
unhandled_events->sdmmc_status, unhandled_events->dma_status);
event.sdmmc_status |= unhandled_events->sdmmc_status;
event.dma_status |= unhandled_events->dma_status;
process_events(event, cmd, state, unhandled_events);
ESP_LOGV(TAG, "sdmmc_handle_event: events unhandled: %08x %08x", unhandled_events->sdmmc_status, unhandled_events->dma_status);
return ESP_OK;
}
@ -365,7 +374,8 @@ static inline bool mask_check_and_clear(uint32_t* state, uint32_t mask) {
return ret;
}
static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd, sdmmc_req_state_t* pstate)
static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd,
sdmmc_req_state_t* pstate, sdmmc_event_t* unhandled_events)
{
const char* const s_state_names[] __attribute__((unused)) = {
"IDLE",
@ -374,7 +384,8 @@ static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd, sdmmc_r
"BUSY"
};
sdmmc_event_t orig_evt = evt;
ESP_LOGV(TAG, "%s: state=%s", __func__, s_state_names[*pstate]);
ESP_LOGV(TAG, "%s: state=%s evt=%x dma=%x", __func__, s_state_names[*pstate],
evt.sdmmc_status, evt.dma_status);
sdmmc_req_state_t next_state = *pstate;
sdmmc_req_state_t state = (sdmmc_req_state_t) -1;
while (next_state != state) {
@ -388,12 +399,19 @@ static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd, sdmmc_r
process_command_response(orig_evt.sdmmc_status, cmd);
break; // Need to wait for the CMD_DONE interrupt
}
process_command_response(orig_evt.sdmmc_status, cmd);
if (cmd->error != ESP_OK || cmd->data == NULL) {
next_state = SDMMC_IDLE;
break;
if (mask_check_and_clear(&evt.sdmmc_status, SDMMC_INTMASK_CMD_DONE)) {
process_command_response(orig_evt.sdmmc_status, cmd);
if (cmd->error != ESP_OK) {
next_state = SDMMC_IDLE;
break;
}
if (cmd->data == NULL) {
next_state = SDMMC_IDLE;
} else {
next_state = SDMMC_SENDING_DATA;
}
}
next_state = SDMMC_SENDING_DATA;
break;
@ -431,6 +449,7 @@ static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd, sdmmc_r
ESP_LOGV(TAG, "%s state=%s next_state=%s", __func__, s_state_names[state], s_state_names[next_state]);
}
*pstate = state;
*unhandled_events = evt;
return ESP_OK;
}