Linux-mmc Archive on lore.kernel.org
 help / color / Atom feed
* [v2, 1/2] mmc: sdhci-of-esdhc: fix esdhc_reset() for different controller versions
@ 2020-01-08  4:07 Yangbo Lu
  2020-01-08  4:07 ` [v2, 2/2] mmc: sdhci-of-esdhc: fix clock setting " Yangbo Lu
  2020-01-16 14:39 ` [v2, 1/2] mmc: sdhci-of-esdhc: fix esdhc_reset() " Ulf Hansson
  0 siblings, 2 replies; 4+ messages in thread
From: Yangbo Lu @ 2020-01-08  4:07 UTC (permalink / raw)
  To: linux-mmc, Ulf Hansson, Adrian Hunter; +Cc: Yangbo Lu

This patch is to fix operating in esdhc_reset() for different
controller versions, and to add bus-width restoring after data
reset for eSDHC (verdor version <= 2.2).

Also add annotation for understanding.

Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
---
Changes for v2:
	- Added ACK from Adrian.
	- Used preferred style for comments.
---
 drivers/mmc/host/sdhci-of-esdhc.c | 43 +++++++++++++++++++++++++++++++++++----
 1 file changed, 39 insertions(+), 4 deletions(-)

diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index cd3b676..1110f90 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -758,23 +758,58 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask)
 {
 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 	struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
-	u32 val;
+	u32 val, bus_width = 0;
 
+	/*
+	 * Add delay to make sure all the DMA transfers are finished
+	 * for quirk.
+	 */
 	if (esdhc->quirk_delay_before_data_reset &&
 	    (mask & SDHCI_RESET_DATA) &&
 	    (host->flags & SDHCI_REQ_USE_DMA))
 		mdelay(5);
 
+	/*
+	 * Save bus-width for eSDHC whose vendor version is 2.2
+	 * or lower for data reset.
+	 */
+	if ((mask & SDHCI_RESET_DATA) &&
+	    (esdhc->vendor_ver <= VENDOR_V_22)) {
+		val = sdhci_readl(host, ESDHC_PROCTL);
+		bus_width = val & ESDHC_CTRL_BUSWIDTH_MASK;
+	}
+
 	sdhci_reset(host, mask);
 
-	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
-	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+	/*
+	 * Restore bus-width setting and interrupt registers for eSDHC
+	 * whose vendor version is 2.2 or lower for data reset.
+	 */
+	if ((mask & SDHCI_RESET_DATA) &&
+	    (esdhc->vendor_ver <= VENDOR_V_22)) {
+		val = sdhci_readl(host, ESDHC_PROCTL);
+		val &= ~ESDHC_CTRL_BUSWIDTH_MASK;
+		val |= bus_width;
+		sdhci_writel(host, val, ESDHC_PROCTL);
+
+		sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+		sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+	}
 
-	if (mask & SDHCI_RESET_ALL) {
+	/*
+	 * Some bits have to be cleaned manually for eSDHC whose spec
+	 * version is higher than 3.0 for all reset.
+	 */
+	if ((mask & SDHCI_RESET_ALL) &&
+	    (esdhc->spec_ver >= SDHCI_SPEC_300)) {
 		val = sdhci_readl(host, ESDHC_TBCTL);
 		val &= ~ESDHC_TB_EN;
 		sdhci_writel(host, val, ESDHC_TBCTL);
 
+		/*
+		 * Initialize eSDHC_DLLCFG1[DLL_PD_PULSE_STRETCH_SEL] to
+		 * 0 for quirk.
+		 */
 		if (esdhc->quirk_unreliable_pulse_detection) {
 			val = sdhci_readl(host, ESDHC_DLLCFG1);
 			val &= ~ESDHC_DLL_PD_PULSE_STRETCH_SEL;
-- 
2.7.4


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [v2, 2/2] mmc: sdhci-of-esdhc: fix clock setting for different controller versions
  2020-01-08  4:07 [v2, 1/2] mmc: sdhci-of-esdhc: fix esdhc_reset() for different controller versions Yangbo Lu
@ 2020-01-08  4:07 ` " Yangbo Lu
  2020-01-16 14:39   ` Ulf Hansson
  2020-01-16 14:39 ` [v2, 1/2] mmc: sdhci-of-esdhc: fix esdhc_reset() " Ulf Hansson
  1 sibling, 1 reply; 4+ messages in thread
From: Yangbo Lu @ 2020-01-08  4:07 UTC (permalink / raw)
  To: linux-mmc, Ulf Hansson, Adrian Hunter; +Cc: Yangbo Lu

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(-)

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


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [v2, 1/2] mmc: sdhci-of-esdhc: fix esdhc_reset() for different controller versions
  2020-01-08  4:07 [v2, 1/2] mmc: sdhci-of-esdhc: fix esdhc_reset() for different controller versions Yangbo Lu
  2020-01-08  4:07 ` [v2, 2/2] mmc: sdhci-of-esdhc: fix clock setting " Yangbo Lu
@ 2020-01-16 14:39 ` " Ulf Hansson
  1 sibling, 0 replies; 4+ messages in thread
From: Ulf Hansson @ 2020-01-16 14:39 UTC (permalink / raw)
  To: Yangbo Lu; +Cc: linux-mmc, Adrian Hunter

On Wed, 8 Jan 2020 at 05:08, Yangbo Lu <yangbo.lu@nxp.com> wrote:
>
> This patch is to fix operating in esdhc_reset() for different
> controller versions, and to add bus-width restoring after data
> reset for eSDHC (verdor version <= 2.2).
>
> Also add annotation for understanding.
>
> Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
> Acked-by: Adrian Hunter <adrian.hunter@intel.com>

Applied for next, thanks!

Kind regards
Uffe


> ---
> Changes for v2:
>         - Added ACK from Adrian.
>         - Used preferred style for comments.
> ---
>  drivers/mmc/host/sdhci-of-esdhc.c | 43 +++++++++++++++++++++++++++++++++++----
>  1 file changed, 39 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
> index cd3b676..1110f90 100644
> --- a/drivers/mmc/host/sdhci-of-esdhc.c
> +++ b/drivers/mmc/host/sdhci-of-esdhc.c
> @@ -758,23 +758,58 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask)
>  {
>         struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>         struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
> -       u32 val;
> +       u32 val, bus_width = 0;
>
> +       /*
> +        * Add delay to make sure all the DMA transfers are finished
> +        * for quirk.
> +        */
>         if (esdhc->quirk_delay_before_data_reset &&
>             (mask & SDHCI_RESET_DATA) &&
>             (host->flags & SDHCI_REQ_USE_DMA))
>                 mdelay(5);
>
> +       /*
> +        * Save bus-width for eSDHC whose vendor version is 2.2
> +        * or lower for data reset.
> +        */
> +       if ((mask & SDHCI_RESET_DATA) &&
> +           (esdhc->vendor_ver <= VENDOR_V_22)) {
> +               val = sdhci_readl(host, ESDHC_PROCTL);
> +               bus_width = val & ESDHC_CTRL_BUSWIDTH_MASK;
> +       }
> +
>         sdhci_reset(host, mask);
>
> -       sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
> -       sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
> +       /*
> +        * Restore bus-width setting and interrupt registers for eSDHC
> +        * whose vendor version is 2.2 or lower for data reset.
> +        */
> +       if ((mask & SDHCI_RESET_DATA) &&
> +           (esdhc->vendor_ver <= VENDOR_V_22)) {
> +               val = sdhci_readl(host, ESDHC_PROCTL);
> +               val &= ~ESDHC_CTRL_BUSWIDTH_MASK;
> +               val |= bus_width;
> +               sdhci_writel(host, val, ESDHC_PROCTL);
> +
> +               sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
> +               sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
> +       }
>
> -       if (mask & SDHCI_RESET_ALL) {
> +       /*
> +        * Some bits have to be cleaned manually for eSDHC whose spec
> +        * version is higher than 3.0 for all reset.
> +        */
> +       if ((mask & SDHCI_RESET_ALL) &&
> +           (esdhc->spec_ver >= SDHCI_SPEC_300)) {
>                 val = sdhci_readl(host, ESDHC_TBCTL);
>                 val &= ~ESDHC_TB_EN;
>                 sdhci_writel(host, val, ESDHC_TBCTL);
>
> +               /*
> +                * Initialize eSDHC_DLLCFG1[DLL_PD_PULSE_STRETCH_SEL] to
> +                * 0 for quirk.
> +                */
>                 if (esdhc->quirk_unreliable_pulse_detection) {
>                         val = sdhci_readl(host, ESDHC_DLLCFG1);
>                         val &= ~ESDHC_DLL_PD_PULSE_STRETCH_SEL;
> --
> 2.7.4
>

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [v2, 2/2] mmc: sdhci-of-esdhc: fix clock setting for different controller versions
  2020-01-08  4:07 ` [v2, 2/2] mmc: sdhci-of-esdhc: fix clock setting " Yangbo Lu
@ 2020-01-16 14:39   ` Ulf Hansson
  0 siblings, 0 replies; 4+ messages in thread
From: Ulf Hansson @ 2020-01-16 14:39 UTC (permalink / raw)
  To: Yangbo Lu; +Cc: linux-mmc, Adrian Hunter

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
>

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, back to index

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-01-08  4:07 [v2, 1/2] mmc: sdhci-of-esdhc: fix esdhc_reset() for different controller versions Yangbo Lu
2020-01-08  4:07 ` [v2, 2/2] mmc: sdhci-of-esdhc: fix clock setting " Yangbo Lu
2020-01-16 14:39   ` Ulf Hansson
2020-01-16 14:39 ` [v2, 1/2] mmc: sdhci-of-esdhc: fix esdhc_reset() " Ulf Hansson

Linux-mmc Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-mmc/0 linux-mmc/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-mmc linux-mmc/ https://lore.kernel.org/linux-mmc \
		linux-mmc@vger.kernel.org
	public-inbox-index linux-mmc

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-mmc


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git