diff options
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.patch | 312 |
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); |