diff options
author | Phil Elwell <phil@raspberrypi.org> | 2016-03-15 14:10:29 +0000 |
---|---|---|
committer | Phil Elwell <phil@raspberrypi.org> | 2016-03-15 14:26:16 +0000 |
commit | 20fe468af4bb40fec0f81753da4b20a8bfc259c9 (patch) | |
tree | 0b50fd9ce313c017a428dd828948f953bc31462c | |
parent | 5f023e8019fffd88c57106b5604c217436c67401 (diff) |
bcm2835-sdhost: Workaround for "slow" sectorsrpi-bootloader-1.20160315-1
Some cards have been seen to cause timeouts after certain sectors are
read. This workaround enforces a minimum delay between the stop after
reading one of those sectors and a subsequent data command.
Using CMD23 (SET_BLOCK_COUNT) avoids this problem, so good cards will
not be penalised by this workaround.
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
-rw-r--r-- | drivers/mmc/host/bcm2835-sdhost.c | 50 |
1 files changed, 46 insertions, 4 deletions
diff --git a/drivers/mmc/host/bcm2835-sdhost.c b/drivers/mmc/host/bcm2835-sdhost.c index c9693859820a..1f73159c9479 100644 --- a/drivers/mmc/host/bcm2835-sdhost.c +++ b/drivers/mmc/host/bcm2835-sdhost.c @@ -209,9 +209,12 @@ struct bcm2835_host { int max_delay; /* maximum length of time spent waiting */ struct timeval stop_time; /* when the last stop was issued */ u32 delay_after_stop; /* minimum time between stop and subsequent data transfer */ + u32 delay_after_this_stop; /* minimum time between this stop and subsequent data transfer */ 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 */ }; #if ENABLE_LOG @@ -432,6 +435,7 @@ static void bcm2835_sdhost_reset_internal(struct bcm2835_host *host) bcm2835_sdhost_set_power(host, true); mdelay(10); host->clock = 0; + host->sectors = 0; bcm2835_sdhost_write(host, host->hcfg, SDHCFG); bcm2835_sdhost_write(host, host->cdiv, SDCDIV); mmiowb(); @@ -887,6 +891,24 @@ static void bcm2835_sdhost_prepare_data(struct bcm2835_host *host, struct mmc_co 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; + } 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); + } + } + if (!host->dma_desc) { /* Use PIO */ int flags = SG_MITER_ATOMIC; @@ -996,7 +1018,7 @@ bool bcm2835_sdhost_send_command(struct bcm2835_host *host, if (cmd->data) { log_event("CMDD", cmd->data->blocks, cmd->data->blksz); - if (host->delay_after_stop) { + if (host->delay_after_this_stop) { struct timeval now; int time_since_stop; do_gettimeofday(&now); @@ -1005,12 +1027,32 @@ bool bcm2835_sdhost_send_command(struct bcm2835_host *host, /* Possibly less than one second */ time_since_stop = time_since_stop * 1000000 + (now.tv_usec - host->stop_time.tv_usec); - if (time_since_stop < host->delay_after_stop) - udelay(host->delay_after_stop - + if (time_since_stop < + host->delay_after_this_stop) + udelay(host->delay_after_this_stop - time_since_stop); } } + host->delay_after_this_stop = host->delay_after_stop; + if ((cmd->data->flags & MMC_DATA_READ) && !host->use_sbc) { + /* See if read crosses one of the hazardous sectors */ + u32 first_blk, last_blk; + + /* Intentionally include the following sector because + without CMD23/SBC the read may run on. */ + first_blk = host->mrq->cmd->arg; + last_blk = first_blk + cmd->data->blocks; + + if (((last_blk >= (host->sectors - 64)) && + (first_blk <= (host->sectors - 64))) || + ((last_blk >= (host->sectors - 32)) && + (first_blk <= (host->sectors - 32)))) { + host->delay_after_this_stop = + max(250u, host->delay_after_stop); + } + } + if (cmd->data->flags & MMC_DATA_WRITE) sdcmd |= SDCMD_WRITE_CMD; if (cmd->data->flags & MMC_DATA_READ) @@ -1085,7 +1127,7 @@ static void bcm2835_sdhost_transfer_complete(struct bcm2835_host *host) if (!host->use_busy) bcm2835_sdhost_finish_command(host, NULL); - if (host->delay_after_stop) + if (host->delay_after_this_stop) do_gettimeofday(&host->stop_time); } } else { |