diff options
author | Ye.Li <B37916@freescale.com> | 2014-01-26 18:05:57 +0800 |
---|---|---|
committer | Ye.Li <B37916@freescale.com> | 2014-01-27 18:22:36 +0800 |
commit | 86817422ce90ea2106ba9cce7c8ce1624ea286a0 (patch) | |
tree | e3a752876c34e484ed3a8e18b101647999d8d95e | |
parent | bc1148f504898d851e5bfdbdc7dcde754d94adba (diff) | |
download | u-boot-imx-86817422ce90ea2106ba9cce7c8ce1624ea286a0.zip u-boot-imx-86817422ce90ea2106ba9cce7c8ce1624ea286a0.tar.gz u-boot-imx-86817422ce90ea2106ba9cce7c8ce1624ea286a0.tar.bz2 |
ENGR00293946 SD:ESDHC/USDHC Fix command timeout after UHS-I tunning error
Got intermittent boot failure of android image from UHS-I SD card. The
MBR read is failed.
When doing the UHS-I tunning of sampling point, ESDHC may get some command
errors (CIE, CEBE or CCE) at incorrect sampling point. In this case,
current driver will return from the "esdhc_send_cmd" function immediately
with having host reset, which also gates off the SD clock automatically.
But the SD card remains in the status of sending tuning block data and
waits the clock. When the next new command is sent from host, the clock
enables the SD card to sending the tuning block and does not respond to
the new command.
Fix this issue by adding a delay before host reset. So the SD card can
output tunning block and turn to "tran" status. The tunning execute bit
"EXE_TUNE" also be cleaned after each tunning.
Signed-off-by: Ye.Li <B37916@freescale.com>
(cherry picked from commit f68b8cbdedba855b87a11d525afe7c50fc4d7b0e)
-rw-r--r-- | drivers/mmc/imx_esdhc.c | 41 |
1 files changed, 36 insertions, 5 deletions
diff --git a/drivers/mmc/imx_esdhc.c b/drivers/mmc/imx_esdhc.c index b242755..fe1fb2f 100644 --- a/drivers/mmc/imx_esdhc.c +++ b/drivers/mmc/imx_esdhc.c @@ -2,7 +2,7 @@ * Copyright 2007, Freescale Semiconductor, Inc * Andy Fleming * - * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. + * Copyright (C) 2008-2014 Freescale Semiconductor, Inc. * * Based vaguely on the pxa mmc code: * (C) Copyright 2003 @@ -233,6 +233,12 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) /* Reset CMD and DATA portions on error */ if (irqstat & (CMD_ERR | IRQSTAT_CTOE)) { + + if (SD_CMD_TUNING == cmd->cmdidx) { + /*add delay to output tunning block from card*/ + udelay(50); + } + writel(readl(®s->sysctl) | SYSCTL_RSTC, ®s->sysctl); while (readl(®s->sysctl) & SYSCTL_RSTC) ; @@ -252,6 +258,14 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) if (cmd->cmdidx == SD_CMD_SWITCH_UHS18V) printf("CMD11 to switch to 1.8V mode failed." "Card requires power cycle\n"); + + /* Clear the tune execute bit*/ + mixctrl = readl(®s->mixctrl); + if (mixctrl & USDHC_MIXCTRL_EXE_TUNE) { + mixctrl &= ~USDHC_MIXCTRL_EXE_TUNE; + writel(mixctrl, ®s->mixctrl); + } + } if (irqstat & CMD_ERR) @@ -328,8 +342,12 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) tmp_ptr = (u32 *)data->dest; for (i = 0; i < (block_cnt); ++i) { - while (!(readl(®s->irqstat) & IRQSTAT_BRR)) - ; + while (!(readl(®s->irqstat) & IRQSTAT_BRR)) { + /*Check data error to avoid dead loop*/ + if (readl(®s->irqstat) & DATA_ERR) { + goto send_end; + } + } for (j = 0; j < (block_size >> 2); ++j, ++tmp_ptr) { *tmp_ptr = readl(®s->datport); @@ -342,8 +360,12 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) tmp_ptr = (u32 *)data->src; for (i = 0; i < (block_cnt); ++i) { - while (!(readl(®s->irqstat) & IRQSTAT_BWR)) - ; + while (!(readl(®s->irqstat) & IRQSTAT_BWR)) { + /*Check timeout error to avoid dead loop*/ + if (readl(®s->irqstat) & IRQSTAT_DTOE) { + goto send_end; + } + } for (j = 0; j < (block_size >> 2); ++j, ++tmp_ptr) { writel(*tmp_ptr, ®s->datport); @@ -357,6 +379,15 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) while (!(readl(®s->irqstat) & IRQSTAT_TC)) ; } +send_end: + + /* Clear the tune execute bit*/ + mixctrl = readl(®s->mixctrl); + if (mixctrl & USDHC_MIXCTRL_EXE_TUNE) { + mixctrl &= ~USDHC_MIXCTRL_EXE_TUNE; + writel(mixctrl, ®s->mixctrl); + } + /* Reset CMD and DATA portions of the controller on error */ if (readl(®s->irqstat) & 0xFFFF0000) { writel(readl(®s->sysctl) | SYSCTL_RSTC | SYSCTL_RSTD, |