diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 38615e032c99..866a2682a8ec 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -161,6 +161,7 @@ /* CQHCI vendor specific registers */ #define CQHCI_VENDOR_CFG1 0xA00 #define CQHCI_VENDOR_DIS_RST_ON_CQ_EN (0x3 << 13) +#define RCLK_TOGGLE BIT(2) struct sdhci_msm_offset { u32 core_hc_mode; @@ -1059,6 +1060,40 @@ static void msm_hc_select_default(struct sdhci_host *host) wmb(); } +/* + * After MCLK ugating, toggle the FIFO write clock to get + * the FIFO pointers and flags to valid state. + */ +static void sdhci_msm_toggle_fifo_write_clk(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + const struct sdhci_msm_offset *msm_host_offset = msm_host->offset; + struct mmc_ios ios = host->mmc->ios; + u32 config; + + if ((msm_host->tuning_done || ios.enhanced_strobe) && + (host->mmc->ios.timing == MMC_TIMING_MMC_HS400)) { + /* + * set HC_REG_DLL_CONFIG_3[1] to select MCLK as + * DLL input clock + */ + config = readl_relaxed(host->ioaddr + msm_host_offset->core_dll_config_3); + config |= RCLK_TOGGLE; + writel_relaxed(config, host->ioaddr + msm_host_offset->core_dll_config_3); + /* ensure above write as toggling same bit quickly */ + wmb(); + udelay(2); + /* + * clear HC_REG_DLL_CONFIG_3[1] to select RCLK as + * DLL input clock + */ + config = readl_relaxed(host->ioaddr + msm_host_offset->core_dll_config_3); + config &= ~RCLK_TOGGLE; + writel_relaxed(config, host->ioaddr + msm_host_offset->core_dll_config_3); + } +} + static void msm_hc_select_hs400(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -4451,6 +4486,7 @@ static __maybe_unused int sdhci_msm_runtime_resume(struct device *dev) } sdhci_msm_registers_restore(host); + sdhci_msm_toggle_fifo_write_clk(host); /* * Whenever core-clock is gated dynamically, it's needed to * restore the SDR DLL settings when the clock is ungated.