brcm2708: switch to linux 4.4 and update patches
[openwrt.git] / target / linux / brcm2708 / patches-4.4 / 0136-bcm2835-sdhost-Add-workaround-for-odd-behaviour-on-s.patch
diff --git a/target/linux/brcm2708/patches-4.4/0136-bcm2835-sdhost-Add-workaround-for-odd-behaviour-on-s.patch b/target/linux/brcm2708/patches-4.4/0136-bcm2835-sdhost-Add-workaround-for-odd-behaviour-on-s.patch
new file mode 100644 (file)
index 0000000..bbfcbfa
--- /dev/null
@@ -0,0 +1,137 @@
+From f99bed36f644ebf32e90f49559aac7a663a9fa59 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.org>
+Date: Tue, 19 Jan 2016 17:16:38 +0000
+Subject: [PATCH 136/156] 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;
+ }