ARM: mmci: avoid reporting too many completed bytes on fifo overrun
authorRussell King <rmk+kernel@arm.linux.org.uk>
Fri, 4 Feb 2011 09:19:46 +0000 (09:19 +0000)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Fri, 4 Feb 2011 13:25:41 +0000 (13:25 +0000)
The data counter counts the number of bytes transferred on the MMC bus.
When a FIFO overrun occurs, we will not have transferred a FIFOs-worth
of data to memory, and so the data counter will be a FIFOs-worth ahead.
If this occurs on a block boundary, we will report one too many sectors
as successful.  Fix this.

Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
drivers/mmc/host/mmci.c

index 2d6de3e..d7b83a8 100644 (file)
@@ -283,22 +283,34 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
        if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
                u32 remain, success;
 
-               /* Calculate how far we are into the transfer */
+               /*
+                * Calculate how far we are into the transfer.  Note that
+                * the data counter gives the number of bytes transferred
+                * on the MMC bus, not on the host side.  On reads, this
+                * can be as much as a FIFO-worth of data ahead.  This
+                * matters for FIFO overruns only.
+                */
                remain = readl(host->base + MMCIDATACNT);
                success = data->blksz * data->blocks - remain;
 
-               dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ (status %08x)\n", status);
+               dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ, status 0x%08x at 0x%08x\n",
+                       status, success);
                if (status & MCI_DATACRCFAIL) {
                        /* Last block was not successful */
-                       host->data_xfered = round_down(success - 1, data->blksz);
+                       success -= 1;
                        data->error = -EILSEQ;
                } else if (status & MCI_DATATIMEOUT) {
-                       host->data_xfered = round_down(success, data->blksz);
                        data->error = -ETIMEDOUT;
-               } else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
-                       host->data_xfered = round_down(success, data->blksz);
+               } else if (status & MCI_TXUNDERRUN) {
+                       data->error = -EIO;
+               } else if (status & MCI_RXOVERRUN) {
+                       if (success > host->variant->fifosize)
+                               success -= host->variant->fifosize;
+                       else
+                               success = 0;
                        data->error = -EIO;
                }
+               host->data_xfered = round_down(success, data->blksz);
 
                /*
                 * We hit an error condition.  Ensure that any data