]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
mmc: sdhci-of-esdhc: avoid clock glitch when frequency is changing
authoryangbo lu <yangbo.lu@nxp.com>
Mon, 26 Dec 2016 09:46:30 +0000 (17:46 +0800)
committerUlf Hansson <ulf.hansson@linaro.org>
Mon, 13 Feb 2017 12:20:15 +0000 (13:20 +0100)
The eSDHC_PRSSTAT[SDSTB] bit indicates whether the internal card clock is
stable. This bit is for the host driver to poll clock status when changing
the clock frequency. It is recommended to clear eSDHC_SYSCTL[SDCLKEN]
to remove glitch on the card clock when the frequency is changing. This
patch is to disable SDCLKEN bit before changing frequency and enable it
after SDSTB bit is set.

Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
drivers/mmc/host/sdhci-esdhc.h
drivers/mmc/host/sdhci-of-esdhc.c

index 8cd8449c7bc57cee1929e35ebd1a3f084bb9530e..ece8b37e51ddde167b44069e80bdfb5c3105fa19 100644 (file)
  * eSDHC register definition
  */
 
+/* Present State Register */
+#define ESDHC_PRSSTAT                  0x24
+#define ESDHC_CLOCK_STABLE             0x00000008
+
 /* Protocol Control Register */
 #define ESDHC_PROCTL                   0x28
 #define ESDHC_CTRL_4BITBUS             (0x1 << 1)
@@ -43,6 +47,7 @@
 #define ESDHC_CLOCK_MASK               0x0000fff0
 #define ESDHC_PREDIV_SHIFT             8
 #define ESDHC_DIVIDER_SHIFT            4
+#define ESDHC_CLOCK_SDCLKEN            0x00000008
 #define ESDHC_CLOCK_PEREN              0x00000004
 #define ESDHC_CLOCK_HCKEN              0x00000002
 #define ESDHC_CLOCK_IPGEN              0x00000001
index 364f6b87a728366b6e32755aac09f1dddae04847..d3aa67142839b2e36e654febd911d8e9eee129b6 100644 (file)
@@ -431,6 +431,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
        struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
        int pre_div = 1;
        int div = 1;
+       u32 timeout;
        u32 temp;
 
        host->mmc->actual_clock = 0;
@@ -451,8 +452,8 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
        }
 
        temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
-       temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
-               | ESDHC_CLOCK_MASK);
+       temp &= ~(ESDHC_CLOCK_SDCLKEN | ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN |
+                 ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK);
        sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
 
        while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
@@ -472,7 +473,21 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
                | (div << ESDHC_DIVIDER_SHIFT)
                | (pre_div << ESDHC_PREDIV_SHIFT));
        sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
-       mdelay(1);
+
+       /* Wait max 20 ms */
+       timeout = 20;
+       while (!(sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)) {
+               if (timeout == 0) {
+                       pr_err("%s: Internal clock never stabilised.\n",
+                               mmc_hostname(host->mmc));
+                       return;
+               }
+               timeout--;
+               mdelay(1);
+       }
+
+       temp |= ESDHC_CLOCK_SDCLKEN;
+       sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
 }
 
 static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)