On 22/11/18 8:34 AM, Yinbo Zhu wrote: > From: Yangbo Lu <yangbo.lu@xxxxxxx> > > 1. Perform the Tuning Process at the HS400 target operating frequency. > Latched the clock division value. > 2. if read transaction, then set the SDTIMNGCTL[FLW_CTL_BG]. > 3. Switch to High Speed mode and then set the card clock frequency to > a value not greater than 52Mhz > 4. Clear TBCTL[TB_EN],tuning block enable bit. > 5. Change to 8 bit DDR Mode > 6. Switch the card to HS400 mode. > 7. Set TBCTL[TB_EN], tuning block enable bit. > 8. Clear SYSCTL[SDCLKEN] > 9. Wait for PRSSTAT[SDSTB] to be set > 10. Change the clock division to latched value.Set TBCTL[HS 400 mode] > and Set SDCLKCTL[CMD_CLK_CTRL] > 11. Set SYSCTL[SDCLKEN] > 12. Wait for PRSSTAT[SDSTB] to be set > 13. Set DLLCFG0[DLL_ENABLE] and DLLCFG0[DLL_FREQ_SEL]. > 14. Wait for delay chain to lock. > 15. Set TBCTL[HS400_WNDW_ADJUST] > 16. Again clear SYSCTL[SDCLKEN] > 17. Wait for PRSSTAT[SDSTB] to be set > 18. Set ESDHCCTL[FAF] > 19. Wait for ESDHCCTL[FAF] to be cleared > 20. Set SYSCTL[SDCLKEN] > 21. Wait for PRSSTAT[SDSTB] to be set. > > Signed-off-by: Yangbo Lu <yangbo.lu@xxxxxxx> > Signed-off-by: Yinbo Zhu <yinbo.zhu@xxxxxxx> >From sdhci point of view, for this patch and patches 3-5: Acked-by: Adrian Hunter <adrian.hunter@xxxxxxxxx> > --- > Change in v5: > rebase this patch > > drivers/mmc/host/sdhci-esdhc.h | 20 +++++++++ > drivers/mmc/host/sdhci-of-esdhc.c | 78 ++++++++++++++++++++++++++++++++----- > 2 files changed, 88 insertions(+), 10 deletions(-) > > diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h > index 3f16d9c..721a635 100644 > --- a/drivers/mmc/host/sdhci-esdhc.h > +++ b/drivers/mmc/host/sdhci-esdhc.h > @@ -59,9 +59,29 @@ > > /* Tuning Block Control Register */ > #define ESDHC_TBCTL 0x120 > +#define ESDHC_HS400_WNDW_ADJUST 0x00000040 > +#define ESDHC_HS400_MODE 0x00000010 > #define ESDHC_TB_EN 0x00000004 > #define ESDHC_TBPTR 0x128 > > +/* SD Clock Control Register */ > +#define ESDHC_SDCLKCTL 0x144 > +#define ESDHC_LPBK_CLK_SEL 0x80000000 > +#define ESDHC_CMD_CLK_CTL 0x00008000 > + > +/* SD Timing Control Register */ > +#define ESDHC_SDTIMNGCTL 0x148 > +#define ESDHC_FLW_CTL_BG 0x00008000 > + > +/* DLL Config 0 Register */ > +#define ESDHC_DLLCFG0 0x160 > +#define ESDHC_DLL_ENABLE 0x80000000 > +#define ESDHC_DLL_FREQ_SEL 0x08000000 > + > +/* DLL Status 0 Register */ > +#define ESDHC_DLLSTAT0 0x170 > +#define ESDHC_DLL_STS_SLV_LOCK 0x08000000 > + > /* Control Register for DMA transfer */ > #define ESDHC_DMA_SYSCTL 0x40c > #define ESDHC_PERIPHERAL_CLK_SEL 0x00080000 > diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c > index 86fc9f0..65419cb 100644 > --- a/drivers/mmc/host/sdhci-of-esdhc.c > +++ b/drivers/mmc/host/sdhci-of-esdhc.c > @@ -592,6 +592,26 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) > | (pre_div << ESDHC_PREDIV_SHIFT)); > sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); > > + if (host->mmc->ios.timing == MMC_TIMING_MMC_HS400 && > + clock == MMC_HS200_MAX_DTR) { > + temp = sdhci_readl(host, ESDHC_TBCTL); > + sdhci_writel(host, temp | ESDHC_HS400_MODE, ESDHC_TBCTL); > + temp = sdhci_readl(host, ESDHC_SDCLKCTL); > + sdhci_writel(host, temp | ESDHC_CMD_CLK_CTL, ESDHC_SDCLKCTL); > + esdhc_clock_enable(host, true); > + > + temp = sdhci_readl(host, ESDHC_DLLCFG0); > + temp |= ESDHC_DLL_ENABLE | ESDHC_DLL_FREQ_SEL; > + sdhci_writel(host, temp, ESDHC_DLLCFG0); > + temp = sdhci_readl(host, ESDHC_TBCTL); > + sdhci_writel(host, temp | ESDHC_HS400_WNDW_ADJUST, ESDHC_TBCTL); > + > + esdhc_clock_enable(host, false); > + temp = sdhci_readl(host, ESDHC_DMA_SYSCTL); > + temp |= ESDHC_FLUSH_ASYNC_FIFO; > + sdhci_writel(host, temp, ESDHC_DMA_SYSCTL); > + } > + > /* Wait max 20 ms */ > timeout = ktime_add_ms(ktime_get(), 20); > while (!(sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)) { > @@ -603,6 +623,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) > udelay(10); > } > > + temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); > temp |= ESDHC_CLOCK_SDCLKEN; > sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); > } > @@ -728,25 +749,46 @@ static int esdhc_signal_voltage_switch(struct mmc_host *mmc, > { }, > }; > > -static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) > +static void esdhc_tuning_block_enable(struct sdhci_host *host, bool enable) > { > - 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); > u32 val; > > - /* Use tuning block for tuning procedure */ > esdhc_clock_enable(host, false); > + > val = sdhci_readl(host, ESDHC_DMA_SYSCTL); > val |= ESDHC_FLUSH_ASYNC_FIFO; > sdhci_writel(host, val, ESDHC_DMA_SYSCTL); > > val = sdhci_readl(host, ESDHC_TBCTL); > - val |= ESDHC_TB_EN; > + if (enable) > + val |= ESDHC_TB_EN; > + else > + val &= ~ESDHC_TB_EN; > sdhci_writel(host, val, ESDHC_TBCTL); > + > esdhc_clock_enable(host, true); > +} > + > +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; > + > + esdhc_tuning_block_enable(host, true); > + > + 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); > + } > > - sdhci_execute_tuning(mmc, opcode); > if (host->tuning_err == -EAGAIN && esdhc->quirk_fixup_tuning) { > > /* program TBPTR[TB_WNDW_END_PTR] = 3*DIV_RATIO and > @@ -765,7 +807,16 @@ static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) > sdhci_writel(host, val, ESDHC_TBCTL); > sdhci_execute_tuning(mmc, opcode); > } > - return 0; > + return ret; > +} > + > +static void esdhc_set_uhs_signaling(struct sdhci_host *host, > + unsigned int timing) > +{ > + if (timing == MMC_TIMING_MMC_HS400) > + esdhc_tuning_block_enable(host, true); > + else > + sdhci_set_uhs_signaling(host, timing); > } > > #ifdef CONFIG_PM_SLEEP > @@ -814,7 +865,7 @@ static SIMPLE_DEV_PM_OPS(esdhc_of_dev_pm_ops, > .adma_workaround = esdhc_of_adma_workaround, > .set_bus_width = esdhc_pltfm_set_bus_width, > .reset = esdhc_reset, > - .set_uhs_signaling = sdhci_set_uhs_signaling, > + .set_uhs_signaling = esdhc_set_uhs_signaling, > }; > > static const struct sdhci_ops sdhci_esdhc_le_ops = { > @@ -831,7 +882,7 @@ static SIMPLE_DEV_PM_OPS(esdhc_of_dev_pm_ops, > .adma_workaround = esdhc_of_adma_workaround, > .set_bus_width = esdhc_pltfm_set_bus_width, > .reset = esdhc_reset, > - .set_uhs_signaling = sdhci_set_uhs_signaling, > + .set_uhs_signaling = esdhc_set_uhs_signaling, > }; > > static const struct sdhci_pltfm_data sdhci_esdhc_be_pdata = { > @@ -909,6 +960,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; > @@ -932,6 +989,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); >