brcm2708-gpu-fw: update to latest version
[librecmc/librecmc.git] / target / linux / brcm2708 / patches-4.4 / 0186-bcm2835-sdhost-Workaround-for-slow-sectors.patch
1 From c215c0d855343400b53ae2ec8b015a3f4a3eb27e Mon Sep 17 00:00:00 2001
2 From: Phil Elwell <phil@raspberrypi.org>
3 Date: Tue, 15 Mar 2016 14:10:29 +0000
4 Subject: [PATCH 186/232] bcm2835-sdhost: Workaround for "slow" sectors
5
6 Some cards have been seen to cause timeouts after certain sectors are
7 read. This workaround enforces a minimum delay between the stop after
8 reading one of those sectors and a subsequent data command.
9
10 Using CMD23 (SET_BLOCK_COUNT) avoids this problem, so good cards will
11 not be penalised by this workaround.
12
13 Signed-off-by: Phil Elwell <phil@raspberrypi.org>
14 ---
15  drivers/mmc/host/bcm2835-sdhost.c | 50 +++++++++++++++++++++++++++++++++++----
16  1 file changed, 46 insertions(+), 4 deletions(-)
17
18 --- a/drivers/mmc/host/bcm2835-sdhost.c
19 +++ b/drivers/mmc/host/bcm2835-sdhost.c
20 @@ -202,9 +202,12 @@ struct bcm2835_host {
21         int                             max_delay;      /* maximum length of time spent waiting */
22         struct timeval                  stop_time;      /* when the last stop was issued */
23         u32                             delay_after_stop; /* minimum time between stop and subsequent data transfer */
24 +       u32                             delay_after_this_stop; /* minimum time between this stop and subsequent data transfer */
25         u32                             overclock_50;   /* frequency to use when 50MHz is requested (in MHz) */
26         u32                             overclock;      /* Current frequency if overclocked, else zero */
27         u32                             pio_limit;      /* Maximum block count for PIO (0 = always DMA) */
28 +
29 +       u32                             sectors;        /* Cached card size in sectors */
30  };
31  
32  #if ENABLE_LOG
33 @@ -425,6 +428,7 @@ static void bcm2835_sdhost_reset_interna
34         bcm2835_sdhost_set_power(host, true);
35         mdelay(10);
36         host->clock = 0;
37 +       host->sectors = 0;
38         bcm2835_sdhost_write(host, host->hcfg, SDHCFG);
39         bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
40         mmiowb();
41 @@ -880,6 +884,24 @@ static void bcm2835_sdhost_prepare_data(
42         host->flush_fifo = 0;
43         host->data->bytes_xfered = 0;
44  
45 +       if (!host->sectors && host->mmc->card) {
46 +               struct mmc_card *card = host->mmc->card;
47 +               if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
48 +                       /*
49 +                        * The EXT_CSD sector count is in number of 512 byte
50 +                        * sectors.
51 +                        */
52 +                       host->sectors = card->ext_csd.sectors;
53 +               } else {
54 +                       /*
55 +                        * The CSD capacity field is in units of read_blkbits.
56 +                        * set_capacity takes units of 512 bytes.
57 +                        */
58 +                       host->sectors = card->csd.capacity <<
59 +                               (card->csd.read_blkbits - 9);
60 +               }
61 +       }
62 +
63         if (!host->dma_desc) {
64                 /* Use PIO */
65                 int flags = SG_MITER_ATOMIC;
66 @@ -989,7 +1011,7 @@ bool bcm2835_sdhost_send_command(struct
67  
68         if (cmd->data) {
69                 log_event("CMDD", cmd->data->blocks, cmd->data->blksz);
70 -               if (host->delay_after_stop) {
71 +               if (host->delay_after_this_stop) {
72                         struct timeval now;
73                         int time_since_stop;
74                         do_gettimeofday(&now);
75 @@ -998,12 +1020,32 @@ bool bcm2835_sdhost_send_command(struct
76                                 /* Possibly less than one second */
77                                 time_since_stop = time_since_stop * 1000000 +
78                                         (now.tv_usec - host->stop_time.tv_usec);
79 -                               if (time_since_stop < host->delay_after_stop)
80 -                                       udelay(host->delay_after_stop -
81 +                               if (time_since_stop <
82 +                                   host->delay_after_this_stop)
83 +                                       udelay(host->delay_after_this_stop -
84                                                time_since_stop);
85                         }
86                 }
87  
88 +               host->delay_after_this_stop = host->delay_after_stop;
89 +               if ((cmd->data->flags & MMC_DATA_READ) && !host->use_sbc) {
90 +                       /* See if read crosses one of the hazardous sectors */
91 +                       u32 first_blk, last_blk;
92 +
93 +                       /* Intentionally include the following sector because
94 +                          without CMD23/SBC the read may run on. */
95 +                       first_blk = host->mrq->cmd->arg;
96 +                       last_blk = first_blk + cmd->data->blocks;
97 +
98 +                       if (((last_blk >= (host->sectors - 64)) &&
99 +                            (first_blk <= (host->sectors - 64))) ||
100 +                           ((last_blk >= (host->sectors - 32)) &&
101 +                            (first_blk <= (host->sectors - 32)))) {
102 +                               host->delay_after_this_stop =
103 +                                       max(250u, host->delay_after_stop);
104 +                       }
105 +               }
106 +
107                 if (cmd->data->flags & MMC_DATA_WRITE)
108                         sdcmd |= SDCMD_WRITE_CMD;
109                 if (cmd->data->flags & MMC_DATA_READ)
110 @@ -1078,7 +1120,7 @@ static void bcm2835_sdhost_transfer_comp
111                         if (!host->use_busy)
112                                 bcm2835_sdhost_finish_command(host, NULL);
113  
114 -                       if (host->delay_after_stop)
115 +                       if (host->delay_after_this_stop)
116                                 do_gettimeofday(&host->stop_time);
117                 }
118         } else {