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:
parent
cf81db40a2
commit
9379d7b9f9
|
@ -74,8 +74,10 @@ static esp_pm_lock_handle_t s_pm_lock;
|
||||||
|
|
||||||
static esp_err_t handle_idle_state_events();
|
static esp_err_t handle_idle_state_events();
|
||||||
static sdmmc_hw_cmd_t make_hw_cmd(sdmmc_command_t* cmd);
|
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 handle_event(sdmmc_command_t* cmd, sdmmc_req_state_t* state,
|
||||||
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 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 process_command_response(uint32_t status, sdmmc_command_t* cmd);
|
||||||
static void fill_dma_descriptors(size_t num_desc);
|
static void fill_dma_descriptors(size_t num_desc);
|
||||||
static size_t get_free_descriptors_count();
|
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
|
// process events until transfer is complete
|
||||||
cmdinfo->error = ESP_OK;
|
cmdinfo->error = ESP_OK;
|
||||||
sdmmc_req_state_t state = SDMMC_SENDING_CMD;
|
sdmmc_req_state_t state = SDMMC_SENDING_CMD;
|
||||||
|
sdmmc_event_t unhandled_events = { 0 };
|
||||||
while (state != SDMMC_IDLE) {
|
while (state != SDMMC_IDLE) {
|
||||||
ret = handle_event(cmdinfo, &state);
|
ret = handle_event(cmdinfo, &state, &unhandled_events);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
break;
|
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;
|
sdmmc_event_t event;
|
||||||
esp_err_t err = sdmmc_host_wait_for_event(cmd->timeout_ms / portTICK_PERIOD_MS, &evt);
|
esp_err_t err = sdmmc_host_wait_for_event(cmd->timeout_ms / portTICK_PERIOD_MS, &event);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "sdmmc_host_wait_for_event returned 0x%x", err);
|
ESP_LOGE(TAG, "sdmmc_host_wait_for_event returned 0x%x", err);
|
||||||
if (err == ESP_ERR_TIMEOUT) {
|
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;
|
return err;
|
||||||
}
|
}
|
||||||
ESP_LOGV(TAG, "sdmmc_handle_event: evt %08x %08x", evt.sdmmc_status, evt.dma_status);
|
ESP_LOGV(TAG, "sdmmc_handle_event: event %08x %08x, unhandled %08x %08x",
|
||||||
process_events(evt, cmd, state);
|
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;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,7 +374,8 @@ static inline bool mask_check_and_clear(uint32_t* state, uint32_t mask) {
|
||||||
return ret;
|
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)) = {
|
const char* const s_state_names[] __attribute__((unused)) = {
|
||||||
"IDLE",
|
"IDLE",
|
||||||
|
@ -374,7 +384,8 @@ static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd, sdmmc_r
|
||||||
"BUSY"
|
"BUSY"
|
||||||
};
|
};
|
||||||
sdmmc_event_t orig_evt = evt;
|
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 next_state = *pstate;
|
||||||
sdmmc_req_state_t state = (sdmmc_req_state_t) -1;
|
sdmmc_req_state_t state = (sdmmc_req_state_t) -1;
|
||||||
while (next_state != state) {
|
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);
|
process_command_response(orig_evt.sdmmc_status, cmd);
|
||||||
break; // Need to wait for the CMD_DONE interrupt
|
break; // Need to wait for the CMD_DONE interrupt
|
||||||
}
|
}
|
||||||
process_command_response(orig_evt.sdmmc_status, cmd);
|
if (mask_check_and_clear(&evt.sdmmc_status, SDMMC_INTMASK_CMD_DONE)) {
|
||||||
if (cmd->error != ESP_OK || cmd->data == NULL) {
|
process_command_response(orig_evt.sdmmc_status, cmd);
|
||||||
next_state = SDMMC_IDLE;
|
if (cmd->error != ESP_OK) {
|
||||||
break;
|
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;
|
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]);
|
ESP_LOGV(TAG, "%s state=%s next_state=%s", __func__, s_state_names[state], s_state_names[next_state]);
|
||||||
}
|
}
|
||||||
*pstate = state;
|
*pstate = state;
|
||||||
|
*unhandled_events = evt;
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue