From a621bf4951f8e66f77d3298148a064c94db1ef0e Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.org>
Date: Tue, 19 Jan 2016 17:16:38 +0000
Subject: [PATCH] bcm2835-sdhost: Add workaround for odd behaviour on some
 cards

For reasons not understood, the sdhost driver fails when reading
sectors very near the end of some SD cards. The problem could
be related to the similar issue that reading the final sector
of any card as part of a multiple read never completes, and the
workaround is an extension of the mechanism introduced to solve
that problem which ensures those sectors are always read singly.
---
 drivers/mmc/host/bcm2835-sdhost.c | 61 +++++++++++++++++++++++++++++++++------
 1 file changed, 52 insertions(+), 9 deletions(-)

--- a/drivers/mmc/host/bcm2835-sdhost.c
+++ b/drivers/mmc/host/bcm2835-sdhost.c
@@ -173,6 +173,9 @@ struct bcm2835_host {
 	u32				overclock_50;	/* frequency to use when 50MHz is requested (in MHz) */
 	u32				overclock;	/* Current frequency if overclocked, else zero */
 	u32				pio_limit;	/* Maximum block count for PIO (0 = always DMA) */
+
+	u32				sectors;	/* Cached card size in sectors */
+	u32				single_read_sectors[8];
 };
 
 
@@ -277,6 +280,9 @@ static void bcm2835_sdhost_reset_interna
 {
 	u32 temp;
 
+	if (host->debug)
+		pr_info("%s: reset\n", mmc_hostname(host->mmc));
+
 	bcm2835_sdhost_set_power(host, false);
 
 	bcm2835_sdhost_write(host, 0, SDCMD);
@@ -299,6 +305,8 @@ static void bcm2835_sdhost_reset_interna
 	bcm2835_sdhost_set_power(host, true);
 	mdelay(10);
 	host->clock = 0;
+	host->sectors = 0;
+	host->single_read_sectors[0] = ~0;
 	bcm2835_sdhost_write(host, host->hcfg, SDHCFG);
 	bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
 	mmiowb();
@@ -309,8 +317,6 @@ static void bcm2835_sdhost_reset(struct
 {
 	struct bcm2835_host *host = mmc_priv(mmc);
 	unsigned long flags;
-	if (host->debug)
-		pr_info("%s: reset\n", mmc_hostname(mmc));
 	spin_lock_irqsave(&host->lock, flags);
 
 	bcm2835_sdhost_reset_internal(host);
@@ -676,6 +682,32 @@ static void bcm2835_sdhost_prepare_data(
 	host->flush_fifo = 0;
 	host->data->bytes_xfered = 0;
 
+	if (!host->sectors && host->mmc->card)
+	{
+		struct mmc_card *card = host->mmc->card;
+		if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
+			/*
+			 * The EXT_CSD sector count is in number of 512 byte
+			 * sectors.
+			 */
+			host->sectors = card->ext_csd.sectors;
+			pr_err("%s: using ext_csd!\n", mmc_hostname(host->mmc));
+		} else {
+			/*
+			 * The CSD capacity field is in units of read_blkbits.
+			 * set_capacity takes units of 512 bytes.
+			 */
+			host->sectors = card->csd.capacity <<
+				(card->csd.read_blkbits - 9);
+		}
+		host->single_read_sectors[0] = host->sectors - 65;
+		host->single_read_sectors[1] = host->sectors - 64;
+		host->single_read_sectors[2] = host->sectors - 33;
+		host->single_read_sectors[3] = host->sectors - 32;
+		host->single_read_sectors[4] = host->sectors - 1;
+		host->single_read_sectors[5] = ~0; /* Safety net */
+	}
+
 	host->use_dma = host->have_dma && (data->blocks > host->pio_limit);
 	if (!host->use_dma) {
 		int flags;
@@ -1246,6 +1278,10 @@ static u32 bcm2835_sdhost_block_irq(stru
 
 			bcm2835_sdhost_finish_data(host);
 		} else {
+			/* Reset the timer */
+			mod_timer(&host->pio_timer,
+				  jiffies + host->pio_timeout);
+
 			bcm2835_sdhost_transfer_pio(host);
 
 			/* Reset the timer */
@@ -1450,8 +1486,8 @@ void bcm2835_sdhost_set_clock(struct bcm
 	host->cdiv = div;
 	bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
 
-	/* Set the timeout to 500ms */
-	bcm2835_sdhost_write(host, host->mmc->actual_clock/2, SDTOUT);
+	/* Set the timeout to 250ms */
+	bcm2835_sdhost_write(host, host->mmc->actual_clock/4, SDTOUT);
 
 	if (host->debug)
 		pr_info("%s: clock=%d -> max_clk=%d, cdiv=%x (actual clock %d)\n",
@@ -1566,13 +1602,20 @@ static int bcm2835_sdhost_multi_io_quirk
 	   reading the final sector of the card as part of a multiple read
 	   problematic. Detect that case and shorten the read accordingly.
 	*/
-	/* csd.capacity is in weird units - convert to sectors */
-	u32 card_sectors = (card->csd.capacity << (card->csd.read_blkbits - 9));
+	struct bcm2835_host *host;
+
+	host = mmc_priv(card->host);
 
-	if ((direction == MMC_DATA_READ) &&
-	    ((blk_pos + blk_size) == card_sectors))
-		blk_size--;
+	if (direction == MMC_DATA_READ)
+	{
+		int i;
+		int sector;
+		for (i = 0; blk_pos > (sector = host->single_read_sectors[i]); i++)
+			continue;
 
+		if ((blk_pos + blk_size) > sector)
+			blk_size = (blk_pos == sector) ? 1 : (sector - blk_pos);
+	}
 	return blk_size;
 }
 
