123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- From 3b99e3ac668642719abd928743e1fce9fe1c24c9 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);
|