mirror of https://gitee.com/openkylin/linux.git
mmc: sdhci-of-esdhc: add hs400 mode support
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> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
parent
cc14eec088
commit
54e08d9a95
|
@ -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
|
||||
|
|
|
@ -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 struct soc_device_attribute soc_fixup_tuning[] = {
|
|||
{ },
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
sdhci_execute_tuning(mmc, opcode);
|
||||
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);
|
||||
}
|
||||
|
||||
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 const struct sdhci_ops sdhci_esdhc_be_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 const struct sdhci_ops sdhci_esdhc_le_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_hs400_prepare_ddr(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.hs400_prepare_ddr = esdhc_hs400_prepare_ddr;
|
||||
host->tuning_delay = 1;
|
||||
|
||||
esdhc_init(pdev, host);
|
||||
|
|
Loading…
Reference in New Issue