in host/dw_mmc.c [2061:2288]
static void dw_mci_tasklet_func(struct tasklet_struct *t)
{
struct dw_mci *host = from_tasklet(host, t, tasklet);
struct mmc_data *data;
struct mmc_command *cmd;
struct mmc_request *mrq;
enum dw_mci_state state;
enum dw_mci_state prev_state;
unsigned int err;
spin_lock(&host->lock);
state = host->state;
data = host->data;
mrq = host->mrq;
do {
prev_state = state;
switch (state) {
case STATE_IDLE:
case STATE_WAITING_CMD11_DONE:
break;
case STATE_SENDING_CMD11:
case STATE_SENDING_CMD:
if (!dw_mci_clear_pending_cmd_complete(host))
break;
cmd = host->cmd;
host->cmd = NULL;
set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
err = dw_mci_command_complete(host, cmd);
if (cmd == mrq->sbc && !err) {
__dw_mci_start_request(host, host->slot,
mrq->cmd);
goto unlock;
}
if (cmd->data && err) {
/*
* During UHS tuning sequence, sending the stop
* command after the response CRC error would
* throw the system into a confused state
* causing all future tuning phases to report
* failure.
*
* In such case controller will move into a data
* transfer state after a response error or
* response CRC error. Let's let that finish
* before trying to send a stop, so we'll go to
* STATE_SENDING_DATA.
*
* Although letting the data transfer take place
* will waste a bit of time (we already know
* the command was bad), it can't cause any
* errors since it's possible it would have
* taken place anyway if this tasklet got
* delayed. Allowing the transfer to take place
* avoids races and keeps things simple.
*/
if (err != -ETIMEDOUT &&
host->dir_status == DW_MCI_RECV_STATUS) {
state = STATE_SENDING_DATA;
continue;
}
send_stop_abort(host, data);
dw_mci_stop_dma(host);
state = STATE_SENDING_STOP;
break;
}
if (!cmd->data || err) {
dw_mci_request_end(host, mrq);
goto unlock;
}
prev_state = state = STATE_SENDING_DATA;
fallthrough;
case STATE_SENDING_DATA:
/*
* We could get a data error and never a transfer
* complete so we'd better check for it here.
*
* Note that we don't really care if we also got a
* transfer complete; stopping the DMA and sending an
* abort won't hurt.
*/
if (test_and_clear_bit(EVENT_DATA_ERROR,
&host->pending_events)) {
if (!(host->data_status & (SDMMC_INT_DRTO |
SDMMC_INT_EBE)))
send_stop_abort(host, data);
dw_mci_stop_dma(host);
state = STATE_DATA_ERROR;
break;
}
if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
&host->pending_events)) {
/*
* If all data-related interrupts don't come
* within the given time in reading data state.
*/
if (host->dir_status == DW_MCI_RECV_STATUS)
dw_mci_set_drto(host);
break;
}
set_bit(EVENT_XFER_COMPLETE, &host->completed_events);
/*
* Handle an EVENT_DATA_ERROR that might have shown up
* before the transfer completed. This might not have
* been caught by the check above because the interrupt
* could have gone off between the previous check and
* the check for transfer complete.
*
* Technically this ought not be needed assuming we
* get a DATA_COMPLETE eventually (we'll notice the
* error and end the request), but it shouldn't hurt.
*
* This has the advantage of sending the stop command.
*/
if (test_and_clear_bit(EVENT_DATA_ERROR,
&host->pending_events)) {
if (!(host->data_status & (SDMMC_INT_DRTO |
SDMMC_INT_EBE)))
send_stop_abort(host, data);
dw_mci_stop_dma(host);
state = STATE_DATA_ERROR;
break;
}
prev_state = state = STATE_DATA_BUSY;
fallthrough;
case STATE_DATA_BUSY:
if (!dw_mci_clear_pending_data_complete(host)) {
/*
* If data error interrupt comes but data over
* interrupt doesn't come within the given time.
* in reading data state.
*/
if (host->dir_status == DW_MCI_RECV_STATUS)
dw_mci_set_drto(host);
break;
}
dw_mci_stop_fault_timer(host);
host->data = NULL;
set_bit(EVENT_DATA_COMPLETE, &host->completed_events);
err = dw_mci_data_complete(host, data);
if (!err) {
if (!data->stop || mrq->sbc) {
if (mrq->sbc && data->stop)
data->stop->error = 0;
dw_mci_request_end(host, mrq);
goto unlock;
}
/* stop command for open-ended transfer*/
if (data->stop)
send_stop_abort(host, data);
} else {
/*
* If we don't have a command complete now we'll
* never get one since we just reset everything;
* better end the request.
*
* If we do have a command complete we'll fall
* through to the SENDING_STOP command and
* everything will be peachy keen.
*/
if (!test_bit(EVENT_CMD_COMPLETE,
&host->pending_events)) {
host->cmd = NULL;
dw_mci_request_end(host, mrq);
goto unlock;
}
}
/*
* If err has non-zero,
* stop-abort command has been already issued.
*/
prev_state = state = STATE_SENDING_STOP;
fallthrough;
case STATE_SENDING_STOP:
if (!dw_mci_clear_pending_cmd_complete(host))
break;
/* CMD error in data command */
if (mrq->cmd->error && mrq->data)
dw_mci_reset(host);
dw_mci_stop_fault_timer(host);
host->cmd = NULL;
host->data = NULL;
if (!mrq->sbc && mrq->stop)
dw_mci_command_complete(host, mrq->stop);
else
host->cmd_status = 0;
dw_mci_request_end(host, mrq);
goto unlock;
case STATE_DATA_ERROR:
if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
&host->pending_events))
break;
state = STATE_DATA_BUSY;
break;
}
} while (state != prev_state);
host->state = state;
unlock:
spin_unlock(&host->lock);
}