From da129170600e33bdcea1762ebe4ea0a2c9312a9e Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Sat, 17 Oct 2020 08:36:27 -0400 Subject: mmc: Add some helper functions for retrying on error All of the existing quirks add retries to various calls of mmc_send_cmd. mmc_send_cmd_quirks is a helper function to do this retrying behavior. It checks if quirks mode is enabled, and if a specific quirk is activated it retries on error. This also adds mmc_send_cmd_retry, which retries on error every time (instead of if a quirk is activated). Signed-off-by: Sean Anderson Reviewed-by: Jaehoon Chung Reviewed-by: Jaehoon Chung --- drivers/mmc/mmc.c | 141 ++++++++++++++++++++++-------------------------------- 1 file changed, 58 insertions(+), 83 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index a47700e..dbf3659 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -207,26 +207,65 @@ int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) } #endif +/** + * mmc_send_cmd_retry() - send a command to the mmc device, retrying on error + * + * @dev: device to receive the command + * @cmd: command to send + * @data: additional data to send/receive + * @retries: how many times to retry; mmc_send_cmd is always called at least + * once + * @return 0 if ok, -ve on error + */ +static int mmc_send_cmd_retry(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data, uint retries) +{ + int ret; + + do { + ret = mmc_send_cmd(mmc, cmd, data); + } while (ret && retries--); + + return ret; +} + +/** + * mmc_send_cmd_quirks() - send a command to the mmc device, retrying if a + * specific quirk is enabled + * + * @dev: device to receive the command + * @cmd: command to send + * @data: additional data to send/receive + * @quirk: retry only if this quirk is enabled + * @retries: how many times to retry; mmc_send_cmd is always called at least + * once + * @return 0 if ok, -ve on error + */ +static int mmc_send_cmd_quirks(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data, u32 quirk, uint retries) +{ + if (CONFIG_IS_ENABLED(MMC_QUIRKS) && mmc->quirks & quirk) + return mmc_send_cmd_retry(mmc, cmd, data, retries); + else + return mmc_send_cmd(mmc, cmd, data); +} + int mmc_send_status(struct mmc *mmc, unsigned int *status) { struct mmc_cmd cmd; - int err, retries = 5; + int ret; cmd.cmdidx = MMC_CMD_SEND_STATUS; cmd.resp_type = MMC_RSP_R1; if (!mmc_host_is_spi(mmc)) cmd.cmdarg = mmc->rca << 16; - while (retries--) { - err = mmc_send_cmd(mmc, &cmd, NULL); - if (!err) { - mmc_trace_state(mmc, &cmd); - *status = cmd.response[0]; - return 0; - } - } + ret = mmc_send_cmd_retry(mmc, &cmd, NULL, 4); mmc_trace_state(mmc, &cmd); - return -ECOMM; + if (!ret) + *status = cmd.response[0]; + + return ret; } int mmc_poll_for_busy(struct mmc *mmc, int timeout_ms) @@ -274,7 +313,6 @@ int mmc_poll_for_busy(struct mmc *mmc, int timeout_ms) int mmc_set_blocklen(struct mmc *mmc, int len) { struct mmc_cmd cmd; - int err; if (mmc->ddr_mode) return 0; @@ -283,24 +321,8 @@ int mmc_set_blocklen(struct mmc *mmc, int len) cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = len; - err = mmc_send_cmd(mmc, &cmd, NULL); - -#ifdef CONFIG_MMC_QUIRKS - if (err && (mmc->quirks & MMC_QUIRK_RETRY_SET_BLOCKLEN)) { - int retries = 4; - /* - * It has been seen that SET_BLOCKLEN may fail on the first - * attempt, let's try a few more time - */ - do { - err = mmc_send_cmd(mmc, &cmd, NULL); - if (!err) - break; - } while (retries--); - } -#endif - - return err; + return mmc_send_cmd_quirks(mmc, &cmd, NULL, + MMC_QUIRK_RETRY_SET_BLOCKLEN, 4); } #ifdef MMC_SUPPORTS_TUNING @@ -771,7 +793,6 @@ static int __mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value, int timeout_ms = DEFAULT_CMD6_TIMEOUT_MS; bool is_part_switch = (set == EXT_CSD_CMD_SET_NORMAL) && (index == EXT_CSD_PART_CONF); - int retries = 3; int ret; if (mmc->gen_cmd6_time) @@ -786,10 +807,7 @@ static int __mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value, (index << 16) | (value << 8); - do { - ret = mmc_send_cmd(mmc, &cmd, NULL); - } while (ret && retries-- > 0); - + ret = mmc_send_cmd_retry(mmc, &cmd, NULL, 3); if (ret) return ret; @@ -1287,22 +1305,15 @@ static int sd_get_capabilities(struct mmc *mmc) cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = 0; - timeout = 3; - -retry_scr: data.dest = (char *)scr; data.blocksize = 8; data.blocks = 1; data.flags = MMC_DATA_READ; - err = mmc_send_cmd(mmc, &cmd, &data); - - if (err) { - if (timeout--) - goto retry_scr; + err = mmc_send_cmd_retry(mmc, &cmd, &data, 3); + if (err) return err; - } mmc->scr[0] = __be32_to_cpu(scr[0]); mmc->scr[1] = __be32_to_cpu(scr[1]); @@ -1463,28 +1474,13 @@ static int sd_read_ssr(struct mmc *mmc) struct mmc_cmd cmd; ALLOC_CACHE_ALIGN_BUFFER(uint, ssr, 16); struct mmc_data data; - int timeout = 3; unsigned int au, eo, et, es; cmd.cmdidx = MMC_CMD_APP_CMD; cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = mmc->rca << 16; - err = mmc_send_cmd(mmc, &cmd, NULL); -#ifdef CONFIG_MMC_QUIRKS - if (err && (mmc->quirks & MMC_QUIRK_RETRY_APP_CMD)) { - int retries = 4; - /* - * It has been seen that APP_CMD may fail on the first - * attempt, let's try a few more times - */ - do { - err = mmc_send_cmd(mmc, &cmd, NULL); - if (!err) - break; - } while (retries--); - } -#endif + err = mmc_send_cmd_quirks(mmc, &cmd, NULL, MMC_QUIRK_RETRY_APP_CMD, 4); if (err) return err; @@ -1492,19 +1488,14 @@ static int sd_read_ssr(struct mmc *mmc) cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = 0; -retry_ssr: data.dest = (char *)ssr; data.blocksize = 64; data.blocks = 1; data.flags = MMC_DATA_READ; - err = mmc_send_cmd(mmc, &cmd, &data); - if (err) { - if (timeout--) - goto retry_ssr; - + err = mmc_send_cmd_retry(mmc, &cmd, &data, 3); + if (err) return err; - } for (i = 0; i < 16; i++) ssr[i] = be32_to_cpu(ssr[i]); @@ -2441,23 +2432,7 @@ static int mmc_startup(struct mmc *mmc) cmd.resp_type = MMC_RSP_R2; cmd.cmdarg = 0; - err = mmc_send_cmd(mmc, &cmd, NULL); - -#ifdef CONFIG_MMC_QUIRKS - if (err && (mmc->quirks & MMC_QUIRK_RETRY_SEND_CID)) { - int retries = 4; - /* - * It has been seen that SEND_CID may fail on the first - * attempt, let's try a few more time - */ - do { - err = mmc_send_cmd(mmc, &cmd, NULL); - if (!err) - break; - } while (retries--); - } -#endif - + err = mmc_send_cmd_quirks(mmc, &cmd, NULL, MMC_QUIRK_RETRY_SEND_CID, 4); if (err) return err; -- cgit v1.1 From 263ddfc3454ead3a988adef39b962479adce2b28 Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Tue, 20 Oct 2020 11:04:51 +0800 Subject: mmc: fsl_esdhc: set sysctl register for clock initialization The initial clock setting should be through sysctl register only, while the mmc_set_clock() will call mmc_set_ios() introduce other configurations like bus width, mode, and so on. Signed-off-by: Yangbo Lu Reviewed-by: Jaehoon Chung --- drivers/mmc/fsl_esdhc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 642784e..68130ee 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -715,7 +715,7 @@ static int esdhc_init_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) esdhc_setbits32(®s->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN); /* Set the initial clock speed */ - mmc_set_clock(mmc, 400000, MMC_CLK_ENABLE); + set_sysctl(priv, mmc, 400000); /* Disable the BRR and BWR bits in IRQSTAT */ esdhc_clrbits32(®s->irqstaten, IRQSTATEN_BRR | IRQSTATEN_BWR); -- cgit v1.1 From 8ee802f899efb422cbd5dc51a734d735320e9999 Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Tue, 20 Oct 2020 11:04:52 +0800 Subject: mmc: fsl_esdhc: make sure delay chain locked for HS400 For eMMC HS400 mode, the DLL reset is a required step for mmc rescan. This step has not been documented in reference manual, but the RM will be fixed sooner or later. In previous commit to support eMMC HS400, db8f936 mmc: fsl_esdhc: support eMMC HS400 mode the steps to configure DLL could be found in commit message, 13. Set DLLCFG0[DLL_ENABLE] and DLLCFG0[DLL_FREQ_SEL]. 14. Wait for delay chain to lock. these would be fixed as, 13. Set DLLCFG0[DLL_ENABLE] and DLLCFG0[DLL_FREQ_SEL]. 13.1 Write DLLCFG0[DLL_RESET] to 1 and wait for 1us, then write DLLCFG0[DLL_RESET] 14. Wait for delay chain to lock. This patch is to add the step of DLL reset, and make sure delay chain locked for HS400. Fixes: db8f93672b42 ("mmc: fsl_esdhc: support eMMC HS400 mode") Signed-off-by: Yangbo Lu Reviewed-by: Jaehoon Chung --- drivers/mmc/fsl_esdhc.c | 28 +++++++++++++++++++++++++--- include/fsl_esdhc.h | 4 ++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 68130ee..a18316e 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -70,7 +70,9 @@ struct fsl_esdhc { uint sdtimingctl; /* SD timing control register */ char reserved8[20]; /* reserved */ uint dllcfg0; /* DLL config 0 register */ - char reserved9[680]; /* reserved */ + char reserved9[12]; /* reserved */ + uint dllstat0; /* DLL status 0 register */ + char reserved10[664];/* reserved */ uint esdhcctl; /* eSDHC control register */ }; @@ -617,9 +619,11 @@ static void esdhc_exit_hs400(struct fsl_esdhc_priv *priv) esdhc_tuning_block_enable(priv, false); } -static void esdhc_set_timing(struct fsl_esdhc_priv *priv, enum bus_mode mode) +static int esdhc_set_timing(struct fsl_esdhc_priv *priv, enum bus_mode mode) { struct fsl_esdhc *regs = priv->esdhc_regs; + ulong start; + u32 val; /* Exit HS400 mode before setting any other mode */ if (esdhc_read32(®s->tbctl) & HS400_MODE && @@ -640,17 +644,33 @@ static void esdhc_set_timing(struct fsl_esdhc_priv *priv, enum bus_mode mode) esdhc_setbits32(®s->dllcfg0, DLL_FREQ_SEL); esdhc_setbits32(®s->dllcfg0, DLL_ENABLE); + + esdhc_setbits32(®s->dllcfg0, DLL_RESET); + udelay(1); + esdhc_clrbits32(®s->dllcfg0, DLL_RESET); + + start = get_timer(0); + val = DLL_STS_SLV_LOCK; + while (!(esdhc_read32(®s->dllstat0) & val)) { + if (get_timer(start) > 1000) { + printf("fsl_esdhc: delay chain lock timeout\n"); + return -ETIMEDOUT; + } + } + esdhc_setbits32(®s->tbctl, HS400_WNDW_ADJUST); esdhc_clock_control(priv, false); esdhc_flush_async_fifo(priv); } esdhc_clock_control(priv, true); + return 0; } static int esdhc_set_ios_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) { struct fsl_esdhc *regs = priv->esdhc_regs; + int ret; if (priv->is_sdhc_per_clk) { /* Select to use peripheral clock */ @@ -667,7 +687,9 @@ static int esdhc_set_ios_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) set_sysctl(priv, mmc, mmc->clock); /* Set timing */ - esdhc_set_timing(priv, mmc->selected_mode); + ret = esdhc_set_timing(priv, mmc->selected_mode); + if (ret) + return ret; /* Set the bus width */ esdhc_clrbits32(®s->proctl, PROCTL_DTW_4 | PROCTL_DTW_8); diff --git a/include/fsl_esdhc.h b/include/fsl_esdhc.h index e6f1c75..850a304 100644 --- a/include/fsl_esdhc.h +++ b/include/fsl_esdhc.h @@ -187,8 +187,12 @@ /* DLL config 0 register */ #define DLL_ENABLE 0x80000000 +#define DLL_RESET 0x40000000 #define DLL_FREQ_SEL 0x08000000 +/* DLL status 0 register */ +#define DLL_STS_SLV_LOCK 0x08000000 + #define MAX_TUNING_LOOP 40 #define HOSTVER_VENDOR(x) (((x) >> 8) & 0xff) -- cgit v1.1 From 58896458b70673a1401b60ba415c0158ae9d08cf Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Tue, 17 Nov 2020 07:04:59 +0900 Subject: mmc: display an error number to debug It's useful to know an error number when it's debugging. Signed-off-by: Jaehoon Chung Reviewed-by: Peng Fan --- drivers/mmc/mmc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index dbf3659..54dcfb7 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -2170,7 +2170,7 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps) err = mmc_execute_tuning(mmc, mwt->tuning); if (err) { - pr_debug("tuning failed\n"); + pr_debug("tuning failed : %d\n", err); goto error; } } @@ -2191,7 +2191,7 @@ error: } } - pr_err("unable to select a mode\n"); + pr_err("unable to select a mode : %d\n", err); return -ENOTSUPP; } @@ -2721,7 +2721,7 @@ static int mmc_power_on(struct mmc *mmc) int ret = regulator_set_enable(mmc->vmmc_supply, true); if (ret) { - puts("Error enabling VMMC supply\n"); + printf("Error enabling VMMC supply : %d\n", ret); return ret; } } @@ -2737,7 +2737,7 @@ static int mmc_power_off(struct mmc *mmc) int ret = regulator_set_enable(mmc->vmmc_supply, false); if (ret) { - pr_debug("Error disabling VMMC supply\n"); + pr_debug("Error disabling VMMC supply : %d\n", ret); return ret; } } @@ -2841,7 +2841,7 @@ retry: if (err) { #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) - pr_err("Card did not respond to voltage select!\n"); + pr_err("Card did not respond to voltage select! : %d\n", err); #endif return -EOPNOTSUPP; } -- cgit v1.1 From b3dc016caade00e07d5ae45042c9be42f611d625 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Fri, 6 Nov 2020 20:30:41 +0900 Subject: mmc: check a return value about regulator's always-on Regulator can be set to "always-on". It's not error about enable/disable. It needs to check about its condition. Signed-off-by: Jaehoon Chung Reviewed-by: Peng Fan --- drivers/mmc/mmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 54dcfb7..a6394bc 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -2720,7 +2720,7 @@ static int mmc_power_on(struct mmc *mmc) if (mmc->vmmc_supply) { int ret = regulator_set_enable(mmc->vmmc_supply, true); - if (ret) { + if (ret && ret != -EACCES) { printf("Error enabling VMMC supply : %d\n", ret); return ret; } @@ -2736,7 +2736,7 @@ static int mmc_power_off(struct mmc *mmc) if (mmc->vmmc_supply) { int ret = regulator_set_enable(mmc->vmmc_supply, false); - if (ret) { + if (ret && ret != -EACCES) { pr_debug("Error disabling VMMC supply : %d\n", ret); return ret; } -- cgit v1.1