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:
Yangbo Lu 2018-11-23 11:15:34 +08:00 committed by Ulf Hansson
parent cc14eec088
commit 54e08d9a95
2 changed files with 89 additions and 11 deletions

View File

@ -59,9 +59,29 @@
/* Tuning Block Control Register */ /* Tuning Block Control Register */
#define ESDHC_TBCTL 0x120 #define ESDHC_TBCTL 0x120
#define ESDHC_HS400_WNDW_ADJUST 0x00000040
#define ESDHC_HS400_MODE 0x00000010
#define ESDHC_TB_EN 0x00000004 #define ESDHC_TB_EN 0x00000004
#define ESDHC_TBPTR 0x128 #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 */ /* Control Register for DMA transfer */
#define ESDHC_DMA_SYSCTL 0x40c #define ESDHC_DMA_SYSCTL 0x40c
#define ESDHC_PERIPHERAL_CLK_SEL 0x00080000 #define ESDHC_PERIPHERAL_CLK_SEL 0x00080000

View File

@ -592,6 +592,26 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
| (pre_div << ESDHC_PREDIV_SHIFT)); | (pre_div << ESDHC_PREDIV_SHIFT));
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); 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 */ /* Wait max 20 ms */
timeout = ktime_add_ms(ktime_get(), 20); timeout = ktime_add_ms(ktime_get(), 20);
while (!(sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)) { 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); udelay(10);
} }
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
temp |= ESDHC_CLOCK_SDCLKEN; temp |= ESDHC_CLOCK_SDCLKEN;
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); 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; u32 val;
/* Use tuning block for tuning procedure */
esdhc_clock_enable(host, false); esdhc_clock_enable(host, false);
val = sdhci_readl(host, ESDHC_DMA_SYSCTL); val = sdhci_readl(host, ESDHC_DMA_SYSCTL);
val |= ESDHC_FLUSH_ASYNC_FIFO; val |= ESDHC_FLUSH_ASYNC_FIFO;
sdhci_writel(host, val, ESDHC_DMA_SYSCTL); sdhci_writel(host, val, ESDHC_DMA_SYSCTL);
val = sdhci_readl(host, ESDHC_TBCTL); 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); 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) { if (host->tuning_err == -EAGAIN && esdhc->quirk_fixup_tuning) {
/* program TBPTR[TB_WNDW_END_PTR] = 3*DIV_RATIO and /* 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_writel(host, val, ESDHC_TBCTL);
sdhci_execute_tuning(mmc, opcode); 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 #ifdef CONFIG_PM_SLEEP
@ -814,7 +865,7 @@ static const struct sdhci_ops sdhci_esdhc_be_ops = {
.adma_workaround = esdhc_of_adma_workaround, .adma_workaround = esdhc_of_adma_workaround,
.set_bus_width = esdhc_pltfm_set_bus_width, .set_bus_width = esdhc_pltfm_set_bus_width,
.reset = esdhc_reset, .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 = { 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, .adma_workaround = esdhc_of_adma_workaround,
.set_bus_width = esdhc_pltfm_set_bus_width, .set_bus_width = esdhc_pltfm_set_bus_width,
.reset = esdhc_reset, .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 = { 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) static int sdhci_esdhc_probe(struct platform_device *pdev)
{ {
struct sdhci_host *host; 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 = host->mmc_host_ops.start_signal_voltage_switch =
esdhc_signal_voltage_switch; esdhc_signal_voltage_switch;
host->mmc_host_ops.execute_tuning = esdhc_execute_tuning; host->mmc_host_ops.execute_tuning = esdhc_execute_tuning;
host->mmc_host_ops.hs400_prepare_ddr = esdhc_hs400_prepare_ddr;
host->tuning_delay = 1; host->tuning_delay = 1;
esdhc_init(pdev, host); esdhc_init(pdev, host);