diff options
| author | Grazvydas Ignotas <notasas@gmail.com> | 2012-03-19 12:12:06 +0000 | 
|---|---|---|
| committer | Andy Fleming <afleming@freescale.com> | 2012-05-08 18:02:22 -0500 | 
| commit | 25c719e2c3247ef0bab28ee562f978e2ce9211e3 (patch) | |
| tree | 3f943876a3f1355615e5b435bd44a47f87ac4d2d | |
| parent | 15ceb1de81e282426d8988264469508fb5dbd192 (diff) | |
| download | olio-uboot-2014.01-25c719e2c3247ef0bab28ee562f978e2ce9211e3.tar.xz olio-uboot-2014.01-25c719e2c3247ef0bab28ee562f978e2ce9211e3.zip | |
mmc: omap: handle controller errors properly
According to OMAP3 TRM, when the controller reports certain errors,
driver must perform a software reset. This is done by setting a bit
in SYSCTL and waiting it to clear:
- SRC on command timeout (CTO)
- SRD on data errors (DTO, DCRC and DEB)
This fixes a problem seen on OMAP3 pandora board with some cards
that won't work with a message printed multiple times:
  timedout waiting on cmd inhibit to clear
Code loosely based on Linux omap_hsmmc driver.
Signed-off-by: Grazvydas Ignotas <notasas@gmail.com>
Tested-by: Tom Rini <trini@ti.com>
Tested-by: Pali Rohár <pali.rohar@gmail.com>
| -rw-r--r-- | drivers/mmc/omap_hsmmc.c | 36 | 
1 files changed, 34 insertions, 2 deletions
| diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index a6bb5faa3..f2a7a7871 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -33,6 +33,10 @@  #include <asm/arch/mmc_host_def.h>  #include <asm/arch/sys_proto.h> +/* common definitions for all OMAPs */ +#define SYSCTL_SRC	(1 << 25) +#define SYSCTL_SRD	(1 << 26) +  /* If we fail after 1 second wait, something is really bad */  #define MAX_RETRY_MS	1000 @@ -195,6 +199,27 @@ static int mmc_init_setup(struct mmc *mmc)  	return 0;  } +/* + * MMC controller internal finite state machine reset + * + * Used to reset command or data internal state machines, using respectively + * SRC or SRD bit of SYSCTL register + */ +static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit) +{ +	ulong start; + +	mmc_reg_out(&mmc_base->sysctl, bit, bit); + +	start = get_timer(0); +	while ((readl(&mmc_base->sysctl) & bit) != 0) { +		if (get_timer(0) - start > MAX_RETRY_MS) { +			printf("%s: timedout waiting for sysctl %x to clear\n", +				__func__, bit); +			return; +		} +	} +}  static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,  			struct mmc_data *data) @@ -284,9 +309,10 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,  		}  	} while (!mmc_stat); -	if ((mmc_stat & IE_CTO) != 0) +	if ((mmc_stat & IE_CTO) != 0) { +		mmc_reset_controller_fsm(mmc_base, SYSCTL_SRC);  		return TIMEOUT; -	else if ((mmc_stat & ERRI_MASK) != 0) +	} else if ((mmc_stat & ERRI_MASK) != 0)  		return -1;  	if (mmc_stat & CC_MASK) { @@ -337,6 +363,9 @@ static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size)  			}  		} while (mmc_stat == 0); +		if ((mmc_stat & (IE_DTO | IE_DCRC | IE_DEB)) != 0) +			mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD); +  		if ((mmc_stat & ERRI_MASK) != 0)  			return 1; @@ -389,6 +418,9 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf,  			}  		} while (mmc_stat == 0); +		if ((mmc_stat & (IE_DTO | IE_DCRC | IE_DEB)) != 0) +			mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD); +  		if ((mmc_stat & ERRI_MASK) != 0)  			return 1; |