011f2c26f1
As usual these patches were extracted and rebased from the raspberry pi repo: https://github.com/raspberrypi/linux/tree/rpi-4.4.y Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
138 lines
4.5 KiB
Diff
138 lines
4.5 KiB
Diff
From a621bf4951f8e66f77d3298148a064c94db1ef0e 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;
|
|
}
|
|
|