Skip to content

Commit

Permalink
mmc: dw_mmc: Add a timeout for sending CMD11
Browse files Browse the repository at this point in the history
In the Designware databook's description of the "Voltage Switch Normal
Scenario" it instructs us to set a timer and fail the voltage change
if we don't see the voltage change interrupt within 2ms.  Let's
implement that.  Without implementing this I have often been able to
reproduce a hang while trying to send CMD11 on an rk3288-based board
while constantly ejecting and inserting UHS cards.

Signed-off-by: Doug Anderson <dianders@chromium.org>
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
  • Loading branch information
Doug Anderson authored and Ulf Hansson committed Mar 31, 2015
1 parent 488b8d6 commit 5c93516
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 0 deletions.
26 changes: 26 additions & 0 deletions drivers/mmc/host/dw_mmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1021,6 +1021,15 @@ static void __dw_mci_start_request(struct dw_mci *host,

dw_mci_start_command(host, cmd, cmdflags);

if (cmd->opcode == SD_SWITCH_VOLTAGE) {
/*
* Databook says to fail after 2ms w/ no response; give an
* extra jiffy just in case we're about to roll over.
*/
mod_timer(&host->cmd11_timer,
jiffies + msecs_to_jiffies(2) + 1);
}

if (mrq->stop)
host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
else
Expand Down Expand Up @@ -2159,6 +2168,8 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
/* Check volt switch first, since it can look like an error */
if ((host->state == STATE_SENDING_CMD11) &&
(pending & SDMMC_INT_VOLT_SWITCH)) {
del_timer(&host->cmd11_timer);

mci_writel(host, RINTSTS, SDMMC_INT_VOLT_SWITCH);
pending &= ~SDMMC_INT_VOLT_SWITCH;
dw_mci_cmd_interrupt(host, pending);
Expand Down Expand Up @@ -2572,6 +2583,18 @@ static bool dw_mci_reset(struct dw_mci *host)
return ret;
}

static void dw_mci_cmd11_timer(unsigned long arg)
{
struct dw_mci *host = (struct dw_mci *)arg;

if (host->state != STATE_SENDING_CMD11)
dev_info(host->dev, "Unexpected CMD11 timeout\n");

host->cmd_status = SDMMC_INT_RTO;
set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
tasklet_schedule(&host->tasklet);
}

#ifdef CONFIG_OF
static struct dw_mci_of_quirks {
char *quirk;
Expand Down Expand Up @@ -2746,6 +2769,9 @@ int dw_mci_probe(struct dw_mci *host)
}
}

setup_timer(&host->cmd11_timer,
dw_mci_cmd11_timer, (unsigned long)host);

host->quirks = host->pdata->quirks;

spin_lock_init(&host->lock);
Expand Down
2 changes: 2 additions & 0 deletions include/linux/mmc/dw_mmc.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ struct dw_mci {
int irq;

int sdio_id0;

struct timer_list cmd11_timer;
};

/* DMA ops for Internal/External DMAC interface */
Expand Down

0 comments on commit 5c93516

Please sign in to comment.