aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/brcm2708/patches-4.4/0416-bcm2835-sdhost-Improvements-to-error-recovery.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/brcm2708/patches-4.4/0416-bcm2835-sdhost-Improvements-to-error-recovery.patch')
-rw-r--r--target/linux/brcm2708/patches-4.4/0416-bcm2835-sdhost-Improvements-to-error-recovery.patch312
1 files changed, 312 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-4.4/0416-bcm2835-sdhost-Improvements-to-error-recovery.patch b/target/linux/brcm2708/patches-4.4/0416-bcm2835-sdhost-Improvements-to-error-recovery.patch
new file mode 100644
index 0000000000..0437931d16
--- /dev/null
+++ b/target/linux/brcm2708/patches-4.4/0416-bcm2835-sdhost-Improvements-to-error-recovery.patch
@@ -0,0 +1,312 @@
+From d90c379ffd993c72371252fe7840c14e4428410b Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.org>
+Date: Wed, 15 Jun 2016 17:13:55 +0100
+Subject: [PATCH] bcm2835-sdhost: Improvements to error recovery
+
+1) Try to avoid reducing overclock when a card is removed.
+
+2) Reset overclock on card insertion.
+
+3) Reduce logging when errors occur, lowering the severity of
+ some messages and making others conditional on the debug
+ flag.
+
+4) Attempt to identify a disconnected SD bus earlier, treating a
+ zero returned OCR (voltage support) as an error condition.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.org>
+---
+ drivers/mmc/host/bcm2835-sdhost.c | 117 ++++++++++++++++++++++++--------------
+ 1 file changed, 74 insertions(+), 43 deletions(-)
+
+--- a/drivers/mmc/host/bcm2835-sdhost.c
++++ b/drivers/mmc/host/bcm2835-sdhost.c
+@@ -38,6 +38,7 @@
+ #include <linux/mmc/mmc.h>
+ #include <linux/mmc/host.h>
+ #include <linux/mmc/sd.h>
++#include <linux/mmc/sdio.h>
+ #include <linux/scatterlist.h>
+ #include <linux/of_address.h>
+ #include <linux/of_irq.h>
+@@ -206,6 +207,7 @@ 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 delay_after_this_stop; /* minimum time between this stop and subsequent data transfer */
++ u32 user_overclock_50; /* User's preferred frequency to use when 50MHz is requested (in MHz) */
+ 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) */
+@@ -282,7 +284,7 @@ static void log_dump(void)
+ do {
+ entry = sdhost_log_buf + idx;
+ if (entry->event[0] != '\0')
+- pr_err("[%08x] %.4s %x %x\n",
++ pr_info("[%08x] %.4s %x %x\n",
+ entry->timestamp,
+ entry->event,
+ entry->param1,
+@@ -324,7 +326,7 @@ static void bcm2835_sdhost_dumpcmd(struc
+ const char *label)
+ {
+ if (cmd)
+- pr_err("%s:%c%s op %d arg 0x%x flags 0x%x - resp %08x %08x %08x %08x, err %d\n",
++ 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,
+@@ -339,7 +341,7 @@ static void bcm2835_sdhost_dumpregs(stru
+ 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",
++ pr_info("%s: data blocks %x blksz %x - err %d\n",
+ mmc_hostname(host->mmc),
+ host->mrq->data->blocks,
+ host->mrq->data->blksz,
+@@ -347,53 +349,53 @@ static void bcm2835_sdhost_dumpregs(stru
+ bcm2835_sdhost_dumpcmd(host, host->mrq->stop, "stop");
+ }
+
+- pr_err("%s: =========== REGISTER DUMP ===========\n",
++ pr_info("%s: =========== REGISTER DUMP ===========\n",
+ mmc_hostname(host->mmc));
+
+- pr_err("%s: SDCMD 0x%08x\n",
++ pr_info("%s: SDCMD 0x%08x\n",
+ mmc_hostname(host->mmc),
+ bcm2835_sdhost_read(host, SDCMD));
+- pr_err("%s: SDARG 0x%08x\n",
++ pr_info("%s: SDARG 0x%08x\n",
+ mmc_hostname(host->mmc),
+ bcm2835_sdhost_read(host, SDARG));
+- pr_err("%s: SDTOUT 0x%08x\n",
++ pr_info("%s: SDTOUT 0x%08x\n",
+ mmc_hostname(host->mmc),
+ bcm2835_sdhost_read(host, SDTOUT));
+- pr_err("%s: SDCDIV 0x%08x\n",
++ pr_info("%s: SDCDIV 0x%08x\n",
+ mmc_hostname(host->mmc),
+ bcm2835_sdhost_read(host, SDCDIV));
+- pr_err("%s: SDRSP0 0x%08x\n",
++ pr_info("%s: SDRSP0 0x%08x\n",
+ mmc_hostname(host->mmc),
+ bcm2835_sdhost_read(host, SDRSP0));
+- pr_err("%s: SDRSP1 0x%08x\n",
++ pr_info("%s: SDRSP1 0x%08x\n",
+ mmc_hostname(host->mmc),
+ bcm2835_sdhost_read(host, SDRSP1));
+- pr_err("%s: SDRSP2 0x%08x\n",
++ pr_info("%s: SDRSP2 0x%08x\n",
+ mmc_hostname(host->mmc),
+ bcm2835_sdhost_read(host, SDRSP2));
+ pr_err("%s: SDRSP3 0x%08x\n",
+ mmc_hostname(host->mmc),
+ bcm2835_sdhost_read(host, SDRSP3));
+- pr_err("%s: SDHSTS 0x%08x\n",
++ pr_info("%s: SDHSTS 0x%08x\n",
+ mmc_hostname(host->mmc),
+ bcm2835_sdhost_read(host, SDHSTS));
+- pr_err("%s: SDVDD 0x%08x\n",
++ pr_info("%s: SDVDD 0x%08x\n",
+ mmc_hostname(host->mmc),
+ bcm2835_sdhost_read(host, SDVDD));
+- pr_err("%s: SDEDM 0x%08x\n",
++ pr_info("%s: SDEDM 0x%08x\n",
+ mmc_hostname(host->mmc),
+ bcm2835_sdhost_read(host, SDEDM));
+- pr_err("%s: SDHCFG 0x%08x\n",
++ pr_info("%s: SDHCFG 0x%08x\n",
+ mmc_hostname(host->mmc),
+ bcm2835_sdhost_read(host, SDHCFG));
+- pr_err("%s: SDHBCT 0x%08x\n",
++ pr_info("%s: SDHBCT 0x%08x\n",
+ mmc_hostname(host->mmc),
+ bcm2835_sdhost_read(host, SDHBCT));
+- pr_err("%s: SDHBLC 0x%08x\n",
++ pr_info("%s: SDHBLC 0x%08x\n",
+ mmc_hostname(host->mmc),
+ bcm2835_sdhost_read(host, SDHBLC));
+
+- pr_err("%s: ===========================================\n",
++ pr_info("%s: ===========================================\n",
+ mmc_hostname(host->mmc));
+ }
+
+@@ -608,7 +610,7 @@ static void bcm2835_sdhost_read_block_pi
+ (fsm_state != SDEDM_FSM_READCRC)) {
+ hsts = bcm2835_sdhost_read(host,
+ SDHSTS);
+- pr_err("%s: fsm %x, hsts %x\n",
++ pr_info("%s: fsm %x, hsts %x\n",
+ mmc_hostname(host->mmc),
+ fsm_state, hsts);
+ if (hsts & SDHSTS_ERROR_MASK)
+@@ -698,7 +700,7 @@ static void bcm2835_sdhost_write_block_p
+ (fsm_state != SDEDM_FSM_WRITESTART2)) {
+ hsts = bcm2835_sdhost_read(host,
+ SDHSTS);
+- pr_err("%s: fsm %x, hsts %x\n",
++ pr_info("%s: fsm %x, hsts %x\n",
+ mmc_hostname(host->mmc),
+ fsm_state, hsts);
+ if (hsts & SDHSTS_ERROR_MASK)
+@@ -953,9 +955,10 @@ bool bcm2835_sdhost_send_command(struct
+
+ while (bcm2835_sdhost_read(host, SDCMD) & SDCMD_NEW_FLAG) {
+ if (timeout == 0) {
+- pr_err("%s: previous command never completed.\n",
++ pr_warn("%s: previous command never completed.\n",
+ mmc_hostname(host->mmc));
+- bcm2835_sdhost_dumpregs(host);
++ if (host->debug)
++ bcm2835_sdhost_dumpregs(host);
+ cmd->error = -EILSEQ;
+ tasklet_schedule(&host->finish_tasklet);
+ return false;
+@@ -1213,10 +1216,12 @@ static void bcm2835_sdhost_finish_comman
+
+ /* Check for errors */
+ if (sdcmd & SDCMD_NEW_FLAG) {
+- pr_err("%s: command never completed.\n",
+- mmc_hostname(host->mmc));
+- bcm2835_sdhost_dumpregs(host);
+- host->cmd->error = -EIO;
++ if (host->debug) {
++ pr_err("%s: command %d never completed.\n",
++ mmc_hostname(host->mmc), host->cmd->opcode);
++ bcm2835_sdhost_dumpregs(host);
++ }
++ host->cmd->error = -EILSEQ;
+ tasklet_schedule(&host->finish_tasklet);
+ return;
+ } else if (sdcmd & SDCMD_FAIL_FLAG) {
+@@ -1238,15 +1243,14 @@ static void bcm2835_sdhost_finish_comman
+ } else {
+ if (sdhsts & SDHSTS_CMD_TIME_OUT) {
+ if (host->debug)
+- pr_err("%s: command %d timeout\n",
++ pr_warn("%s: command %d timeout\n",
+ mmc_hostname(host->mmc),
+ host->cmd->opcode);
+ host->cmd->error = -ETIMEDOUT;
+ } else {
+- pr_err("%s: unexpected command %d error\n",
++ pr_warn("%s: unexpected command %d error\n",
+ mmc_hostname(host->mmc),
+ host->cmd->opcode);
+- bcm2835_sdhost_dumpregs(host);
+ host->cmd->error = -EILSEQ;
+ }
+ tasklet_schedule(&host->finish_tasklet);
+@@ -1370,8 +1374,10 @@ static void bcm2835_sdhost_busy_irq(stru
+ } else if (intmask & SDHSTS_CMD_TIME_OUT)
+ host->cmd->error = -ETIMEDOUT;
+
+- log_dump();
+- bcm2835_sdhost_dumpregs(host);
++ if (host->debug) {
++ log_dump();
++ bcm2835_sdhost_dumpregs(host);
++ }
+ }
+ else
+ bcm2835_sdhost_finish_command(host, NULL);
+@@ -1595,7 +1601,7 @@ void bcm2835_sdhost_set_clock(struct bcm
+ host->overclock_50 = (clock/MHZ);
+
+ if (clock != host->overclock) {
+- pr_warn("%s: overclocking to %dHz\n",
++ pr_info("%s: overclocking to %dHz\n",
+ mmc_hostname(host->mmc), clock);
+ host->overclock = clock;
+ }
+@@ -1605,6 +1611,11 @@ void bcm2835_sdhost_set_clock(struct bcm
+ pr_warn("%s: cancelling overclock\n",
+ mmc_hostname(host->mmc));
+ }
++ } else if (input_clock == 0) {
++ /* Reset the preferred overclock when the clock is stopped.
++ * This always happens during initialisation. */
++ host->overclock_50 = host->user_overclock_50;
++ host->overclock = 0;
+ }
+
+ /* Set the timeout to 500ms */
+@@ -1678,13 +1689,15 @@ static void bcm2835_sdhost_request(struc
+ log_event("REQ<", (u32)mrq, edm);
+ if ((fsm != SDEDM_FSM_IDENTMODE) &&
+ (fsm != SDEDM_FSM_DATAMODE)) {
+- pr_err("%s: previous command (%d) not complete (EDM %x)\n",
+- mmc_hostname(host->mmc),
+- bcm2835_sdhost_read(host, SDCMD) & SDCMD_CMD_MASK,
+- edm);
+ log_event("REQ!", (u32)mrq, edm);
+- log_dump();
+- bcm2835_sdhost_dumpregs(host);
++ if (host->debug) {
++ pr_warn("%s: previous command (%d) not complete (EDM %x)\n",
++ mmc_hostname(host->mmc),
++ bcm2835_sdhost_read(host, SDCMD) & SDCMD_CMD_MASK,
++ edm);
++ log_dump();
++ bcm2835_sdhost_dumpregs(host);
++ }
+ mrq->cmd->error = -EILSEQ;
+ tasklet_schedule(&host->finish_tasklet);
+ mmiowb();
+@@ -1814,16 +1827,19 @@ static void bcm2835_sdhost_tasklet_finis
+ mrq = host->mrq;
+
+ /* Drop the overclock after any data corruption, or after any
+- error overclocked */
++ * error while overclocked. Ignore errors for status commands,
++ * as they are likely when a card is ejected. */
+ if (host->overclock) {
+- if ((mrq->cmd && mrq->cmd->error) ||
++ if ((mrq->cmd && mrq->cmd->error &&
++ (mrq->cmd->opcode != MMC_SEND_STATUS)) ||
+ (mrq->data && mrq->data->error) ||
+- (mrq->stop && mrq->stop->error)) {
++ (mrq->stop && mrq->stop->error) ||
++ (mrq->sbc && mrq->sbc->error)) {
+ host->overclock_50--;
+ pr_warn("%s: reducing overclock due to errors\n",
+ mmc_hostname(host->mmc));
+ host->reset_clock = 1;
+- mrq->cmd->error = -EILSEQ;
++ mrq->cmd->error = -ETIMEDOUT;
+ mrq->cmd->retries = 1;
+ }
+ }
+@@ -1848,6 +1864,21 @@ static void bcm2835_sdhost_tasklet_finis
+ mmc_hostname(host->mmc), err);
+ }
+
++ /* The SDHOST block doesn't report any errors for a disconnected
++ interface. All cards and SDIO devices should report some supported
++ voltage range, so a zero response to SEND_OP_COND, IO_SEND_OP_COND
++ or APP_SEND_OP_COND can be treated as an error. */
++ if (((mrq->cmd->opcode == MMC_SEND_OP_COND) ||
++ (mrq->cmd->opcode == SD_IO_SEND_OP_COND) ||
++ (mrq->cmd->opcode == SD_APP_OP_COND)) &&
++ (mrq->cmd->error == 0) &&
++ (mrq->cmd->resp[0] == 0)) {
++ mrq->cmd->error = -ETIMEDOUT;
++ if (host->debug)
++ pr_info("%s: faking timeout due to zero OCR\n",
++ mmc_hostname(host->mmc));
++ }
++
+ mmc_request_done(host->mmc, mrq);
+ log_event("TSK>", (u32)mrq, 0);
+ }
+@@ -2023,7 +2054,7 @@ static int bcm2835_sdhost_probe(struct p
+ &host->delay_after_stop);
+ of_property_read_u32(node,
+ "brcm,overclock-50",
+- &host->overclock_50);
++ &host->user_overclock_50);
+ of_property_read_u32(node,
+ "brcm,pio-limit",
+ &host->pio_limit);