mirror of
https://github.com/AsahiLinux/u-boot
synced 2025-02-17 22:49:02 +00:00
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>
This commit is contained in:
parent
15ceb1de81
commit
25c719e2c3
1 changed files with 34 additions and 2 deletions
|
@ -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;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue