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>
master
Grazvydas Ignotas 12 years ago committed by Andy Fleming
parent 15ceb1de81
commit 25c719e2c3
  1. 36
      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;

Loading…
Cancel
Save