mmc: sdhci-of-esdhc: fix clock setting for different controller versions
authorYangbo Lu <yangbo.lu@nxp.com>
Wed, 8 Jan 2020 04:07:13 +0000 (12:07 +0800)
committerUlf Hansson <ulf.hansson@linaro.org>
Thu, 16 Jan 2020 10:50:18 +0000 (11:50 +0100)
This patch is to fix clock setting code for different controller
versions. Two of HW changes after vendor version 2.2 are removing
PEREN/HCKEN/IPGEN bits in system control register, and adding SD
clock stable bit in present state register. This patch cleans up
related code too.

Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
Link: https://lore.kernel.org/r/20200108040713.38888-2-yangbo.lu@nxp.com
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
drivers/mmc/host/sdhci-of-esdhc.c

index d23b96e..8c06017 100644 (file)
@@ -562,32 +562,46 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
 
 static void esdhc_clock_enable(struct sdhci_host *host, bool enable)
 {
-       u32 val;
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
        ktime_t timeout;
+       u32 val, clk_en;
+
+       clk_en = ESDHC_CLOCK_SDCLKEN;
+
+       /*
+        * IPGEN/HCKEN/PEREN bits exist on eSDHC whose vendor version
+        * is 2.2 or lower.
+        */
+       if (esdhc->vendor_ver <= VENDOR_V_22)
+               clk_en |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN |
+                          ESDHC_CLOCK_PEREN);
 
        val = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
 
        if (enable)
-               val |= ESDHC_CLOCK_SDCLKEN;
+               val |= clk_en;
        else
-               val &= ~ESDHC_CLOCK_SDCLKEN;
+               val &= ~clk_en;
 
        sdhci_writel(host, val, ESDHC_SYSTEM_CONTROL);
 
-       /* Wait max 20 ms */
+       /*
+        * Wait max 20 ms. If vendor version is 2.2 or lower, do not
+        * wait clock stable bit which does not exist.
+        */
        timeout = ktime_add_ms(ktime_get(), 20);
-       val = ESDHC_CLOCK_STABLE;
-       while  (1) {
+       while (esdhc->vendor_ver > VENDOR_V_22) {
                bool timedout = ktime_after(ktime_get(), timeout);
 
-               if (sdhci_readl(host, ESDHC_PRSSTAT) & val)
+               if (sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)
                        break;
                if (timedout) {
                        pr_err("%s: Internal clock never stabilised.\n",
                                mmc_hostname(host->mmc));
                        break;
                }
-               udelay(10);
+               usleep_range(10, 20);
        }
 }
 
@@ -621,77 +635,97 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
        struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
-       int pre_div = 1;
-       int div = 1;
-       int division;
+       unsigned int pre_div = 1, div = 1;
+       unsigned int clock_fixup = 0;
        ktime_t timeout;
-       long fixup = 0;
        u32 temp;
 
-       host->mmc->actual_clock = 0;
-
        if (clock == 0) {
+               host->mmc->actual_clock = 0;
                esdhc_clock_enable(host, false);
                return;
        }
 
-       /* Workaround to start pre_div at 2 for VNN < VENDOR_V_23 */
+       /* Start pre_div at 2 for vendor version < 2.3. */
        if (esdhc->vendor_ver < VENDOR_V_23)
                pre_div = 2;
 
+       /* Fix clock value. */
        if (host->mmc->card && mmc_card_sd(host->mmc->card) &&
-               esdhc->clk_fixup && host->mmc->ios.timing == MMC_TIMING_LEGACY)
-               fixup = esdhc->clk_fixup->sd_dflt_max_clk;
+           esdhc->clk_fixup && host->mmc->ios.timing == MMC_TIMING_LEGACY)
+               clock_fixup = esdhc->clk_fixup->sd_dflt_max_clk;
        else if (esdhc->clk_fixup)
-               fixup = esdhc->clk_fixup->max_clk[host->mmc->ios.timing];
-
-       if (fixup && clock > fixup)
-               clock = fixup;
+               clock_fixup = esdhc->clk_fixup->max_clk[host->mmc->ios.timing];
 
-       temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
-       temp &= ~(ESDHC_CLOCK_SDCLKEN | ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN |
-                 ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK);
-       sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
+       if (clock_fixup == 0 || clock < clock_fixup)
+               clock_fixup = clock;
 
-       while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
+       /* Calculate pre_div and div. */
+       while (host->max_clk / pre_div / 16 > clock_fixup && pre_div < 256)
                pre_div *= 2;
 
-       while (host->max_clk / pre_div / div > clock && div < 16)
+       while (host->max_clk / pre_div / div > clock_fixup && div < 16)
                div++;
 
+       esdhc->div_ratio = pre_div * div;
+
+       /* Limit clock division for HS400 200MHz clock for quirk. */
        if (esdhc->quirk_limited_clk_division &&
            clock == MMC_HS200_MAX_DTR &&
            (host->mmc->ios.timing == MMC_TIMING_MMC_HS400 ||
             host->flags & SDHCI_HS400_TUNING)) {
-               division = pre_div * div;
-               if (division <= 4) {
+               if (esdhc->div_ratio <= 4) {
                        pre_div = 4;
                        div = 1;
-               } else if (division <= 8) {
+               } else if (esdhc->div_ratio <= 8) {
                        pre_div = 4;
                        div = 2;
-               } else if (division <= 12) {
+               } else if (esdhc->div_ratio <= 12) {
                        pre_div = 4;
                        div = 3;
                } else {
                        pr_warn("%s: using unsupported clock division.\n",
                                mmc_hostname(host->mmc));
                }
+               esdhc->div_ratio = pre_div * div;
        }
 
+       host->mmc->actual_clock = host->max_clk / esdhc->div_ratio;
+
        dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
-               clock, host->max_clk / pre_div / div);
-       host->mmc->actual_clock = host->max_clk / pre_div / div;
-       esdhc->div_ratio = pre_div * div;
+               clock, host->mmc->actual_clock);
+
+       /* Set clock division into register. */
        pre_div >>= 1;
        div--;
 
+       esdhc_clock_enable(host, false);
+
        temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
-       temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
-               | (div << ESDHC_DIVIDER_SHIFT)
-               (pre_div << ESDHC_PREDIV_SHIFT));
+       temp &= ~ESDHC_CLOCK_MASK;
+       temp |= ((div << ESDHC_DIVIDER_SHIFT) |
+               (pre_div << ESDHC_PREDIV_SHIFT));
        sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
 
+       /*
+        * Wait max 20 ms. If vendor version is 2.2 or lower, do not
+        * wait clock stable bit which does not exist.
+        */
+       timeout = ktime_add_ms(ktime_get(), 20);
+       while (esdhc->vendor_ver > VENDOR_V_22) {
+               bool timedout = ktime_after(ktime_get(), timeout);
+
+               if (sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)
+                       break;
+               if (timedout) {
+                       pr_err("%s: Internal clock never stabilised.\n",
+                               mmc_hostname(host->mmc));
+                       break;
+               }
+               usleep_range(10, 20);
+       }
+
+       /* Additional setting for HS400. */
        if (host->mmc->ios.timing == MMC_TIMING_MMC_HS400 &&
            clock == MMC_HS200_MAX_DTR) {
                temp = sdhci_readl(host, ESDHC_TBCTL);
@@ -711,25 +745,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
                esdhc_clock_enable(host, false);
                esdhc_flush_async_fifo(host);
        }
-
-       /* Wait max 20 ms */
-       timeout = ktime_add_ms(ktime_get(), 20);
-       while (1) {
-               bool timedout = ktime_after(ktime_get(), timeout);
-
-               if (sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)
-                       break;
-               if (timedout) {
-                       pr_err("%s: Internal clock never stabilised.\n",
-                               mmc_hostname(host->mmc));
-                       return;
-               }
-               udelay(10);
-       }
-
-       temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
-       temp |= ESDHC_CLOCK_SDCLKEN;
-       sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
+       esdhc_clock_enable(host, false);
 }
 
 static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)