2015-08-17 06:04:32 +00:00
|
|
|
From 23d5b7edb052559fcac15a305d3f665aa432831c Mon Sep 17 00:00:00 2001
|
2015-07-17 12:48:39 +00:00
|
|
|
From: Phil Elwell <phil@raspberrypi.org>
|
|
|
|
Date: Wed, 17 Jun 2015 11:36:53 +0100
|
2015-08-17 06:04:32 +00:00
|
|
|
Subject: [PATCH 076/148] bcm2835-sdhost: Improve error handling and recovery
|
2015-07-17 12:48:39 +00:00
|
|
|
|
|
|
|
1) Expose the hw_reset method to the MMC framework, removing many
|
|
|
|
internal calls by the driver.
|
|
|
|
|
|
|
|
2) Reduce overclock setting on error.
|
|
|
|
|
|
|
|
3) Increase timeout to cope with high capacity cards.
|
|
|
|
|
|
|
|
4) Add properties and parameters to control pio_limit and debug.
|
|
|
|
|
|
|
|
5) Reduce messages at probe time.
|
|
|
|
---
|
|
|
|
arch/arm/boot/dts/overlays/README | 8 +-
|
|
|
|
arch/arm/boot/dts/overlays/sdhost-overlay.dts | 4 +-
|
|
|
|
drivers/mmc/host/bcm2835-sdhost.c | 578 ++++++++++++++++++--------
|
|
|
|
3 files changed, 404 insertions(+), 186 deletions(-)
|
|
|
|
|
|
|
|
--- a/arch/arm/boot/dts/overlays/README
|
|
|
|
+++ b/arch/arm/boot/dts/overlays/README
|
|
|
|
@@ -408,7 +408,13 @@ Info: Selects the bcm2835-sdhost SD/MM
|
|
|
|
Load: dtoverlay=sdhost,<param>=<val>
|
|
|
|
Params: overclock_50 Clock (in MHz) to use when the MMC framework
|
|
|
|
requests 50MHz
|
|
|
|
- force_pio Disable DMA support
|
|
|
|
+
|
|
|
|
+ force_pio Disable DMA support (default off)
|
|
|
|
+
|
|
|
|
+ pio_limit Number of blocks above which to use DMA
|
|
|
|
+ (default 2)
|
|
|
|
+
|
|
|
|
+ debug Enable debug output (default off)
|
|
|
|
|
|
|
|
|
|
|
|
Name: spi-bcm2708
|
|
|
|
--- a/arch/arm/boot/dts/overlays/sdhost-overlay.dts
|
|
|
|
+++ b/arch/arm/boot/dts/overlays/sdhost-overlay.dts
|
|
|
|
@@ -22,6 +22,7 @@
|
|
|
|
dma-names = "tx", "rx";
|
|
|
|
brcm,delay-after-stop = <0>;
|
|
|
|
brcm,overclock-50 = <0>;
|
|
|
|
+ brcm,pio-limit = <2>;
|
|
|
|
status = "okay";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
@@ -70,9 +71,10 @@
|
|
|
|
};
|
|
|
|
|
|
|
|
__overrides__ {
|
|
|
|
- delay_after_stop = <&sdhost>,"brcm,delay-after-stop:0";
|
|
|
|
overclock_50 = <&sdhost>,"brcm,overclock-50:0";
|
|
|
|
force_pio = <&sdhost>,"brcm,force-pio?";
|
|
|
|
+ pio_limit = <&sdhost>,"brcm,pio-limit:0";
|
|
|
|
+ debug = <&sdhost>,"brcm,debug?";
|
|
|
|
sdhost_freq = <&clk_sdhost>,"clock-frequency:0";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
--- a/drivers/mmc/host/bcm2835-sdhost.c
|
|
|
|
+++ b/drivers/mmc/host/bcm2835-sdhost.c
|
|
|
|
@@ -90,9 +90,8 @@
|
|
|
|
/* Reserved */
|
|
|
|
#define SDHSTS_DATA_FLAG 0x01
|
|
|
|
|
|
|
|
-#define SDHSTS_TRANSFER_ERROR_MASK (SDHSTS_CRC16_ERROR|SDHSTS_REW_TIME_OUT|SDHSTS_FIFO_ERROR)
|
|
|
|
+#define SDHSTS_TRANSFER_ERROR_MASK (SDHSTS_CRC7_ERROR|SDHSTS_CRC16_ERROR|SDHSTS_REW_TIME_OUT|SDHSTS_FIFO_ERROR)
|
|
|
|
#define SDHSTS_ERROR_MASK (SDHSTS_CMD_TIME_OUT|SDHSTS_TRANSFER_ERROR_MASK)
|
|
|
|
-/* SDHSTS_CRC7_ERROR - ignore this as MMC cards generate this spuriously */
|
|
|
|
|
|
|
|
#define SDHCFG_BUSY_IRPT_EN (1<<10)
|
|
|
|
#define SDHCFG_BLOCK_IRPT_EN (1<<8)
|
|
|
|
@@ -111,16 +110,7 @@
|
|
|
|
#define SDEDM_READ_THRESHOLD_SHIFT 14
|
|
|
|
#define SDEDM_THRESHOLD_MASK 0x1f
|
|
|
|
|
|
|
|
-/* the inclusive limit in bytes under which PIO will be used instead of DMA */
|
|
|
|
-#ifdef CONFIG_MMC_BCM2835_SDHOST_PIO_DMA_BARRIER
|
|
|
|
-#define PIO_DMA_BARRIER CONFIG_MMC_BCM2835_SDHOST_PIO_DMA_BARRIER
|
|
|
|
-#else
|
|
|
|
-#define PIO_DMA_BARRIER 0
|
|
|
|
-#endif
|
|
|
|
-
|
|
|
|
-#define MIN_FREQ 400000
|
|
|
|
-#define TIMEOUT_VAL 0xE
|
|
|
|
-#define BCM2835_SDHOST_WRITE_DELAY(f) (((2 * 1000000) / f) + 1)
|
|
|
|
+#define MHZ 1000000
|
|
|
|
|
|
|
|
#ifndef BCM2708_PERI_BASE
|
|
|
|
#define BCM2708_PERI_BASE 0x20000000
|
|
|
|
@@ -138,19 +128,20 @@ struct bcm2835_host {
|
|
|
|
|
|
|
|
struct mmc_host *mmc;
|
|
|
|
|
|
|
|
- u32 timeout;
|
|
|
|
+ u32 pio_timeout; /* In jiffies */
|
|
|
|
|
|
|
|
int clock; /* Current clock speed */
|
|
|
|
|
|
|
|
bool slow_card; /* Force 11-bit divisor */
|
|
|
|
|
|
|
|
unsigned int max_clk; /* Max possible freq */
|
|
|
|
- unsigned int timeout_clk; /* Timeout freq (KHz) */
|
|
|
|
|
|
|
|
struct tasklet_struct finish_tasklet; /* Tasklet structures */
|
|
|
|
|
|
|
|
struct timer_list timer; /* Timer for timeouts */
|
|
|
|
|
|
|
|
+ struct timer_list pio_timer; /* PIO error detection timer */
|
|
|
|
+
|
|
|
|
struct sg_mapping_iter sg_miter; /* SG state for PIO */
|
|
|
|
unsigned int blocks; /* remaining PIO blocks */
|
|
|
|
|
|
|
|
@@ -170,6 +161,10 @@ struct bcm2835_host {
|
|
|
|
|
|
|
|
unsigned int use_busy:1; /* Wait for busy interrupt */
|
|
|
|
|
|
|
|
+ unsigned int reduce_overclock:1; /* ...at the next opportunity */
|
|
|
|
+
|
|
|
|
+ unsigned int debug:1; /* Enable debug output */
|
|
|
|
+
|
|
|
|
u32 thread_isr;
|
|
|
|
|
|
|
|
/*DMA part*/
|
|
|
|
@@ -185,7 +180,8 @@ struct bcm2835_host {
|
|
|
|
struct timeval stop_time; /* when the last stop was issued */
|
|
|
|
u32 delay_after_stop; /* minimum time between stop and subsequent data transfer */
|
|
|
|
u32 overclock_50; /* frequency to use when 50MHz is requested (in MHz) */
|
|
|
|
- u32 max_overclock; /* Highest reported */
|
|
|
|
+ u32 overclock; /* Current frequency if overclocked, else zero */
|
|
|
|
+ u32 pio_limit; /* Maximum block count for PIO (0 = always DMA) */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
@@ -204,41 +200,79 @@ static inline u32 bcm2835_sdhost_read_re
|
|
|
|
return readl_relaxed(host->ioaddr + reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
+static void bcm2835_sdhost_dumpcmd(struct bcm2835_host *host,
|
|
|
|
+ struct mmc_command *cmd,
|
|
|
|
+ const char *label)
|
|
|
|
+{
|
|
|
|
+ if (cmd)
|
|
|
|
+ pr_info("%s:%c%s op %d arg 0x%x flags 0x%x - resp %08x %08x %08x %08x, err %d\n",
|
|
|
|
+ mmc_hostname(host->mmc),
|
|
|
|
+ (cmd == host->cmd) ? '>' : ' ',
|
|
|
|
+ label, cmd->opcode, cmd->arg, cmd->flags,
|
|
|
|
+ cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3],
|
|
|
|
+ cmd->error);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
static void bcm2835_sdhost_dumpregs(struct bcm2835_host *host)
|
|
|
|
{
|
|
|
|
- pr_info(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
|
|
|
|
+ bcm2835_sdhost_dumpcmd(host, host->mrq->sbc, "sbc");
|
|
|
|
+ bcm2835_sdhost_dumpcmd(host, host->mrq->cmd, "cmd");
|
|
|
|
+ if (host->mrq->data)
|
|
|
|
+ pr_err("%s: data blocks %x blksz %x - err %d\n",
|
|
|
|
+ mmc_hostname(host->mmc),
|
|
|
|
+ host->mrq->data->blocks,
|
|
|
|
+ host->mrq->data->blksz,
|
|
|
|
+ host->mrq->data->error);
|
|
|
|
+ bcm2835_sdhost_dumpcmd(host, host->mrq->stop, "stop");
|
|
|
|
+
|
|
|
|
+ pr_info("%s: =========== REGISTER DUMP ===========\n",
|
|
|
|
mmc_hostname(host->mmc));
|
|
|
|
|
|
|
|
- pr_info(DRIVER_NAME ": SDCMD 0x%08x\n",
|
|
|
|
+ pr_info("%s: SDCMD 0x%08x\n",
|
|
|
|
+ mmc_hostname(host->mmc),
|
|
|
|
bcm2835_sdhost_read(host, SDCMD));
|
|
|
|
- pr_info(DRIVER_NAME ": SDARG 0x%08x\n",
|
|
|
|
+ pr_info("%s: SDARG 0x%08x\n",
|
|
|
|
+ mmc_hostname(host->mmc),
|
|
|
|
bcm2835_sdhost_read(host, SDARG));
|
|
|
|
- pr_info(DRIVER_NAME ": SDTOUT 0x%08x\n",
|
|
|
|
+ pr_info("%s: SDTOUT 0x%08x\n",
|
|
|
|
+ mmc_hostname(host->mmc),
|
|
|
|
bcm2835_sdhost_read(host, SDTOUT));
|
|
|
|
- pr_info(DRIVER_NAME ": SDCDIV 0x%08x\n",
|
|
|
|
+ pr_info("%s: SDCDIV 0x%08x\n",
|
|
|
|
+ mmc_hostname(host->mmc),
|
|
|
|
bcm2835_sdhost_read(host, SDCDIV));
|
|
|
|
- pr_info(DRIVER_NAME ": SDRSP0 0x%08x\n",
|
|
|
|
+ pr_info("%s: SDRSP0 0x%08x\n",
|
|
|
|
+ mmc_hostname(host->mmc),
|
|
|
|
bcm2835_sdhost_read(host, SDRSP0));
|
|
|
|
- pr_info(DRIVER_NAME ": SDRSP1 0x%08x\n",
|
|
|
|
+ pr_info("%s: SDRSP1 0x%08x\n",
|
|
|
|
+ mmc_hostname(host->mmc),
|
|
|
|
bcm2835_sdhost_read(host, SDRSP1));
|
|
|
|
- pr_info(DRIVER_NAME ": SDRSP2 0x%08x\n",
|
|
|
|
+ pr_info("%s: SDRSP2 0x%08x\n",
|
|
|
|
+ mmc_hostname(host->mmc),
|
|
|
|
bcm2835_sdhost_read(host, SDRSP2));
|
|
|
|
- pr_info(DRIVER_NAME ": SDRSP3 0x%08x\n",
|
|
|
|
+ pr_info("%s: SDRSP3 0x%08x\n",
|
|
|
|
+ mmc_hostname(host->mmc),
|
|
|
|
bcm2835_sdhost_read(host, SDRSP3));
|
|
|
|
- pr_info(DRIVER_NAME ": SDHSTS 0x%08x\n",
|
|
|
|
+ pr_info("%s: SDHSTS 0x%08x\n",
|
|
|
|
+ mmc_hostname(host->mmc),
|
|
|
|
bcm2835_sdhost_read(host, SDHSTS));
|
|
|
|
- pr_info(DRIVER_NAME ": SDVDD 0x%08x\n",
|
|
|
|
+ pr_info("%s: SDVDD 0x%08x\n",
|
|
|
|
+ mmc_hostname(host->mmc),
|
|
|
|
bcm2835_sdhost_read(host, SDVDD));
|
|
|
|
- pr_info(DRIVER_NAME ": SDEDM 0x%08x\n",
|
|
|
|
+ pr_info("%s: SDEDM 0x%08x\n",
|
|
|
|
+ mmc_hostname(host->mmc),
|
|
|
|
bcm2835_sdhost_read(host, SDEDM));
|
|
|
|
- pr_info(DRIVER_NAME ": SDHCFG 0x%08x\n",
|
|
|
|
+ pr_info("%s: SDHCFG 0x%08x\n",
|
|
|
|
+ mmc_hostname(host->mmc),
|
|
|
|
bcm2835_sdhost_read(host, SDHCFG));
|
|
|
|
- pr_info(DRIVER_NAME ": SDHBCT 0x%08x\n",
|
|
|
|
+ pr_info("%s: SDHBCT 0x%08x\n",
|
|
|
|
+ mmc_hostname(host->mmc),
|
|
|
|
bcm2835_sdhost_read(host, SDHBCT));
|
|
|
|
- pr_info(DRIVER_NAME ": SDHBLC 0x%08x\n",
|
|
|
|
+ pr_info("%s: SDHBLC 0x%08x\n",
|
|
|
|
+ mmc_hostname(host->mmc),
|
|
|
|
bcm2835_sdhost_read(host, SDHBLC));
|
|
|
|
|
|
|
|
- pr_debug(DRIVER_NAME ": ===========================================\n");
|
|
|
|
+ pr_info("%s: ===========================================\n",
|
|
|
|
+ mmc_hostname(host->mmc));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -248,12 +282,10 @@ static void bcm2835_sdhost_set_power(str
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
-static void bcm2835_sdhost_reset(struct bcm2835_host *host)
|
|
|
|
+static void bcm2835_sdhost_reset_internal(struct bcm2835_host *host)
|
|
|
|
{
|
|
|
|
u32 temp;
|
|
|
|
|
|
|
|
- pr_debug("bcm2835_sdhost_reset\n");
|
|
|
|
-
|
|
|
|
bcm2835_sdhost_set_power(host, false);
|
|
|
|
|
|
|
|
bcm2835_sdhost_write(host, 0, SDCMD);
|
|
|
|
@@ -281,6 +313,20 @@ static void bcm2835_sdhost_reset(struct
|
|
|
|
mmiowb();
|
|
|
|
}
|
|
|
|
|
|
|
|
+
|
|
|
|
+static void bcm2835_sdhost_reset(struct mmc_host *mmc)
|
|
|
|
+{
|
|
|
|
+ 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);
|
|
|
|
+
|
|
|
|
+ spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
static void bcm2835_sdhost_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
|
|
|
|
|
|
|
|
static void bcm2835_sdhost_init(struct bcm2835_host *host, int soft)
|
|
|
|
@@ -290,7 +336,7 @@ static void bcm2835_sdhost_init(struct b
|
|
|
|
/* Set interrupt enables */
|
|
|
|
host->hcfg = SDHCFG_BUSY_IRPT_EN;
|
|
|
|
|
|
|
|
- bcm2835_sdhost_reset(host);
|
|
|
|
+ bcm2835_sdhost_reset_internal(host);
|
|
|
|
|
|
|
|
if (soft) {
|
|
|
|
/* force clock reconfiguration */
|
|
|
|
@@ -420,6 +466,40 @@ static void bcm2835_sdhost_dma_complete(
|
|
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
+static bool data_transfer_wait(struct bcm2835_host *host, const char *caller)
|
|
|
|
+{
|
|
|
|
+ unsigned long timeout = 1000000;
|
|
|
|
+ u32 hsts;
|
|
|
|
+ while (timeout)
|
|
|
|
+ {
|
|
|
|
+ hsts = bcm2835_sdhost_read(host, SDHSTS);
|
|
|
|
+ if (hsts & (SDHSTS_TRANSFER_ERROR_MASK |
|
|
|
|
+ SDHSTS_DATA_FLAG)) {
|
|
|
|
+ bcm2835_sdhost_write(host, SDHSTS_TRANSFER_ERROR_MASK,
|
|
|
|
+ SDHSTS);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ timeout--;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (hsts & (SDHSTS_CRC16_ERROR |
|
|
|
|
+ SDHSTS_CRC7_ERROR |
|
|
|
|
+ SDHSTS_FIFO_ERROR)) {
|
|
|
|
+ pr_err("%s: data error in %s - HSTS %x\n",
|
|
|
|
+ mmc_hostname(host->mmc), caller, hsts);
|
|
|
|
+ host->data->error = -EILSEQ;
|
|
|
|
+ return false;
|
|
|
|
+ } else if ((timeout == 0) ||
|
|
|
|
+ (hsts & (SDHSTS_CMD_TIME_OUT |
|
|
|
|
+ SDHSTS_REW_TIME_OUT))) {
|
|
|
|
+ pr_err("%s: timeout in %s - HSTS %x\n",
|
|
|
|
+ mmc_hostname(host->mmc), caller, hsts);
|
|
|
|
+ host->data->error = -ETIMEDOUT;
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
static void bcm2835_sdhost_read_block_pio(struct bcm2835_host *host)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
@@ -443,35 +523,15 @@ static void bcm2835_sdhost_read_block_pi
|
|
|
|
buf = (u32 *)host->sg_miter.addr;
|
|
|
|
|
|
|
|
while (len) {
|
|
|
|
- while (1) {
|
|
|
|
- u32 hsts;
|
|
|
|
- hsts = bcm2835_sdhost_read(host, SDHSTS);
|
|
|
|
- if (hsts & SDHSTS_DATA_FLAG)
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- if (hsts & SDHSTS_ERROR_MASK) {
|
|
|
|
- pr_err("%s: Transfer error - HSTS %x, HBCT %x - %x left\n",
|
|
|
|
- mmc_hostname(host->mmc),
|
|
|
|
- hsts,
|
|
|
|
- bcm2835_sdhost_read(host, SDHBCT),
|
|
|
|
- blksize + len);
|
|
|
|
- if (hsts & SDHSTS_REW_TIME_OUT)
|
|
|
|
- host->data->error = -ETIMEDOUT;
|
|
|
|
- else if (hsts & (SDHSTS_CRC16_ERROR ||
|
|
|
|
- SDHSTS_CRC7_ERROR))
|
|
|
|
- host->data->error = -EILSEQ;
|
|
|
|
- else {
|
|
|
|
- pr_err("%s: unexpected data error\n",
|
|
|
|
- mmc_hostname(host->mmc));
|
|
|
|
- bcm2835_sdhost_dumpregs(host);
|
|
|
|
- host->cmd->error = -EIO;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
+ if (!data_transfer_wait(host, "read_block_pio"))
|
|
|
|
+ break;
|
|
|
|
|
|
|
|
*(buf++) = bcm2835_sdhost_read(host, SDDATA);
|
|
|
|
len -= 4;
|
|
|
|
}
|
|
|
|
+
|
|
|
|
+ if (host->data->error)
|
|
|
|
+ break;
|
|
|
|
}
|
|
|
|
|
|
|
|
sg_miter_stop(&host->sg_miter);
|
|
|
|
@@ -502,11 +562,15 @@ static void bcm2835_sdhost_write_block_p
|
|
|
|
buf = host->sg_miter.addr;
|
|
|
|
|
|
|
|
while (len) {
|
|
|
|
- while (!(bcm2835_sdhost_read(host, SDHSTS) & SDHSTS_DATA_FLAG))
|
|
|
|
- continue;
|
|
|
|
+ if (!data_transfer_wait(host, "write_block_pio"))
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
bcm2835_sdhost_write(host, *(buf++), SDDATA);
|
|
|
|
len -= 4;
|
|
|
|
}
|
|
|
|
+
|
|
|
|
+ if (host->data->error)
|
|
|
|
+ break;
|
|
|
|
}
|
|
|
|
|
|
|
|
sg_miter_stop(&host->sg_miter);
|
|
|
|
@@ -519,10 +583,15 @@ static void bcm2835_sdhost_transfer_pio(
|
|
|
|
{
|
|
|
|
BUG_ON(!host->data);
|
|
|
|
|
|
|
|
- if (host->data->flags & MMC_DATA_READ)
|
|
|
|
+ if (host->data->flags & MMC_DATA_READ) {
|
|
|
|
bcm2835_sdhost_read_block_pio(host);
|
|
|
|
- else
|
|
|
|
+ } else {
|
|
|
|
bcm2835_sdhost_write_block_pio(host);
|
|
|
|
+
|
|
|
|
+ /* Start a timer in case a transfer error occurs because
|
|
|
|
+ there is no error interrupt */
|
|
|
|
+ mod_timer(&host->pio_timer, jiffies + host->pio_timeout);
|
|
|
|
+ }
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -607,6 +676,7 @@ static void bcm2835_sdhost_prepare_data(
|
|
|
|
host->flush_fifo = 0;
|
|
|
|
host->data->bytes_xfered = 0;
|
|
|
|
|
|
|
|
+ host->use_dma = host->have_dma && (data->blocks > host->pio_limit);
|
|
|
|
if (!host->use_dma) {
|
|
|
|
int flags;
|
|
|
|
|
|
|
|
@@ -619,8 +689,6 @@ static void bcm2835_sdhost_prepare_data(
|
|
|
|
host->blocks = data->blocks;
|
|
|
|
}
|
|
|
|
|
|
|
|
- host->use_dma = host->have_dma && data->blocks > PIO_DMA_BARRIER;
|
|
|
|
-
|
|
|
|
bcm2835_sdhost_set_transfer_irqs(host);
|
|
|
|
|
|
|
|
bcm2835_sdhost_write(host, data->blksz, SDHBCT);
|
|
|
|
@@ -638,22 +706,25 @@ void bcm2835_sdhost_send_command(struct
|
|
|
|
|
|
|
|
WARN_ON(host->cmd);
|
|
|
|
|
|
|
|
- if (1) {
|
|
|
|
- pr_debug("bcm2835_sdhost_send_command: %08x %08x (flags %x)\n",
|
|
|
|
- cmd->opcode, cmd->arg, (cmd->flags & 0xff) | (cmd->data ? cmd->data->flags : 0));
|
|
|
|
- if (cmd->data)
|
|
|
|
- pr_debug("bcm2835_sdhost_send_command: %s %d*%x\n",
|
|
|
|
- (cmd->data->flags & MMC_DATA_READ) ?
|
|
|
|
- "read" : "write", cmd->data->blocks,
|
|
|
|
- cmd->data->blksz);
|
|
|
|
- }
|
|
|
|
+ if (cmd->data)
|
|
|
|
+ pr_debug("%s: send_command %d 0x%x "
|
|
|
|
+ "(flags 0x%x) - %s %d*%d\n",
|
|
|
|
+ mmc_hostname(host->mmc),
|
|
|
|
+ cmd->opcode, cmd->arg, cmd->flags,
|
|
|
|
+ (cmd->data->flags & MMC_DATA_READ) ?
|
|
|
|
+ "read" : "write", cmd->data->blocks,
|
|
|
|
+ cmd->data->blksz);
|
|
|
|
+ else
|
|
|
|
+ pr_debug("%s: send_command %d 0x%x (flags 0x%x)\n",
|
|
|
|
+ mmc_hostname(host->mmc),
|
|
|
|
+ cmd->opcode, cmd->arg, cmd->flags);
|
|
|
|
|
|
|
|
/* Wait max 10 ms */
|
|
|
|
timeout = 1000;
|
|
|
|
|
|
|
|
while (bcm2835_sdhost_read(host, SDCMD) & SDCMD_NEW_FLAG) {
|
|
|
|
if (timeout == 0) {
|
|
|
|
- pr_err("%s: Previous command never completed.\n",
|
|
|
|
+ pr_err("%s: previous command never completed.\n",
|
|
|
|
mmc_hostname(host->mmc));
|
|
|
|
bcm2835_sdhost_dumpregs(host);
|
|
|
|
cmd->error = -EIO;
|
|
|
|
@@ -666,16 +737,16 @@ void bcm2835_sdhost_send_command(struct
|
|
|
|
|
|
|
|
if ((1000-timeout)/100 > 1 && (1000-timeout)/100 > host->max_delay) {
|
|
|
|
host->max_delay = (1000-timeout)/100;
|
|
|
|
- pr_warning("Warning: SDHost controller hung for %d ms\n", host->max_delay);
|
|
|
|
+ pr_warning("%s: controller hung for %d ms\n",
|
|
|
|
+ mmc_hostname(host->mmc),
|
|
|
|
+ host->max_delay);
|
|
|
|
}
|
|
|
|
|
|
|
|
timeout = jiffies;
|
|
|
|
-#ifdef CONFIG_ARCH_BCM2835
|
|
|
|
if (!cmd->data && cmd->busy_timeout > 9000)
|
|
|
|
timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
|
|
|
|
else
|
|
|
|
-#endif
|
|
|
|
- timeout += 10 * HZ;
|
|
|
|
+ timeout += 10 * HZ;
|
|
|
|
mod_timer(&host->timer, timeout);
|
|
|
|
|
|
|
|
host->cmd = cmd;
|
|
|
|
@@ -685,7 +756,7 @@ void bcm2835_sdhost_send_command(struct
|
|
|
|
bcm2835_sdhost_write(host, cmd->arg, SDARG);
|
|
|
|
|
|
|
|
if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
|
|
|
|
- pr_err("%s: Unsupported response type!\n",
|
|
|
|
+ pr_err("%s: unsupported response type!\n",
|
|
|
|
mmc_hostname(host->mmc));
|
|
|
|
cmd->error = -EINVAL;
|
|
|
|
tasklet_schedule(&host->finish_tasklet);
|
|
|
|
@@ -783,13 +854,6 @@ static void bcm2835_sdhost_transfer_comp
|
|
|
|
pr_debug("transfer_complete(error %d, stop %d)\n",
|
|
|
|
data->error, data->stop ? 1 : 0);
|
|
|
|
|
|
|
|
- if (data->error)
|
|
|
|
- /*
|
|
|
|
- * The controller needs a reset of internal state machines
|
|
|
|
- * upon error conditions.
|
|
|
|
- */
|
|
|
|
- bcm2835_sdhost_reset(host);
|
|
|
|
-
|
|
|
|
/*
|
|
|
|
* Need to send CMD12 if -
|
|
|
|
* a) open-ended multiblock transfer (no CMD23)
|
|
|
|
@@ -845,7 +909,7 @@ static void bcm2835_sdhost_finish_comman
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (timeout == 0) {
|
|
|
|
- pr_err("%s: Command never completed.\n",
|
|
|
|
+ pr_err("%s: command never completed.\n",
|
|
|
|
mmc_hostname(host->mmc));
|
|
|
|
bcm2835_sdhost_dumpregs(host);
|
|
|
|
host->cmd->error = -EIO;
|
|
|
|
@@ -875,14 +939,23 @@ static void bcm2835_sdhost_finish_comman
|
|
|
|
{
|
|
|
|
u32 sdhsts = bcm2835_sdhost_read(host, SDHSTS);
|
|
|
|
|
|
|
|
- pr_debug("%s: error detected - CMD %x, HSTS %03x, EDM %x\n",
|
|
|
|
- mmc_hostname(host->mmc), sdcmd, sdhsts,
|
|
|
|
- bcm2835_sdhost_read(host, SDEDM));
|
|
|
|
-
|
|
|
|
- if (sdhsts & SDHSTS_CMD_TIME_OUT)
|
|
|
|
+ if (host->debug)
|
|
|
|
+ pr_info("%s: error detected - CMD %x, HSTS %03x, EDM %x\n",
|
|
|
|
+ mmc_hostname(host->mmc), sdcmd, sdhsts,
|
|
|
|
+ bcm2835_sdhost_read(host, SDEDM));
|
|
|
|
+
|
|
|
|
+ if (sdhsts & SDHSTS_CMD_TIME_OUT) {
|
|
|
|
+ switch (host->cmd->opcode) {
|
|
|
|
+ case 5: case 52: case 53:
|
|
|
|
+ /* Don't warn about SDIO commands */
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ pr_err("%s: command timeout\n",
|
|
|
|
+ mmc_hostname(host->mmc));
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
host->cmd->error = -ETIMEDOUT;
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
+ } else {
|
|
|
|
pr_err("%s: unexpected command error\n",
|
|
|
|
mmc_hostname(host->mmc));
|
|
|
|
bcm2835_sdhost_dumpregs(host);
|
|
|
|
@@ -897,11 +970,13 @@ static void bcm2835_sdhost_finish_comman
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
host->cmd->resp[3 - i] = bcm2835_sdhost_read(host, SDRSP0 + i*4);
|
|
|
|
- pr_debug("bcm2835_sdhost_finish_command: %08x %08x %08x %08x\n",
|
|
|
|
+ pr_debug("%s: finish_command %08x %08x %08x %08x\n",
|
|
|
|
+ mmc_hostname(host->mmc),
|
|
|
|
host->cmd->resp[0], host->cmd->resp[1], host->cmd->resp[2], host->cmd->resp[3]);
|
|
|
|
} else {
|
|
|
|
host->cmd->resp[0] = bcm2835_sdhost_read(host, SDRSP0);
|
|
|
|
- pr_debug("bcm2835_sdhost_finish_command: %08x\n",
|
|
|
|
+ pr_debug("%s: finish_command %08x\n",
|
|
|
|
+ mmc_hostname(host->mmc),
|
|
|
|
host->cmd->resp[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -932,7 +1007,7 @@ static void bcm2835_sdhost_finish_comman
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
-static void bcm2835_sdhost_timeout_timer(unsigned long data)
|
|
|
|
+static void bcm2835_sdhost_timeout(unsigned long data)
|
|
|
|
{
|
|
|
|
struct bcm2835_host *host;
|
|
|
|
unsigned long flags;
|
|
|
|
@@ -942,7 +1017,7 @@ static void bcm2835_sdhost_timeout_timer
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
|
|
|
|
|
|
if (host->mrq) {
|
|
|
|
- pr_err("%s: Timeout waiting for hardware interrupt.\n",
|
|
|
|
+ pr_err("%s: timeout waiting for hardware interrupt.\n",
|
|
|
|
mmc_hostname(host->mmc));
|
|
|
|
bcm2835_sdhost_dumpregs(host);
|
|
|
|
|
|
|
|
@@ -964,6 +1039,41 @@ static void bcm2835_sdhost_timeout_timer
|
|
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
+static void bcm2835_sdhost_pio_timeout(unsigned long data)
|
|
|
|
+{
|
|
|
|
+ struct bcm2835_host *host;
|
|
|
|
+ unsigned long flags;
|
|
|
|
+
|
|
|
|
+ host = (struct bcm2835_host *)data;
|
|
|
|
+
|
|
|
|
+ spin_lock_irqsave(&host->lock, flags);
|
|
|
|
+
|
|
|
|
+ if (host->data) {
|
|
|
|
+ u32 hsts = bcm2835_sdhost_read(host, SDHSTS);
|
|
|
|
+
|
|
|
|
+ if (hsts & SDHSTS_REW_TIME_OUT) {
|
|
|
|
+ pr_err("%s: transfer timeout\n",
|
|
|
|
+ mmc_hostname(host->mmc));
|
|
|
|
+ if (host->debug)
|
|
|
|
+ bcm2835_sdhost_dumpregs(host);
|
|
|
|
+ } else {
|
|
|
|
+ pr_err("%s: unexpected transfer timeout\n",
|
|
|
|
+ mmc_hostname(host->mmc));
|
|
|
|
+ bcm2835_sdhost_dumpregs(host);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ bcm2835_sdhost_write(host, SDHSTS_TRANSFER_ERROR_MASK,
|
|
|
|
+ SDHSTS);
|
|
|
|
+
|
|
|
|
+ host->data->error = -ETIMEDOUT;
|
|
|
|
+
|
|
|
|
+ bcm2835_sdhost_finish_data(host);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ mmiowb();
|
|
|
|
+ spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
static void bcm2835_sdhost_enable_sdio_irq_nolock(struct bcm2835_host *host, int enable)
|
|
|
|
{
|
|
|
|
if (enable)
|
|
|
|
@@ -979,7 +1089,7 @@ static void bcm2835_sdhost_enable_sdio_i
|
|
|
|
struct bcm2835_host *host = mmc_priv(mmc);
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
- pr_debug("bcm2835_sdhost_enable_sdio_irq(%d)\n", enable);
|
|
|
|
+ pr_debug("%s: enable_sdio_irq(%d)\n", mmc_hostname(mmc), enable);
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
|
|
bcm2835_sdhost_enable_sdio_irq_nolock(host, enable);
|
|
|
|
spin_unlock_irqrestore(&host->lock, flags);
|
|
|
|
@@ -987,11 +1097,12 @@ static void bcm2835_sdhost_enable_sdio_i
|
|
|
|
|
|
|
|
static u32 bcm2835_sdhost_busy_irq(struct bcm2835_host *host, u32 intmask)
|
|
|
|
{
|
|
|
|
- const u32 handled = (SDHSTS_CMD_TIME_OUT | SDHSTS_CRC16_ERROR |
|
|
|
|
- SDHSTS_CRC7_ERROR | SDHSTS_FIFO_ERROR);
|
|
|
|
+ const u32 handled = (SDHSTS_REW_TIME_OUT | SDHSTS_CMD_TIME_OUT |
|
|
|
|
+ SDHSTS_CRC16_ERROR | SDHSTS_CRC7_ERROR |
|
|
|
|
+ SDHSTS_FIFO_ERROR);
|
|
|
|
|
|
|
|
if (!host->cmd) {
|
|
|
|
- pr_err("%s: Got command busy interrupt 0x%08x even "
|
|
|
|
+ pr_err("%s: got command busy interrupt 0x%08x even "
|
|
|
|
"though no command operation was in progress.\n",
|
|
|
|
mmc_hostname(host->mmc), (unsigned)intmask);
|
|
|
|
bcm2835_sdhost_dumpregs(host);
|
|
|
|
@@ -999,7 +1110,7 @@ static u32 bcm2835_sdhost_busy_irq(struc
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!host->use_busy) {
|
|
|
|
- pr_err("%s: Got command busy interrupt 0x%08x even "
|
|
|
|
+ pr_err("%s: got command busy interrupt 0x%08x even "
|
|
|
|
"though not expecting one.\n",
|
|
|
|
mmc_hostname(host->mmc), (unsigned)intmask);
|
|
|
|
bcm2835_sdhost_dumpregs(host);
|
|
|
|
@@ -1007,14 +1118,28 @@ static u32 bcm2835_sdhost_busy_irq(struc
|
|
|
|
}
|
|
|
|
host->use_busy = 0;
|
|
|
|
|
|
|
|
- if (intmask & SDHSTS_CMD_TIME_OUT)
|
|
|
|
- host->cmd->error = -ETIMEDOUT;
|
|
|
|
- else if (intmask & (SDHSTS_CRC16_ERROR | SDHSTS_CRC7_ERROR |
|
|
|
|
- SDHSTS_FIFO_ERROR))
|
|
|
|
- host->cmd->error = -EILSEQ;
|
|
|
|
+ if (intmask & SDHSTS_ERROR_MASK)
|
|
|
|
+ {
|
|
|
|
+ pr_err("sdhost_busy_irq: intmask %x, data %p\n", intmask, host->mrq->data);
|
|
|
|
+ if (intmask & SDHSTS_CRC7_ERROR)
|
|
|
|
+ host->cmd->error = -EILSEQ;
|
|
|
|
+ else if (intmask & (SDHSTS_CRC16_ERROR |
|
|
|
|
+ SDHSTS_FIFO_ERROR)) {
|
|
|
|
+ if (host->mrq->data)
|
|
|
|
+ host->mrq->data->error = -EILSEQ;
|
|
|
|
+ else
|
|
|
|
+ host->cmd->error = -EILSEQ;
|
|
|
|
+ } else if (intmask & SDHSTS_REW_TIME_OUT) {
|
|
|
|
+ if (host->mrq->data)
|
|
|
|
+ host->mrq->data->error = -ETIMEDOUT;
|
|
|
|
+ else
|
|
|
|
+ host->cmd->error = -ETIMEDOUT;
|
|
|
|
+ } else if (intmask & SDHSTS_CMD_TIME_OUT)
|
|
|
|
+ host->cmd->error = -ETIMEDOUT;
|
|
|
|
|
|
|
|
- if (host->cmd->error)
|
|
|
|
+ bcm2835_sdhost_dumpregs(host);
|
|
|
|
tasklet_schedule(&host->finish_tasklet);
|
|
|
|
+ }
|
|
|
|
else
|
|
|
|
bcm2835_sdhost_finish_command(host);
|
|
|
|
|
|
|
|
@@ -1023,8 +1148,9 @@ static u32 bcm2835_sdhost_busy_irq(struc
|
|
|
|
|
|
|
|
static u32 bcm2835_sdhost_data_irq(struct bcm2835_host *host, u32 intmask)
|
|
|
|
{
|
|
|
|
- const u32 handled = (SDHSTS_CMD_TIME_OUT | SDHSTS_CRC16_ERROR |
|
|
|
|
- SDHSTS_CRC7_ERROR | SDHSTS_FIFO_ERROR);
|
|
|
|
+ const u32 handled = (SDHSTS_REW_TIME_OUT |
|
|
|
|
+ SDHSTS_CRC16_ERROR |
|
|
|
|
+ SDHSTS_FIFO_ERROR);
|
|
|
|
|
|
|
|
/* There are no dedicated data/space available interrupt
|
|
|
|
status bits, so it is necessary to use the single shared
|
|
|
|
@@ -1034,13 +1160,19 @@ static u32 bcm2835_sdhost_data_irq(struc
|
|
|
|
if (!host->data)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
- // XXX FIFO_ERROR
|
|
|
|
- if (intmask & SDHSTS_CMD_TIME_OUT)
|
|
|
|
- host->cmd->error = -ETIMEDOUT;
|
|
|
|
- else if ((intmask & (SDHSTS_CRC16_ERROR | SDHSTS_CRC7_ERROR)) &&
|
|
|
|
- ((bcm2835_sdhost_read(host, SDCMD) & SDCMD_CMD_MASK)
|
|
|
|
- != MMC_BUS_TEST_R))
|
|
|
|
- host->cmd->error = -EILSEQ;
|
|
|
|
+ if (intmask & (SDHSTS_CRC16_ERROR |
|
|
|
|
+ SDHSTS_FIFO_ERROR |
|
|
|
|
+ SDHSTS_REW_TIME_OUT)) {
|
|
|
|
+ if (intmask & (SDHSTS_CRC16_ERROR |
|
|
|
|
+ SDHSTS_FIFO_ERROR))
|
|
|
|
+ host->data->error = -EILSEQ;
|
|
|
|
+ else
|
|
|
|
+ host->data->error = -ETIMEDOUT;
|
|
|
|
+
|
|
|
|
+ bcm2835_sdhost_dumpregs(host);
|
|
|
|
+ tasklet_schedule(&host->finish_tasklet);
|
|
|
|
+ return handled;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
/* Use the block interrupt for writes after the first block */
|
|
|
|
if (host->data->flags & MMC_DATA_WRITE) {
|
|
|
|
@@ -1067,31 +1199,48 @@ static u32 bcm2835_sdhost_block_irq(stru
|
|
|
|
{
|
|
|
|
struct dma_chan *dma_chan;
|
|
|
|
u32 dir_data;
|
|
|
|
- const u32 handled = (SDHSTS_CMD_TIME_OUT | SDHSTS_CRC16_ERROR |
|
|
|
|
- SDHSTS_CRC7_ERROR | SDHSTS_FIFO_ERROR);
|
|
|
|
+ const u32 handled = (SDHSTS_REW_TIME_OUT |
|
|
|
|
+ SDHSTS_CRC16_ERROR |
|
|
|
|
+ SDHSTS_FIFO_ERROR);
|
|
|
|
|
|
|
|
if (!host->data) {
|
|
|
|
- pr_err("%s: Got block interrupt 0x%08x even "
|
|
|
|
+ pr_err("%s: got block interrupt 0x%08x even "
|
|
|
|
"though no data operation was in progress.\n",
|
|
|
|
mmc_hostname(host->mmc), (unsigned)intmask);
|
|
|
|
bcm2835_sdhost_dumpregs(host);
|
|
|
|
return handled;
|
|
|
|
}
|
|
|
|
|
|
|
|
- if (intmask & SDHSTS_CMD_TIME_OUT)
|
|
|
|
- host->cmd->error = -ETIMEDOUT;
|
|
|
|
- else if ((intmask & (SDHSTS_CRC16_ERROR | SDHSTS_CRC7_ERROR)) &&
|
|
|
|
- ((bcm2835_sdhost_read(host, SDCMD) & SDCMD_CMD_MASK)
|
|
|
|
- != MMC_BUS_TEST_R))
|
|
|
|
- host->cmd->error = -EILSEQ;
|
|
|
|
+ if (intmask & (SDHSTS_CRC16_ERROR |
|
|
|
|
+ SDHSTS_FIFO_ERROR |
|
|
|
|
+ SDHSTS_REW_TIME_OUT)) {
|
|
|
|
+ if (intmask & (SDHSTS_CRC16_ERROR |
|
|
|
|
+ SDHSTS_FIFO_ERROR))
|
|
|
|
+ host->data->error = -EILSEQ;
|
|
|
|
+ else
|
|
|
|
+ host->data->error = -ETIMEDOUT;
|
|
|
|
+
|
|
|
|
+ if (host->debug)
|
|
|
|
+ bcm2835_sdhost_dumpregs(host);
|
|
|
|
+ tasklet_schedule(&host->finish_tasklet);
|
|
|
|
+ return handled;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
if (!host->use_dma) {
|
|
|
|
BUG_ON(!host->blocks);
|
|
|
|
host->blocks--;
|
|
|
|
- if ((host->blocks == 0) || host->data->error)
|
|
|
|
+ if ((host->blocks == 0) || host->data->error) {
|
|
|
|
+ /* Cancel the timer */
|
|
|
|
+ del_timer(&host->pio_timer);
|
|
|
|
+
|
|
|
|
bcm2835_sdhost_finish_data(host);
|
|
|
|
- else
|
|
|
|
+ } else {
|
|
|
|
bcm2835_sdhost_transfer_pio(host);
|
|
|
|
+
|
|
|
|
+ /* Reset the timer */
|
|
|
|
+ mod_timer(&host->pio_timer,
|
|
|
|
+ jiffies + host->pio_timeout);
|
|
|
|
+ }
|
|
|
|
} else if (host->data->flags & MMC_DATA_WRITE) {
|
|
|
|
dma_chan = host->dma_chan_tx;
|
|
|
|
dir_data = DMA_TO_DEVICE;
|
|
|
|
@@ -1125,7 +1274,7 @@ static irqreturn_t bcm2835_sdhost_irq(in
|
|
|
|
SDHSTS_BLOCK_IRPT |
|
|
|
|
SDHSTS_SDIO_IRPT |
|
|
|
|
SDHSTS_DATA_FLAG);
|
|
|
|
- if ((handled == SDHSTS_DATA_FLAG) && // XXX
|
|
|
|
+ if ((handled == SDHSTS_DATA_FLAG) &&
|
|
|
|
(loops == 0) && !host->data) {
|
|
|
|
pr_err("%s: sdhost_irq data interrupt 0x%08x even "
|
|
|
|
"though no data operation was in progress.\n",
|
|
|
|
@@ -1177,10 +1326,11 @@ static irqreturn_t bcm2835_sdhost_irq(in
|
|
|
|
spin_unlock(&host->lock);
|
|
|
|
|
|
|
|
if (early)
|
|
|
|
- pr_debug("%s: early %x (loops %d)\n", mmc_hostname(host->mmc), early, loops);
|
|
|
|
+ pr_debug("%s: early %x (loops %d)\n",
|
|
|
|
+ mmc_hostname(host->mmc), early, loops);
|
|
|
|
|
|
|
|
if (unexpected) {
|
|
|
|
- pr_err("%s: Unexpected interrupt 0x%08x.\n",
|
|
|
|
+ pr_err("%s: unexpected interrupt 0x%08x.\n",
|
|
|
|
mmc_hostname(host->mmc), unexpected);
|
|
|
|
bcm2835_sdhost_dumpregs(host);
|
|
|
|
}
|
|
|
|
@@ -1227,8 +1377,22 @@ void bcm2835_sdhost_set_clock(struct bcm
|
|
|
|
int div = 0; /* Initialized for compiler warning */
|
|
|
|
unsigned int input_clock = clock;
|
|
|
|
|
|
|
|
- if (host->overclock_50 && (clock == 50000000))
|
|
|
|
- clock = host->overclock_50 * 1000000 + 999999;
|
|
|
|
+ if (host->debug)
|
|
|
|
+ pr_info("%s: set_clock(%d)\n", mmc_hostname(host->mmc), clock);
|
|
|
|
+
|
|
|
|
+ if ((clock == 0) && host->reduce_overclock) {
|
|
|
|
+ /* This is a reset following data corruption - reduce any
|
|
|
|
+ overclock */
|
|
|
|
+ host->reduce_overclock = 0;
|
|
|
|
+ if (host->overclock_50 > 50) {
|
|
|
|
+ pr_warn("%s: reducing overclock due to errors\n",
|
|
|
|
+ mmc_hostname(host->mmc));
|
|
|
|
+ host->overclock_50--;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (host->overclock_50 && (clock == 50*MHZ))
|
|
|
|
+ clock = host->overclock_50 * MHZ + (MHZ - 1);
|
|
|
|
|
|
|
|
/* The SDCDIV register has 11 bits, and holds (div - 2).
|
|
|
|
But in data mode the max is 50MHz wihout a minimum, and only the
|
|
|
|
@@ -1275,17 +1439,34 @@ void bcm2835_sdhost_set_clock(struct bcm
|
|
|
|
clock = host->max_clk / (div + 2);
|
|
|
|
host->mmc->actual_clock = clock;
|
|
|
|
|
|
|
|
- if ((clock > input_clock) && (clock > host->max_overclock)) {
|
|
|
|
- pr_warn("%s: Overclocking to %dHz\n",
|
|
|
|
- mmc_hostname(host->mmc), clock);
|
|
|
|
- host->max_overclock = clock;
|
|
|
|
+ if (clock > input_clock) {
|
|
|
|
+ /* Save the closest value, to make it easier
|
|
|
|
+ to reduce in the event of error */
|
|
|
|
+ host->overclock_50 = (clock/MHZ);
|
|
|
|
+
|
|
|
|
+ if (clock != host->overclock) {
|
|
|
|
+ pr_warn("%s: overclocking to %dHz\n",
|
|
|
|
+ mmc_hostname(host->mmc), clock);
|
|
|
|
+ host->overclock = clock;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else if ((clock == 50 * MHZ) && host->overclock)
|
|
|
|
+ {
|
|
|
|
+ pr_warn("%s: cancelling overclock\n",
|
|
|
|
+ mmc_hostname(host->mmc));
|
|
|
|
+ host->overclock = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
host->cdiv = div;
|
|
|
|
bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
|
|
|
|
|
|
|
|
- pr_debug(DRIVER_NAME ": clock=%d -> max_clk=%d, cdiv=%x (actual clock %d)\n",
|
|
|
|
- input_clock, host->max_clk, host->cdiv, host->mmc->actual_clock);
|
|
|
|
+ /* Set the timeout to 500ms */
|
|
|
|
+ bcm2835_sdhost_write(host, host->mmc->actual_clock/2, SDTOUT);
|
|
|
|
+
|
|
|
|
+ if (host->debug)
|
|
|
|
+ pr_info("%s: clock=%d -> max_clk=%d, cdiv=%x (actual clock %d)\n",
|
|
|
|
+ mmc_hostname(host->mmc), input_clock,
|
|
|
|
+ host->max_clk, host->cdiv, host->mmc->actual_clock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bcm2835_sdhost_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|
|
|
@@ -1293,29 +1474,32 @@ static void bcm2835_sdhost_request(struc
|
|
|
|
struct bcm2835_host *host;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
- if (1) {
|
|
|
|
+ host = mmc_priv(mmc);
|
|
|
|
+
|
|
|
|
+ if (host->debug) {
|
|
|
|
struct mmc_command *cmd = mrq->cmd;
|
|
|
|
- const char *src = "cmd";
|
|
|
|
BUG_ON(!cmd);
|
|
|
|
- pr_debug("bcm2835_sdhost_request: %s %08x %08x (flags %x)\n",
|
|
|
|
- src, cmd->opcode, cmd->arg, cmd->flags);
|
|
|
|
if (cmd->data)
|
|
|
|
- pr_debug("bcm2835_sdhost_request: %s %d*%d\n",
|
|
|
|
- (cmd->data->flags & MMC_DATA_READ) ?
|
|
|
|
- "read" : "write", cmd->data->blocks,
|
|
|
|
- cmd->data->blksz);
|
|
|
|
+ pr_info("%s: cmd %d 0x%x (flags 0x%x) - %s %d*%d\n",
|
|
|
|
+ mmc_hostname(mmc),
|
|
|
|
+ cmd->opcode, cmd->arg, cmd->flags,
|
|
|
|
+ (cmd->data->flags & MMC_DATA_READ) ?
|
|
|
|
+ "read" : "write", cmd->data->blocks,
|
|
|
|
+ cmd->data->blksz);
|
|
|
|
+ else
|
|
|
|
+ pr_info("%s: cmd %d 0x%x (flags 0x%x)\n",
|
|
|
|
+ mmc_hostname(mmc),
|
|
|
|
+ cmd->opcode, cmd->arg, cmd->flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mrq->data && !is_power_of_2(mrq->data->blksz)) {
|
|
|
|
- pr_err("%s: Unsupported block size (%d bytes)\n",
|
|
|
|
+ pr_err("%s: unsupported block size (%d bytes)\n",
|
|
|
|
mmc_hostname(mmc), mrq->data->blksz);
|
|
|
|
mrq->cmd->error = -EINVAL;
|
|
|
|
mmc_request_done(mmc, mrq);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
- host = mmc_priv(mmc);
|
|
|
|
-
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
|
|
|
|
|
|
WARN_ON(host->mrq != NULL);
|
|
|
|
@@ -1345,9 +1529,12 @@ static void bcm2835_sdhost_set_ios(struc
|
|
|
|
struct bcm2835_host *host = mmc_priv(mmc);
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
- pr_debug("bcm2835_sdhost_set_ios: clock %d, pwr %d, bus_width %d, timing %d, vdd %d, drv_type %d\n",
|
|
|
|
- ios->clock, ios->power_mode, ios->bus_width,
|
|
|
|
- ios->timing, ios->signal_voltage, ios->drv_type);
|
|
|
|
+ if (host->debug)
|
|
|
|
+ pr_info("%s: ios clock %d, pwr %d, bus_width %d, "
|
|
|
|
+ "timing %d, vdd %d, drv_type %d\n",
|
|
|
|
+ mmc_hostname(mmc),
|
|
|
|
+ ios->clock, ios->power_mode, ios->bus_width,
|
|
|
|
+ ios->timing, ios->signal_voltage, ios->drv_type);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&host->lock, flags);
|
|
|
|
|
|
|
|
@@ -1396,6 +1583,7 @@ static struct mmc_host_ops bcm2835_sdhos
|
|
|
|
.request = bcm2835_sdhost_request,
|
|
|
|
.set_ios = bcm2835_sdhost_set_ios,
|
|
|
|
.enable_sdio_irq = bcm2835_sdhost_enable_sdio_irq,
|
|
|
|
+ .hw_reset = bcm2835_sdhost_reset,
|
|
|
|
.multi_io_quirk = bcm2835_sdhost_multi_io_quirk,
|
|
|
|
};
|
|
|
|
|
|
|
|
@@ -1423,15 +1611,24 @@ static void bcm2835_sdhost_tasklet_finis
|
|
|
|
|
|
|
|
mrq = host->mrq;
|
|
|
|
|
|
|
|
- /*
|
|
|
|
- * The controller needs a reset of internal state machines
|
|
|
|
- * upon error conditions.
|
|
|
|
- */
|
|
|
|
- if (((mrq->cmd && mrq->cmd->error) ||
|
|
|
|
- (mrq->data && (mrq->data->error ||
|
|
|
|
- (mrq->data->stop && mrq->data->stop->error))))) {
|
|
|
|
+ /* Drop the overclock after any data corruption, or after any
|
|
|
|
+ error overclocked */
|
|
|
|
+ if (mrq->data && (mrq->data->error == -EILSEQ))
|
|
|
|
+ host->reduce_overclock = 1;
|
|
|
|
+ else if (host->overclock) {
|
|
|
|
+ /* Convert timeout errors while overclocked to data errors,
|
|
|
|
+ because the system recovers better. */
|
|
|
|
+ if (mrq->cmd && mrq->cmd->error) {
|
|
|
|
+ host->reduce_overclock = 1;
|
|
|
|
+ if (mrq->cmd->error == -ETIMEDOUT)
|
|
|
|
+ mrq->cmd->error = -EILSEQ;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- bcm2835_sdhost_reset(host);
|
|
|
|
+ if (mrq->data && mrq->data->error) {
|
|
|
|
+ host->reduce_overclock = 1;
|
|
|
|
+ if (mrq->data->error == -ETIMEDOUT)
|
|
|
|
+ mrq->data->error = -EILSEQ;
|
|
|
|
+ }
|
|
|
|
}
|
|
|
|
|
|
|
|
host->mrq = NULL;
|
|
|
|
@@ -1450,35 +1647,37 @@ int bcm2835_sdhost_add_host(struct bcm28
|
|
|
|
{
|
|
|
|
struct mmc_host *mmc;
|
|
|
|
struct dma_slave_config cfg;
|
|
|
|
+ char pio_limit_string[20];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
mmc = host->mmc;
|
|
|
|
|
|
|
|
- bcm2835_sdhost_reset(host);
|
|
|
|
+ bcm2835_sdhost_reset_internal(host);
|
|
|
|
|
|
|
|
mmc->f_max = host->max_clk;
|
|
|
|
mmc->f_min = host->max_clk / SDCDIV_MAX_CDIV;
|
|
|
|
|
|
|
|
- /* SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK */
|
|
|
|
- host->timeout_clk = mmc->f_max / 1000;
|
|
|
|
-#ifdef CONFIG_ARCH_BCM2835
|
|
|
|
- mmc->max_busy_timeout = (1 << 27) / host->timeout_clk;
|
|
|
|
-#endif
|
|
|
|
+ mmc->max_busy_timeout = (~(unsigned int)0)/(mmc->f_max/1000);
|
|
|
|
+
|
|
|
|
+ pr_debug("f_max %d, f_min %d, max_busy_timeout %d\n",
|
|
|
|
+ mmc->f_max, mmc->f_min, mmc->max_busy_timeout);
|
|
|
|
+
|
|
|
|
/* host controller capabilities */
|
|
|
|
mmc->caps |= /* MMC_CAP_SDIO_IRQ |*/ MMC_CAP_4_BIT_DATA |
|
|
|
|
MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
|
|
|
|
- MMC_CAP_NEEDS_POLL |
|
|
|
|
+ MMC_CAP_NEEDS_POLL | MMC_CAP_HW_RESET |
|
|
|
|
(ALLOW_CMD23 * MMC_CAP_CMD23);
|
|
|
|
|
|
|
|
spin_lock_init(&host->lock);
|
|
|
|
|
|
|
|
if (host->allow_dma) {
|
|
|
|
- if (!host->dma_chan_tx || !host->dma_chan_rx ||
|
|
|
|
- IS_ERR(host->dma_chan_tx) || IS_ERR(host->dma_chan_rx)) {
|
|
|
|
- pr_err("%s: Unable to initialise DMA channels. Falling back to PIO\n", DRIVER_NAME);
|
|
|
|
+ if (IS_ERR_OR_NULL(host->dma_chan_tx) ||
|
|
|
|
+ IS_ERR_OR_NULL(host->dma_chan_rx)) {
|
|
|
|
+ pr_err("%s: unable to initialise DMA channels. "
|
|
|
|
+ "Falling back to PIO\n",
|
|
|
|
+ mmc_hostname(mmc));
|
|
|
|
host->have_dma = false;
|
|
|
|
} else {
|
|
|
|
- pr_info("DMA channels allocated for the SDHost driver");
|
|
|
|
host->have_dma = true;
|
|
|
|
|
|
|
|
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
|
|
|
@@ -1496,7 +1695,6 @@ int bcm2835_sdhost_add_host(struct bcm28
|
|
|
|
ret = dmaengine_slave_config(host->dma_chan_rx, &cfg);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
- pr_info("Forcing PIO mode\n");
|
|
|
|
host->have_dma = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@@ -1512,18 +1710,23 @@ int bcm2835_sdhost_add_host(struct bcm28
|
|
|
|
tasklet_init(&host->finish_tasklet,
|
|
|
|
bcm2835_sdhost_tasklet_finish, (unsigned long)host);
|
|
|
|
|
|
|
|
- setup_timer(&host->timer, bcm2835_sdhost_timeout_timer, (unsigned long)host);
|
|
|
|
+ setup_timer(&host->timer, bcm2835_sdhost_timeout,
|
|
|
|
+ (unsigned long)host);
|
|
|
|
+
|
|
|
|
+ setup_timer(&host->pio_timer, bcm2835_sdhost_pio_timeout,
|
|
|
|
+ (unsigned long)host);
|
|
|
|
|
|
|
|
bcm2835_sdhost_init(host, 0);
|
|
|
|
#ifndef CONFIG_ARCH_BCM2835
|
|
|
|
ret = request_irq(host->irq, bcm2835_sdhost_irq, 0 /*IRQF_SHARED*/,
|
|
|
|
mmc_hostname(mmc), host);
|
|
|
|
#else
|
|
|
|
- ret = request_threaded_irq(host->irq, bcm2835_sdhost_irq, bcm2835_sdhost_thread_irq,
|
|
|
|
+ ret = request_threaded_irq(host->irq, bcm2835_sdhost_irq,
|
|
|
|
+ bcm2835_sdhost_thread_irq,
|
|
|
|
IRQF_SHARED, mmc_hostname(mmc), host);
|
|
|
|
#endif
|
|
|
|
if (ret) {
|
|
|
|
- pr_err("%s: Failed to request IRQ %d: %d\n",
|
|
|
|
+ pr_err("%s: failed to request IRQ %d: %d\n",
|
|
|
|
mmc_hostname(mmc), host->irq, ret);
|
|
|
|
goto untasklet;
|
|
|
|
}
|
|
|
|
@@ -1531,10 +1734,13 @@ int bcm2835_sdhost_add_host(struct bcm28
|
|
|
|
mmiowb();
|
|
|
|
mmc_add_host(mmc);
|
|
|
|
|
|
|
|
- pr_info("Load BCM2835 SDHost driver\n");
|
|
|
|
- if (host->delay_after_stop)
|
|
|
|
- pr_info("BCM2835 SDHost: delay_after_stop=%dus\n",
|
|
|
|
- host->delay_after_stop);
|
|
|
|
+ pio_limit_string[0] = '\0';
|
|
|
|
+ if (host->have_dma && (host->pio_limit > 0))
|
|
|
|
+ sprintf(pio_limit_string, " (>%d)", host->pio_limit);
|
|
|
|
+ pr_info("%s: %s loaded - DMA %s%s\n",
|
|
|
|
+ mmc_hostname(mmc), DRIVER_NAME,
|
|
|
|
+ host->have_dma ? "enabled" : "disabled",
|
|
|
|
+ pio_limit_string);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
@@ -1562,7 +1768,7 @@ static int bcm2835_sdhost_probe(struct p
|
|
|
|
mmc->ops = &bcm2835_sdhost_ops;
|
|
|
|
host = mmc_priv(mmc);
|
|
|
|
host->mmc = mmc;
|
|
|
|
- host->timeout = msecs_to_jiffies(1000);
|
|
|
|
+ host->pio_timeout = msecs_to_jiffies(500);
|
|
|
|
spin_lock_init(&host->lock);
|
|
|
|
|
|
|
|
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
|
@@ -1588,8 +1794,12 @@ static int bcm2835_sdhost_probe(struct p
|
|
|
|
of_property_read_u32(node,
|
|
|
|
"brcm,overclock-50",
|
|
|
|
&host->overclock_50);
|
|
|
|
+ of_property_read_u32(node,
|
|
|
|
+ "brcm,pio-limit",
|
|
|
|
+ &host->pio_limit);
|
|
|
|
host->allow_dma = ALLOW_DMA &&
|
|
|
|
!of_property_read_bool(node, "brcm,force-pio");
|
|
|
|
+ host->debug = of_property_read_bool(node, "brcm,debug");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (host->allow_dma) {
|