From b1e725f2c6e2d8f9855a0b4cba508a2abe748b4d Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Mon, 19 Mar 2012 03:50:53 +0000 Subject: mmc: omap: follow TRM procedure to power on cards According to OMAP3 TRM, PBIASLITEPWRDNZ bits must be cleared while MMC power supply is being enabled and is ramping up (those bits might be left set by the previous bootloader). It doesn't say what happens if this procedure is violated, but better not to risk here and do things as required. Signed-off-by: Grazvydas Ignotas Acked-by: Tom Rini --- drivers/mmc/omap_hsmmc.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'drivers/mmc/omap_hsmmc.c') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 2400db2f35..2b12929576 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -62,15 +62,21 @@ static void omap4_vmmc_pbias_config(struct mmc *mmc) unsigned char mmc_board_init(struct mmc *mmc) { -#if defined(CONFIG_TWL4030_POWER) - twl4030_power_mmc_init(); -#endif - #if defined(CONFIG_OMAP34XX) t2_t *t2_base = (t2_t *)T2_BASE; struct prcm *prcm_base = (struct prcm *)PRCM_BASE; + u32 pbias_lite; - writel(readl(&t2_base->pbias_lite) | PBIASLITEPWRDNZ1 | + pbias_lite = readl(&t2_base->pbias_lite); + pbias_lite &= ~(PBIASLITEPWRDNZ1 | PBIASLITEPWRDNZ0); + writel(pbias_lite, &t2_base->pbias_lite); +#endif +#if defined(CONFIG_TWL4030_POWER) + twl4030_power_mmc_init(); + mdelay(100); /* ramp-up delay from Linux code */ +#endif +#if defined(CONFIG_OMAP34XX) + writel(pbias_lite | PBIASLITEPWRDNZ1 | PBIASSPEEDCTRL0 | PBIASLITEPWRDNZ0, &t2_base->pbias_lite); -- cgit v1.2.3 From 15ceb1de81e282426d8988264469508fb5dbd192 Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Mon, 19 Mar 2012 12:11:43 +0000 Subject: mmc: omap: improve stat wait message The message didn't state that it's waiting for STAT to _clear_, and printing the STAT value itself can help to identify problems. Signed-off-by: Grazvydas Ignotas Acked-by: Tom Rini --- drivers/mmc/omap_hsmmc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mmc/omap_hsmmc.c') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 2b12929576..a6bb5faa3a 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -215,7 +215,8 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, start = get_timer(0); while (readl(&mmc_base->stat)) { if (get_timer(0) - start > MAX_RETRY_MS) { - printf("%s: timedout waiting for stat!\n", __func__); + printf("%s: timedout waiting for STAT (%x) to clear\n", + __func__, readl(&mmc_base->stat)); return TIMEOUT; } } -- cgit v1.2.3 From 25c719e2c3247ef0bab28ee562f978e2ce9211e3 Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Mon, 19 Mar 2012 12:12:06 +0000 Subject: mmc: omap: handle controller errors properly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Tested-by: Tom Rini Tested-by: Pali Rohár --- drivers/mmc/omap_hsmmc.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) (limited to 'drivers/mmc/omap_hsmmc.c') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index a6bb5faa3a..f2a7a78716 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -33,6 +33,10 @@ #include #include +/* 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; -- cgit v1.2.3