mmc_spi: Recover from CRC errors for r/w operation over SPI.
authorSonic Zhang <sonic.zhang@analog.com>
Mon, 12 Jul 2010 07:50:56 +0000 (15:50 +0800)
committerChris Ball <cjb@laptop.org>
Sat, 23 Oct 2010 13:11:22 +0000 (21:11 +0800)
The SPI bus is not reliable for large data transfers on all platforms.
The current mmc_spi driver fails SD read/write commands immediately if
occasional CRC errors are reported by the SD device.  This patch makes
the operation recover from CRC errors by repeating the last SD command.
The retry count is set to 5 to ensure the driver passes stress tests.

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Signed-off-by: Chris Ball <cjb@laptop.org>
drivers/mmc/host/mmc_spi.c

index 5b0b506..fd877f6 100644 (file)
@@ -1055,6 +1055,8 @@ static void mmc_spi_request(struct mmc_host *mmc, struct mmc_request *mrq)
 {
        struct mmc_spi_host     *host = mmc_priv(mmc);
        int                     status = -EINVAL;
+       int                     crc_retry = 5;
+       struct mmc_command      stop;
 
 #ifdef DEBUG
        /* MMC core and layered drivers *MUST* issue SPI-aware commands */
@@ -1087,10 +1089,29 @@ static void mmc_spi_request(struct mmc_host *mmc, struct mmc_request *mrq)
        /* request exclusive bus access */
        spi_bus_lock(host->spi->master);
 
+crc_recover:
        /* issue command; then optionally data and stop */
        status = mmc_spi_command_send(host, mrq, mrq->cmd, mrq->data != NULL);
        if (status == 0 && mrq->data) {
                mmc_spi_data_do(host, mrq->cmd, mrq->data, mrq->data->blksz);
+
+               /*
+                * The SPI bus is not always reliable for large data transfers.
+                * If an occasional crc error is reported by the SD device with
+                * data read/write over SPI, it may be recovered by repeating
+                * the last SD command again. The retry count is set to 5 to
+                * ensure the driver passes stress tests.
+                */
+               if (mrq->data->error == -EILSEQ && crc_retry) {
+                       stop.opcode = MMC_STOP_TRANSMISSION;
+                       stop.arg = 0;
+                       stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+                       status = mmc_spi_command_send(host, mrq, &stop, 0);
+                       crc_retry--;
+                       mrq->data->error = 0;
+                       goto crc_recover;
+               }
+
                if (mrq->stop)
                        status = mmc_spi_command_send(host, mrq, mrq->stop, 0);
                else