diff mbox series

[v2,2/2] mmc: sdhci-of-esdhc: fix clock setting for different controller versions

Message ID 20200108040713.38888-2-yangbo.lu@nxp.com (mailing list archive)
State New, archived
Headers show
Series [v2,1/2] mmc: sdhci-of-esdhc: fix esdhc_reset() for different controller versions | expand

Commit Message

Yangbo Lu Jan. 8, 2020, 4:07 a.m. UTC
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>
---
Changes for v2:
	- Used preferred style for comments.
---
 drivers/mmc/host/sdhci-of-esdhc.c | 128 +++++++++++++++++++++-----------------
 1 file changed, 72 insertions(+), 56 deletions(-)

Comments

Ulf Hansson Jan. 16, 2020, 2:39 p.m. UTC | #1
On Wed, 8 Jan 2020 at 05:08, Yangbo Lu <yangbo.lu@nxp.com> wrote:
>
> 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>

Applied for next, thanks!

Kind regards
Uffe


> ---
> Changes for v2:
>         - Used preferred style for comments.
> ---
>  drivers/mmc/host/sdhci-of-esdhc.c | 128 +++++++++++++++++++++-----------------
>  1 file changed, 72 insertions(+), 56 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
> index 1110f90..6a58cf9 100644
> --- a/drivers/mmc/host/sdhci-of-esdhc.c
> +++ b/drivers/mmc/host/sdhci-of-esdhc.c
> @@ -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)
> --
> 2.7.4
>
diff mbox series

Patch

diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index 1110f90..6a58cf9 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -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)