Skip to content
Snippets Groups Projects
Commit 25c719e2 authored by Grazvydas Ignotas's avatar Grazvydas Ignotas Committed by Andy Fleming
Browse files

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: default avatarGrazvydas Ignotas <notasas@gmail.com>
Tested-by: default avatarTom Rini <trini@ti.com>
Tested-by: default avatarPali Rohár <pali.rohar@gmail.com>
parent 15ceb1de
No related branches found
No related tags found
No related merge requests found
...@@ -33,6 +33,10 @@ ...@@ -33,6 +33,10 @@
#include <asm/arch/mmc_host_def.h> #include <asm/arch/mmc_host_def.h>
#include <asm/arch/sys_proto.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 */ /* If we fail after 1 second wait, something is really bad */
#define MAX_RETRY_MS 1000 #define MAX_RETRY_MS 1000
...@@ -195,6 +199,27 @@ static int mmc_init_setup(struct mmc *mmc) ...@@ -195,6 +199,27 @@ static int mmc_init_setup(struct mmc *mmc)
return 0; 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, static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data) struct mmc_data *data)
...@@ -284,9 +309,10 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, ...@@ -284,9 +309,10 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
} }
} while (!mmc_stat); } 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; return TIMEOUT;
else if ((mmc_stat & ERRI_MASK) != 0) } else if ((mmc_stat & ERRI_MASK) != 0)
return -1; return -1;
if (mmc_stat & CC_MASK) { if (mmc_stat & CC_MASK) {
...@@ -337,6 +363,9 @@ static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size) ...@@ -337,6 +363,9 @@ static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size)
} }
} while (mmc_stat == 0); } 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) if ((mmc_stat & ERRI_MASK) != 0)
return 1; return 1;
...@@ -389,6 +418,9 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, ...@@ -389,6 +418,9 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf,
} }
} while (mmc_stat == 0); } 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) if ((mmc_stat & ERRI_MASK) != 0)
return 1; return 1;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment