浏览代码

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>
Russell King 14 年之前
父节点
当前提交
c8afc9d59c
共有 1 个文件被更改,包括 18 次插入6 次删除
  1. 18 6
      drivers/mmc/host/mmci.c

+ 18 - 6
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