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