diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2011-02-04 09:19:46 +0000 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2011-02-04 13:25:41 +0000 |
commit | c8afc9d59ce1100d3f7704e86fda5a25361c45bf (patch) | |
tree | 759b6215b7c067f249a03dfd7e283a9e5b6856e1 /drivers/mmc/host/mmci.c | |
parent | 613b152c63e35095a929f9bb80441cbe91ff5e80 (diff) |
ARM: mmci: avoid reporting too many completed bytes on fifo overrun
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>
Diffstat (limited to 'drivers/mmc/host/mmci.c')
-rw-r--r-- | drivers/mmc/host/mmci.c | 24 |
1 files changed, 18 insertions, 6 deletions
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 2d6de3e03e2d..d7b83a8b3534 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -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 |