openwrt/target/linux/brcm2708/patches-4.4/0134-bcm2835-sdhost-Add-wor...

138 lines
4.5 KiB
Diff

From 2e34fd2e06d95cd4e4e29d94ce016ab4426f75af 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;
}