brcm2708: update linux 4.4 patches to latest version
[openwrt.git] / target / linux / brcm2708 / patches-4.4 / 0136-bcm2835-sdhost-Add-workaround-for-odd-behaviour-on-s.patch
1 From 205e27a7f94a5531764cc517ce43623361ca466c 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 136/170] bcm2835-sdhost: Add workaround for odd behaviour on
5  some cards
6
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.
13 ---
14  drivers/mmc/host/bcm2835-sdhost.c | 61 +++++++++++++++++++++++++++++++++------
15  1 file changed, 52 insertions(+), 9 deletions(-)
16
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) */
23 +
24 +       u32                             sectors;        /* Cached card size in sectors */
25 +       u32                             single_read_sectors[8];
26  };
27  
28  
29 @@ -277,6 +280,9 @@ static void bcm2835_sdhost_reset_interna
30  {
31         u32 temp;
32  
33 +       if (host->debug)
34 +               pr_info("%s: reset\n", mmc_hostname(host->mmc));
35 +
36         bcm2835_sdhost_set_power(host, false);
37  
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);
41         mdelay(10);
42         host->clock = 0;
43 +       host->sectors = 0;
44 +       host->single_read_sectors[0] = ~0;
45         bcm2835_sdhost_write(host, host->hcfg, SDHCFG);
46         bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
47         mmiowb();
48 @@ -309,8 +317,6 @@ static void bcm2835_sdhost_reset(struct
49  {
50         struct bcm2835_host *host = mmc_priv(mmc);
51         unsigned long flags;
52 -       if (host->debug)
53 -               pr_info("%s: reset\n", mmc_hostname(mmc));
54         spin_lock_irqsave(&host->lock, flags);
55  
56         bcm2835_sdhost_reset_internal(host);
57 @@ -676,6 +682,32 @@ static void bcm2835_sdhost_prepare_data(
58         host->flush_fifo = 0;
59         host->data->bytes_xfered = 0;
60  
61 +       if (!host->sectors && host->mmc->card)
62 +       {
63 +               struct mmc_card *card = host->mmc->card;
64 +               if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
65 +                       /*
66 +                        * The EXT_CSD sector count is in number of 512 byte
67 +                        * sectors.
68 +                        */
69 +                       host->sectors = card->ext_csd.sectors;
70 +                       pr_err("%s: using ext_csd!\n", mmc_hostname(host->mmc));
71 +               } else {
72 +                       /*
73 +                        * The CSD capacity field is in units of read_blkbits.
74 +                        * set_capacity takes units of 512 bytes.
75 +                        */
76 +                       host->sectors = card->csd.capacity <<
77 +                               (card->csd.read_blkbits - 9);
78 +               }
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 */
85 +       }
86 +
87         host->use_dma = host->have_dma && (data->blocks > host->pio_limit);
88         if (!host->use_dma) {
89                 int flags;
90 @@ -1246,6 +1278,10 @@ static u32 bcm2835_sdhost_block_irq(stru
91  
92                         bcm2835_sdhost_finish_data(host);
93                 } else {
94 +                       /* Reset the timer */
95 +                       mod_timer(&host->pio_timer,
96 +                                 jiffies + host->pio_timeout);
97 +
98                         bcm2835_sdhost_transfer_pio(host);
99  
100                         /* Reset the timer */
101 @@ -1450,8 +1486,8 @@ void bcm2835_sdhost_set_clock(struct bcm
102         host->cdiv = div;
103         bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
104  
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);
109  
110         if (host->debug)
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.
115         */
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;
119 +
120 +       host = mmc_priv(card->host);
121  
122 -       if ((direction == MMC_DATA_READ) &&
123 -           ((blk_pos + blk_size) == card_sectors))
124 -               blk_size--;
125 +       if (direction == MMC_DATA_READ)
126 +       {
127 +               int i;
128 +               int sector;
129 +               for (i = 0; blk_pos > (sector = host->single_read_sectors[i]); i++)
130 +                       continue;
131  
132 +               if ((blk_pos + blk_size) > sector)
133 +                       blk_size = (blk_pos == sector) ? 1 : (sector - blk_pos);
134 +       }
135         return blk_size;
136  }
137