summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Elwell <phil@raspberrypi.org>2016-03-15 14:10:29 +0000
committerPhil Elwell <phil@raspberrypi.org>2016-03-15 14:26:16 +0000
commit20fe468af4bb40fec0f81753da4b20a8bfc259c9 (patch)
tree0b50fd9ce313c017a428dd828948f953bc31462c
parent5f023e8019fffd88c57106b5604c217436c67401 (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.c50
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 {