Skip to content

Commit

Permalink
mmc: sdhci-of-esdhc: add hs400 mode support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Yangbo Lu authored and Ulf Hansson committed Dec 17, 2018
1 parent cc14eec commit 54e08d9
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 10 deletions.
20 changes: 20 additions & 0 deletions drivers/mmc/host/sdhci-esdhc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
78 changes: 68 additions & 10 deletions drivers/mmc/host/sdhci-of-esdhc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
}

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
Expand All @@ -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
Expand Down Expand Up @@ -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 = {
Expand All @@ -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 = {
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down

0 comments on commit 54e08d9

Please sign in to comment.