[PATCH 3/3] RFC: mmci: stop using the blockend interrupts

Linus Walleij linus.walleij at stericsson.com
Wed Jan 19 16:26:47 EST 2011


Implement a suggestion from Russell to drop the use of blockend
interrupts altogether and instead rely on the data counter.

Tested with error-free cards on U300, U8500 and RealView PB1176.

Signed-off-by: Linus Walleij <linus.walleij at stericsson.com>
---
This simplifies the code a lot and works fine on error-free
transfers, we need to test it on some buggy cards.
---
 drivers/mmc/host/mmci.c |   99 ++++++++--------------------------------------
 drivers/mmc/host/mmci.h |    3 -
 2 files changed, 18 insertions(+), 84 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 7e0d38c..fc72e96 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -46,8 +46,6 @@ static unsigned int fmax = 515633;
  *	      is asserted (likewise for RX)
  * @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY
  *		  is asserted (likewise for RX)
- * @broken_blockend: the MCI_DATABLOCKEND is broken on the hardware
- *		and will not work at all.
  * @sdio: variant supports SDIO
  * @st_clkdiv: true if using a ST-specific clock divider algorithm
  */
@@ -57,7 +55,6 @@ struct variant_data {
 	unsigned int		datalength_bits;
 	unsigned int		fifosize;
 	unsigned int		fifohalfsize;
-	bool			broken_blockend;
 	bool			sdio;
 	bool			st_clkdiv;
 };
@@ -73,7 +70,6 @@ static struct variant_data variant_u300 = {
 	.fifohalfsize		= 8 * 4,
 	.clkreg_enable		= 1 << 13, /* HWFCEN */
 	.datalength_bits	= 16,
-	.broken_blockend	= true,
 	.sdio			= true,
 };
 
@@ -83,7 +79,6 @@ static struct variant_data variant_ux500 = {
 	.clkreg			= MCI_CLK_ENABLE,
 	.clkreg_enable		= 1 << 14, /* HWFCEN */
 	.datalength_bits	= 24,
-	.broken_blockend	= true,
 	.sdio			= true,
 	.st_clkdiv		= true,
 };
@@ -207,8 +202,6 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
 	host->data = data;
 	host->size = data->blksz * data->blocks;
 	host->data_xfered = 0;
-	host->last_blockend = false;
-	host->dataend = false;
 
 	mmci_init_sg(host, data);
 
@@ -250,10 +243,6 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
 
 	writel(datactrl, base + MMCIDATACTRL);
 	irqmask0 = readl(base + MMCIMASK0);
-	if (variant->broken_blockend)
-		irqmask0 &= ~MCI_DATABLOCKENDMASK;
-	else
-		irqmask0 |= MCI_DATABLOCKENDMASK;
 	irqmask0 &= ~MCI_DATAENDMASK;
 	writel(irqmask0, base + MMCIMASK0);
 	mmci_set_mask1(host, irqmask1);
@@ -291,21 +280,26 @@ static void
 mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
 	      unsigned int status)
 {
-	struct variant_data *variant = host->variant;
-
 	/* First check for errors */
 	if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
+		u32 remain, success;
+
+		/* Calculate how far we are into the transfer */
+		remain = readl(host->base + MMCIDATACNT) << 2;
+		success = data->blksz * data->blocks - remain;
+
 		dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ (status %08x)\n", status);
-		if (status & MCI_DATACRCFAIL)
+		if (status & MCI_DATACRCFAIL) {
+			/* Last block was not successful */
+			host->data_xfered = ((success / data->blksz) - 1 * data->blksz);
 			data->error = -EILSEQ;
-		else if (status & MCI_DATATIMEOUT)
+		} else if (status & MCI_DATATIMEOUT) {
+			host->data_xfered = success;
 			data->error = -ETIMEDOUT;
-		else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN))
+		} else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
+			host->data_xfered = success;
 			data->error = -EIO;
-
-		/* Force-complete the transaction */
-		host->last_blockend = true;
-		host->dataend = true;
+		}
 
 		/*
 		 * We hit an error condition.  Ensure that any data
@@ -324,71 +318,14 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
 		}
 	}
 
-	/*
-	 * On ARM variants in PIO mode, MCI_DATABLOCKEND is always sent first,
-	 * and we increase the transfered number of bytes for that IRQ. Repeat
-	 * for the number of blocks in the transfer. Then MCI_DATAEND follows
-	 * and we conclude the transaction.
-	 *
-	 * On the U300 and Ux500 a few to many MCI_DATABLOCKEND interrupts
-	 * are usually missing, so the data counter is not properly increased.
-	 * This is likely because new block arrive while the IRQ is being
-	 * processed without the transfers being held back, and ACK:in the
-	 * interrupt will only clear the very last block and intermediate
-	 * blockend interrupts get lost. So we simply mask of the blockend
-	 * interrupt on these and only use the dataend interrupt.
-	 */
-	if (status & MCI_DATABLOCKEND) {
-		/*
-		 * Just being a little over-cautious, we do not
-		 * use this progressive update if the hardware blockend
-		 * flag is unreliable: since it can stay high between
-		 * IRQs it will corrupt the transfer counter.
-		 */
-		if (!variant->broken_blockend)
-			host->data_xfered += data->blksz;
-		if (host->data_xfered == data->blksz * data->blocks)
-			host->last_blockend = true;
-	}
+	if (status & MCI_DATABLOCKEND)
+		dev_err(mmc_dev(host->mmc), "stray MCI_DATABLOCKEND interrupt\n");
 
 	if (status & MCI_DATAEND) {
-		if (!host->last_blockend && !variant->broken_blockend &&
-		    !data->error) {
-			/*
-			 * This typically occur on hardware with broken
-			 * blockend reporting, which will fire less blockend
-			 * IRQs than would be expected. Print an error and
-			 * repair the situation, but the hardware should
-			 * really be flagged as broken so this IRQ is not used
-			 * at all.
-			 */
-			dev_err(mmc_dev(host->mmc),
-				"missing %d block interrupts!\n",
-				(data->blksz * data->blocks - host->data_xfered) / data->blksz);
-			host->data_xfered = data->blksz * data->blocks;
-			host->last_blockend = true;
-		}
-		host->dataend = true;
-	}
-
-	/*
-	 * On variants with broken blockend we shall only wait for dataend,
-	 * on others we must sync with the blockend signal since they can
-	 * appear out-of-order.
-	 */
-	if (host->dataend &&
-	    (host->last_blockend || variant->broken_blockend)) {
 		mmci_stop_data(host);
 
-		/* Reset these flags */
-		host->last_blockend = false;
-		host->dataend = false;
-
-		/*
-		 * Variants with broken blockend flags need to handle the
-		 * end of the entire transfer here.
-		 */
-		if (variant->broken_blockend && !data->error)
+		if (!data->error)
+			/* The error clause is handled above, success! */
 			host->data_xfered += data->blksz * data->blocks;
 
 		if (!data->stop) {
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 7ac8c4d..c1df7b8 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -177,9 +177,6 @@ struct mmci_host {
 	struct timer_list	timer;
 	unsigned int		oldstat;
 
-	bool			last_blockend;
-	bool			dataend;
-
 	/* pio stuff */
 	struct sg_mapping_iter	sg_miter;
 	unsigned int		size;
-- 
1.7.3.2




More information about the linux-arm-kernel mailing list