From: Yangbo Lu <yangbo.lu@xxxxxxx> 1. Set SDTIMNGCTL[FLW_CTL_BG] just after HS400 tuning. 2. Clear TBCTL[TB_EN] before switching from DDR to HS400. 3. Re-set TBCTL[TB_EN] when switching to HS400. 4. Remove useless controller DDR timing setting for HS400. 5. add prepare_ddr_to_hs400 callback. 6. improve tuning block enabling/disabling. Signed-off-by: Yangbo Lu <yangbo.lu@xxxxxxx> Signed-off-by: Yinbo Zhu <yinbo.zhu@xxxxxxx> --- drivers/mmc/core/mmc.c | 3 +++ drivers/mmc/host/sdhci-of-esdhc.c | 32 +++++++++++++++++++++++--------- include/linux/mmc/host.h | 1 + 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 4f4a627..a5a3b3b 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1148,6 +1148,9 @@ static int mmc_select_hs400(struct mmc_card *card) goto out_err; /* Switch card to DDR */ + if (host->ops->prepare_ddr_to_hs400) + host->ops->prepare_ddr_to_hs400(host); + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, EXT_CSD_DDR_BUS_WIDTH_8, diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index efae07c..dea6611 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -738,6 +738,7 @@ static void esdhc_tuning_block_enable(struct sdhci_host *host, bool enable) u32 val; esdhc_clock_enable(host, false); + val = sdhci_readl(host, ESDHC_DMA_SYSCTL); val |= ESDHC_FLUSH_ASYNC_FIFO; sdhci_writel(host, val, ESDHC_DMA_SYSCTL); @@ -748,6 +749,7 @@ static void esdhc_tuning_block_enable(struct sdhci_host *host, bool enable) else val &= ~ESDHC_TB_EN; sdhci_writel(host, val, ESDHC_TBCTL); + esdhc_clock_enable(host, true); } @@ -756,13 +758,26 @@ static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) struct sdhci_host *host = mmc_priv(mmc); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host); + bool hs400_tuning; + u32 val; + int ret; if (esdhc->quirk_limited_clk_division && host->flags & SDHCI_HS400_TUNING) esdhc_of_set_clock(host, host->clock); esdhc_tuning_block_enable(host, true); - return sdhci_execute_tuning(mmc, opcode); + + hs400_tuning = host->flags & SDHCI_HS400_TUNING; + ret = sdhci_execute_tuning(mmc, opcode); + + if (hs400_tuning) { + val = sdhci_readl(host, ESDHC_SDTIMNGCTL); + val |= ESDHC_FLW_CTL_BG; + sdhci_writel(host, val, ESDHC_SDTIMNGCTL); + } + + return ret; } static void esdhc_set_ddr_signaling(struct sdhci_host *host) @@ -791,18 +806,10 @@ static void esdhc_set_ddr_signaling(struct sdhci_host *host) static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) { - u32 val; - if (timing == MMC_TIMING_MMC_DDR52 || timing == MMC_TIMING_UHS_DDR50) { esdhc_set_ddr_signaling(host); } else if (timing == MMC_TIMING_MMC_HS400) { - val = sdhci_readl(host, ESDHC_SDTIMNGCTL); - val |= ESDHC_FLW_CTL_BG; - sdhci_writel(host, val, ESDHC_SDTIMNGCTL); - - esdhc_tuning_block_enable(host, false); - esdhc_set_ddr_signaling(host); esdhc_tuning_block_enable(host, true); } else { sdhci_set_uhs_signaling(host, timing); @@ -963,6 +970,12 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host) } } +static int esdhc_prepare_ddr_to_hs400(struct mmc_host *mmc) +{ + esdhc_tuning_block_enable(mmc_priv(mmc), false); + return 0; +} + static int sdhci_esdhc_probe(struct platform_device *pdev) { struct sdhci_host *host; @@ -986,6 +999,7 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) host->mmc_host_ops.start_signal_voltage_switch = esdhc_signal_voltage_switch; host->mmc_host_ops.execute_tuning = esdhc_execute_tuning; + host->mmc_host_ops.prepare_ddr_to_hs400 = esdhc_prepare_ddr_to_hs400; host->tuning_delay = 1; esdhc_init(pdev, host); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 0b24394..c97ffd6 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -146,6 +146,7 @@ struct mmc_host_ops { /* Prepare HS400 target operating frequency depending host driver */ int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios); + int (*prepare_ddr_to_hs400)(struct mmc_host *host); /* Prepare enhanced strobe depending host driver */ void (*hs400_enhanced_strobe)(struct mmc_host *host, struct mmc_ios *ios); -- 1.7.1