Commit 4bf78099 authored by Adrian Hunter's avatar Adrian Hunter Committed by Ulf Hansson

mmc: sdhci: Fix data command CRC error handling

Existing data command CRC error handling is non-standard and does not work
with some Intel host controllers. Specifically, the assumption that the host
controller will continue operating normally after the error interrupt,
is not valid. Change the driver to handle the error in the same manner
as a data CRC error, taking care to ensure that the data line reset is
done for single or multi-block transfers, and it is done before
unmapping DMA.
Signed-off-by: default avatarAdrian Hunter <adrian.hunter@intel.com>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent 7e2d23ec
...@@ -1191,8 +1191,7 @@ static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq) ...@@ -1191,8 +1191,7 @@ static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
return (!(host->flags & SDHCI_DEVICE_DEAD) && return (!(host->flags & SDHCI_DEVICE_DEAD) &&
((mrq->cmd && mrq->cmd->error) || ((mrq->cmd && mrq->cmd->error) ||
(mrq->sbc && mrq->sbc->error) || (mrq->sbc && mrq->sbc->error) ||
(mrq->data && ((mrq->data->error && !mrq->data->stop) || (mrq->data && mrq->data->stop && mrq->data->stop->error) ||
(mrq->data->stop && mrq->data->stop->error))) ||
(host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))); (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)));
} }
...@@ -1244,6 +1243,16 @@ static void sdhci_finish_data(struct sdhci_host *host) ...@@ -1244,6 +1243,16 @@ static void sdhci_finish_data(struct sdhci_host *host)
host->data = NULL; host->data = NULL;
host->data_cmd = NULL; host->data_cmd = NULL;
/*
* The controller needs a reset of internal state machines upon error
* conditions.
*/
if (data->error) {
if (!host->cmd || host->cmd == data_cmd)
sdhci_do_reset(host, SDHCI_RESET_CMD);
sdhci_do_reset(host, SDHCI_RESET_DATA);
}
if ((host->flags & (SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA)) == if ((host->flags & (SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA)) ==
(SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA)) (SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA))
sdhci_adma_table_post(host, data); sdhci_adma_table_post(host, data);
...@@ -1268,17 +1277,6 @@ static void sdhci_finish_data(struct sdhci_host *host) ...@@ -1268,17 +1277,6 @@ static void sdhci_finish_data(struct sdhci_host *host)
if (data->stop && if (data->stop &&
(data->error || (data->error ||
!data->mrq->sbc)) { !data->mrq->sbc)) {
/*
* The controller needs a reset of internal state machines
* upon error conditions.
*/
if (data->error) {
if (!host->cmd || host->cmd == data_cmd)
sdhci_do_reset(host, SDHCI_RESET_CMD);
sdhci_do_reset(host, SDHCI_RESET_DATA);
}
/* /*
* 'cap_cmd_during_tfr' request must not use the command line * 'cap_cmd_during_tfr' request must not use the command line
* after mmc_command_done() has been called. It is upper layer's * after mmc_command_done() has been called. It is upper layer's
...@@ -2757,7 +2755,7 @@ static void sdhci_timeout_data_timer(struct timer_list *t) ...@@ -2757,7 +2755,7 @@ static void sdhci_timeout_data_timer(struct timer_list *t)
* * * *
\*****************************************************************************/ \*****************************************************************************/
static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask) static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p)
{ {
if (!host->cmd) { if (!host->cmd) {
/* /*
...@@ -2780,20 +2778,12 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask) ...@@ -2780,20 +2778,12 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
else else
host->cmd->error = -EILSEQ; host->cmd->error = -EILSEQ;
/* /* Treat data command CRC error the same as data CRC error */
* If this command initiates a data phase and a response
* CRC error is signalled, the card can start transferring
* data - the card may have received the command without
* error. We must not terminate the mmc_request early.
*
* If the card did not receive the command or returned an
* error which prevented it sending data, the data phase
* will time out.
*/
if (host->cmd->data && if (host->cmd->data &&
(intmask & (SDHCI_INT_CRC | SDHCI_INT_TIMEOUT)) == (intmask & (SDHCI_INT_CRC | SDHCI_INT_TIMEOUT)) ==
SDHCI_INT_CRC) { SDHCI_INT_CRC) {
host->cmd = NULL; host->cmd = NULL;
*intmask_p |= SDHCI_INT_DATA_CRC;
return; return;
} }
...@@ -3021,7 +3011,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) ...@@ -3021,7 +3011,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
} }
if (intmask & SDHCI_INT_CMD_MASK) if (intmask & SDHCI_INT_CMD_MASK)
sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK, &intmask);
if (intmask & SDHCI_INT_DATA_MASK) if (intmask & SDHCI_INT_DATA_MASK)
sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment