summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mmc/omap_hsmmc.c36
1 files changed, 34 insertions, 2 deletions
diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c
index a6bb5fa..f2a7a78 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;