aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorDong Aisheng <b29396@freescale.com>2013-09-13 07:11:33 -0400
committerChris Ball <cjb@laptop.org>2013-09-26 07:57:21 -0400
commitfed2f6e2d42e4bcdec5ea357e7a9db8602744753 (patch)
tree8477f64b02abcccfe5684ff26f645531bce790e8 /drivers/mmc
parentd31fc00a71b4f3a52d23c5a995ccd63e50a2c877 (diff)
mmc: sdhci-esdhc-imx: support real clock on and off for imx6q
The signal voltage switch flow requires to shutdown and output clock in a specific sequence according to standard host controller v3.0 spec. In that timing, the card must really receive clock or not. However, for i.MX6Q, the uSDHC will not output clock even the clock is enabled until there is command or data in transfer on the bus, which will then cause singal voltage switch always to fail. For i.MX6Q, we clear ESDHC_VENDOR_SPEC_FRC_SDCLK_ON bit to let controller to gate off clock automatically and set that bit to force clock output if clock is on. This is required by SD3.0 support. Signed-off-by: Dong Aisheng <b29396@freescale.com> Acked-by: Shawn Guo <shawn.guo@linaro.org> Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c19
1 files changed, 17 insertions, 2 deletions
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 67eaec92e333..37fafd7df964 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -34,6 +34,7 @@
34/* VENDOR SPEC register */ 34/* VENDOR SPEC register */
35#define ESDHC_VENDOR_SPEC 0xc0 35#define ESDHC_VENDOR_SPEC 0xc0
36#define ESDHC_VENDOR_SPEC_SDIO_QUIRK (1 << 1) 36#define ESDHC_VENDOR_SPEC_SDIO_QUIRK (1 << 1)
37#define ESDHC_VENDOR_SPEC_FRC_SDCLK_ON (1 << 8)
37#define ESDHC_WTMK_LVL 0x44 38#define ESDHC_WTMK_LVL 0x44
38#define ESDHC_MIX_CTRL 0x48 39#define ESDHC_MIX_CTRL 0x48
39#define ESDHC_MIX_CTRL_AC23EN (1 << 7) 40#define ESDHC_MIX_CTRL_AC23EN (1 << 7)
@@ -409,13 +410,20 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
409 unsigned int clock) 410 unsigned int clock)
410{ 411{
411 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 412 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
413 struct pltfm_imx_data *imx_data = pltfm_host->priv;
412 unsigned int host_clock = clk_get_rate(pltfm_host->clk); 414 unsigned int host_clock = clk_get_rate(pltfm_host->clk);
413 int pre_div = 2; 415 int pre_div = 2;
414 int div = 1; 416 int div = 1;
415 u32 temp; 417 u32 temp, val;
416 418
417 if (clock == 0) 419 if (clock == 0) {
420 if (is_imx6q_usdhc(imx_data)) {
421 val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
422 writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
423 host->ioaddr + ESDHC_VENDOR_SPEC);
424 }
418 goto out; 425 goto out;
426 }
419 427
420 temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); 428 temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
421 temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN 429 temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
@@ -439,6 +447,13 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
439 | (div << ESDHC_DIVIDER_SHIFT) 447 | (div << ESDHC_DIVIDER_SHIFT)
440 | (pre_div << ESDHC_PREDIV_SHIFT)); 448 | (pre_div << ESDHC_PREDIV_SHIFT));
441 sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); 449 sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
450
451 if (is_imx6q_usdhc(imx_data)) {
452 val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
453 writel(val | ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
454 host->ioaddr + ESDHC_VENDOR_SPEC);
455 }
456
442 mdelay(1); 457 mdelay(1);
443out: 458out:
444 host->clock = clock; 459 host->clock = clock;