summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHaibo Chen <haibo.chen@nxp.com>2022-10-28 17:04:29 +0800
committerUlf Hansson <ulf.hansson@linaro.org>2022-12-07 13:22:33 +0100
commit4fb2786961c517d0a6e4e8bf947d6f7259155d6a (patch)
tree2324283e46f3d7da1c56827941761031bb4de6c4
parent08b863bb034cfda73ac2d37faab9d54185f04231 (diff)
downloadlinux-4fb2786961c517d0a6e4e8bf947d6f7259155d6a.tar.bz2
mmc: sdhci-esdhc-imx: reset the tuning logic before execute tuning
For standard tuning method on usdhc, the previous tuning result can impact current tuning result, let current tuning can't set the correct delay cell. And from the logic, this is also reasonable for manual tuning method. So reset the tuning logic before execute tuning. To avoid compile issue, this patch also move the esdhc_reset_tuning() upper. Find this issue when support SDIO WiFi in band wakeup feature. After system resume back, will do re-tuning, but then meet data CRC error. Do not meet this issue on SD/eMMC, because we already call esdhc_reset_tuning() when config the legency ios, and SD/eMMC need to re-init when system resume back, but SDIO device don't do re-init if it has MMC_PM_KEEP_POWER pm_flags. Signed-off-by: Haibo Chen <haibo.chen@nxp.com> Acked-by: Adrian Hunter <adrian.hunter@intel.com> Link: https://lore.kernel.org/r/1666947869-7904-1-git-send-email-haibo.chen@nxp.com Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c82
1 files changed, 44 insertions, 38 deletions
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 9f7eea4f948f..89ef0c80ac37 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -1013,6 +1013,44 @@ static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
SDHCI_HOST_CONTROL);
}
+static void esdhc_reset_tuning(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
+ u32 ctrl;
+ int ret;
+
+ /* Reset the tuning circuit */
+ if (esdhc_is_usdhc(imx_data)) {
+ if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
+ ctrl = readl(host->ioaddr + ESDHC_MIX_CTRL);
+ ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
+ ctrl &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
+ writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
+ writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
+ } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
+ ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
+ ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
+ ctrl &= ~ESDHC_MIX_CTRL_EXE_TUNE;
+ writel(ctrl, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
+ /* Make sure ESDHC_MIX_CTRL_EXE_TUNE cleared */
+ ret = readl_poll_timeout(host->ioaddr + SDHCI_AUTO_CMD_STATUS,
+ ctrl, !(ctrl & ESDHC_MIX_CTRL_EXE_TUNE), 1, 50);
+ if (ret == -ETIMEDOUT)
+ dev_warn(mmc_dev(host->mmc),
+ "Warning! clear execute tuning bit failed\n");
+ /*
+ * SDHCI_INT_DATA_AVAIL is W1C bit, set this bit will clear the
+ * usdhc IP internal logic flag execute_tuning_with_clr_buf, which
+ * will finally make sure the normal data transfer logic correct.
+ */
+ ctrl = readl(host->ioaddr + SDHCI_INT_STATUS);
+ ctrl |= SDHCI_INT_DATA_AVAIL;
+ writel(ctrl, host->ioaddr + SDHCI_INT_STATUS);
+ }
+ }
+}
+
static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
struct sdhci_host *host = mmc_priv(mmc);
@@ -1024,6 +1062,12 @@ static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
if (host->timing == MMC_TIMING_UHS_DDR50)
return 0;
+ /*
+ * Reset tuning circuit logic. If not, the previous tuning result
+ * will impact current tuning, make current tuning can't set the
+ * correct delay cell.
+ */
+ esdhc_reset_tuning(host);
return sdhci_execute_tuning(mmc, opcode);
}
@@ -1197,44 +1241,6 @@ static void esdhc_set_strobe_dll(struct sdhci_host *host)
"warning! HS400 strobe DLL status REF/SLV not lock in 50us, STROBE DLL status is %x!\n", v);
}
-static void esdhc_reset_tuning(struct sdhci_host *host)
-{
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
- u32 ctrl;
- int ret;
-
- /* Reset the tuning circuit */
- if (esdhc_is_usdhc(imx_data)) {
- if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
- ctrl = readl(host->ioaddr + ESDHC_MIX_CTRL);
- ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
- ctrl &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
- writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
- writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
- } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
- ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
- ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
- ctrl &= ~ESDHC_MIX_CTRL_EXE_TUNE;
- writel(ctrl, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
- /* Make sure ESDHC_MIX_CTRL_EXE_TUNE cleared */
- ret = readl_poll_timeout(host->ioaddr + SDHCI_AUTO_CMD_STATUS,
- ctrl, !(ctrl & ESDHC_MIX_CTRL_EXE_TUNE), 1, 50);
- if (ret == -ETIMEDOUT)
- dev_warn(mmc_dev(host->mmc),
- "Warning! clear execute tuning bit failed\n");
- /*
- * SDHCI_INT_DATA_AVAIL is W1C bit, set this bit will clear the
- * usdhc IP internal logic flag execute_tuning_with_clr_buf, which
- * will finally make sure the normal data transfer logic correct.
- */
- ctrl = readl(host->ioaddr + SDHCI_INT_STATUS);
- ctrl |= SDHCI_INT_DATA_AVAIL;
- writel(ctrl, host->ioaddr + SDHCI_INT_STATUS);
- }
- }
-}
-
static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
{
u32 m;