diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2011-02-04 04:19:46 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2011-02-04 08:25:41 -0500 |
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, | |||
283 | if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) { | 283 | if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) { |
284 | u32 remain, success; | 284 | u32 remain, success; |
285 | 285 | ||
286 | /* Calculate how far we are into the transfer */ | 286 | /* |
287 | * Calculate how far we are into the transfer. Note that | ||
288 | * the data counter gives the number of bytes transferred | ||
289 | * on the MMC bus, not on the host side. On reads, this | ||
290 | * can be as much as a FIFO-worth of data ahead. This | ||
291 | * matters for FIFO overruns only. | ||
292 | */ | ||
287 | remain = readl(host->base + MMCIDATACNT); | 293 | remain = readl(host->base + MMCIDATACNT); |
288 | success = data->blksz * data->blocks - remain; | 294 | success = data->blksz * data->blocks - remain; |
289 | 295 | ||
290 | dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ (status %08x)\n", status); | 296 | dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ, status 0x%08x at 0x%08x\n", |
297 | status, success); | ||
291 | if (status & MCI_DATACRCFAIL) { | 298 | if (status & MCI_DATACRCFAIL) { |
292 | /* Last block was not successful */ | 299 | /* Last block was not successful */ |
293 | host->data_xfered = round_down(success - 1, data->blksz); | 300 | success -= 1; |
294 | data->error = -EILSEQ; | 301 | data->error = -EILSEQ; |
295 | } else if (status & MCI_DATATIMEOUT) { | 302 | } else if (status & MCI_DATATIMEOUT) { |
296 | host->data_xfered = round_down(success, data->blksz); | ||
297 | data->error = -ETIMEDOUT; | 303 | data->error = -ETIMEDOUT; |
298 | } else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN)) { | 304 | } else if (status & MCI_TXUNDERRUN) { |
299 | host->data_xfered = round_down(success, data->blksz); | 305 | data->error = -EIO; |
306 | } else if (status & MCI_RXOVERRUN) { | ||
307 | if (success > host->variant->fifosize) | ||
308 | success -= host->variant->fifosize; | ||
309 | else | ||
310 | success = 0; | ||
300 | data->error = -EIO; | 311 | data->error = -EIO; |
301 | } | 312 | } |
313 | host->data_xfered = round_down(success, data->blksz); | ||
302 | 314 | ||
303 | /* | 315 | /* |
304 | * We hit an error condition. Ensure that any data | 316 | * We hit an error condition. Ensure that any data |