Message ID | 20181122063448.12351-2-yinbo.zhu@nxp.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [v5,1/5] mmc: mmc: add hs400_prepare_ddr callback | expand |
On 22/11/18 8:34 AM, Yinbo Zhu wrote: > From: Yangbo Lu <yangbo.lu@nxp.com> > > 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@nxp.com> > Signed-off-by: Yinbo Zhu <yinbo.zhu@nxp.com> From sdhci point of view, for this patch and patches 3-5: Acked-by: Adrian Hunter <adrian.hunter@intel.com> > --- > 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); >
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);