1 From 48d2aa4f18c8e32198136e5aaea252777cfc0490 Mon Sep 17 00:00:00 2001
2 From: Phil Elwell <phil@raspberrypi.org>
3 Date: Tue, 19 Jan 2016 17:16:38 +0000
4 Subject: [PATCH 134/304] bcm2835-sdhost: Add workaround for odd behaviour on
7 For reasons not understood, the sdhost driver fails when reading
8 sectors very near the end of some SD cards. The problem could
9 be related to the similar issue that reading the final sector
10 of any card as part of a multiple read never completes, and the
11 workaround is an extension of the mechanism introduced to solve
12 that problem which ensures those sectors are always read singly.
14 drivers/mmc/host/bcm2835-sdhost.c | 61 +++++++++++++++++++++++++++++++++------
15 1 file changed, 52 insertions(+), 9 deletions(-)
17 --- a/drivers/mmc/host/bcm2835-sdhost.c
18 +++ b/drivers/mmc/host/bcm2835-sdhost.c
19 @@ -173,6 +173,9 @@ struct bcm2835_host {
20 u32 overclock_50; /* frequency to use when 50MHz is requested (in MHz) */
21 u32 overclock; /* Current frequency if overclocked, else zero */
22 u32 pio_limit; /* Maximum block count for PIO (0 = always DMA) */
24 + u32 sectors; /* Cached card size in sectors */
25 + u32 single_read_sectors[8];
29 @@ -277,6 +280,9 @@ static void bcm2835_sdhost_reset_interna
34 + pr_info("%s: reset\n", mmc_hostname(host->mmc));
36 bcm2835_sdhost_set_power(host, false);
38 bcm2835_sdhost_write(host, 0, SDCMD);
39 @@ -299,6 +305,8 @@ static void bcm2835_sdhost_reset_interna
40 bcm2835_sdhost_set_power(host, true);
44 + host->single_read_sectors[0] = ~0;
45 bcm2835_sdhost_write(host, host->hcfg, SDHCFG);
46 bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
48 @@ -309,8 +317,6 @@ static void bcm2835_sdhost_reset(struct
50 struct bcm2835_host *host = mmc_priv(mmc);
53 - pr_info("%s: reset\n", mmc_hostname(mmc));
54 spin_lock_irqsave(&host->lock, flags);
56 bcm2835_sdhost_reset_internal(host);
57 @@ -676,6 +682,32 @@ static void bcm2835_sdhost_prepare_data(
59 host->data->bytes_xfered = 0;
61 + if (!host->sectors && host->mmc->card)
63 + struct mmc_card *card = host->mmc->card;
64 + if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
66 + * The EXT_CSD sector count is in number of 512 byte
69 + host->sectors = card->ext_csd.sectors;
70 + pr_err("%s: using ext_csd!\n", mmc_hostname(host->mmc));
73 + * The CSD capacity field is in units of read_blkbits.
74 + * set_capacity takes units of 512 bytes.
76 + host->sectors = card->csd.capacity <<
77 + (card->csd.read_blkbits - 9);
79 + host->single_read_sectors[0] = host->sectors - 65;
80 + host->single_read_sectors[1] = host->sectors - 64;
81 + host->single_read_sectors[2] = host->sectors - 33;
82 + host->single_read_sectors[3] = host->sectors - 32;
83 + host->single_read_sectors[4] = host->sectors - 1;
84 + host->single_read_sectors[5] = ~0; /* Safety net */
87 host->use_dma = host->have_dma && (data->blocks > host->pio_limit);
90 @@ -1246,6 +1278,10 @@ static u32 bcm2835_sdhost_block_irq(stru
92 bcm2835_sdhost_finish_data(host);
94 + /* Reset the timer */
95 + mod_timer(&host->pio_timer,
96 + jiffies + host->pio_timeout);
98 bcm2835_sdhost_transfer_pio(host);
100 /* Reset the timer */
101 @@ -1450,8 +1486,8 @@ void bcm2835_sdhost_set_clock(struct bcm
103 bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
105 - /* Set the timeout to 500ms */
106 - bcm2835_sdhost_write(host, host->mmc->actual_clock/2, SDTOUT);
107 + /* Set the timeout to 250ms */
108 + bcm2835_sdhost_write(host, host->mmc->actual_clock/4, SDTOUT);
111 pr_info("%s: clock=%d -> max_clk=%d, cdiv=%x (actual clock %d)\n",
112 @@ -1566,13 +1602,20 @@ static int bcm2835_sdhost_multi_io_quirk
113 reading the final sector of the card as part of a multiple read
114 problematic. Detect that case and shorten the read accordingly.
116 - /* csd.capacity is in weird units - convert to sectors */
117 - u32 card_sectors = (card->csd.capacity << (card->csd.read_blkbits - 9));
118 + struct bcm2835_host *host;
120 + host = mmc_priv(card->host);
122 - if ((direction == MMC_DATA_READ) &&
123 - ((blk_pos + blk_size) == card_sectors))
125 + if (direction == MMC_DATA_READ)
129 + for (i = 0; blk_pos > (sector = host->single_read_sectors[i]); i++)
132 + if ((blk_pos + blk_size) > sector)
133 + blk_size = (blk_pos == sector) ? 1 : (sector - blk_pos);