Commit 1b21a701 authored by Yangbo Lu's avatar Yangbo Lu Committed by Ulf Hansson

mmc: sdhci-of-esdhc: fix clock setting for different controller versions

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: default avatarYangbo Lu <yangbo.lu@nxp.com>
Link: https://lore.kernel.org/r/20200108040713.38888-2-yangbo.lu@nxp.comSigned-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent 2aa3d826
...@@ -562,32 +562,46 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host) ...@@ -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) 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; 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); val = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
if (enable) if (enable)
val |= ESDHC_CLOCK_SDCLKEN; val |= clk_en;
else else
val &= ~ESDHC_CLOCK_SDCLKEN; val &= ~clk_en;
sdhci_writel(host, val, ESDHC_SYSTEM_CONTROL); 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); timeout = ktime_add_ms(ktime_get(), 20);
val = ESDHC_CLOCK_STABLE; while (esdhc->vendor_ver > VENDOR_V_22) {
while (1) {
bool timedout = ktime_after(ktime_get(), timeout); bool timedout = ktime_after(ktime_get(), timeout);
if (sdhci_readl(host, ESDHC_PRSSTAT) & val) if (sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)
break; break;
if (timedout) { if (timedout) {
pr_err("%s: Internal clock never stabilised.\n", pr_err("%s: Internal clock never stabilised.\n",
mmc_hostname(host->mmc)); mmc_hostname(host->mmc));
break; break;
} }
udelay(10); usleep_range(10, 20);
} }
} }
...@@ -621,77 +635,97 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) ...@@ -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_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host); struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
int pre_div = 1; unsigned int pre_div = 1, div = 1;
int div = 1; unsigned int clock_fixup = 0;
int division;
ktime_t timeout; ktime_t timeout;
long fixup = 0;
u32 temp; u32 temp;
host->mmc->actual_clock = 0;
if (clock == 0) { if (clock == 0) {
host->mmc->actual_clock = 0;
esdhc_clock_enable(host, false); esdhc_clock_enable(host, false);
return; 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) if (esdhc->vendor_ver < VENDOR_V_23)
pre_div = 2; pre_div = 2;
/* Fix clock value. */
if (host->mmc->card && mmc_card_sd(host->mmc->card) && if (host->mmc->card && mmc_card_sd(host->mmc->card) &&
esdhc->clk_fixup && host->mmc->ios.timing == MMC_TIMING_LEGACY) esdhc->clk_fixup && host->mmc->ios.timing == MMC_TIMING_LEGACY)
fixup = esdhc->clk_fixup->sd_dflt_max_clk; clock_fixup = esdhc->clk_fixup->sd_dflt_max_clk;
else if (esdhc->clk_fixup) else if (esdhc->clk_fixup)
fixup = esdhc->clk_fixup->max_clk[host->mmc->ios.timing]; clock_fixup = esdhc->clk_fixup->max_clk[host->mmc->ios.timing];
if (fixup && clock > fixup)
clock = fixup;
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); if (clock_fixup == 0 || clock < clock_fixup)
temp &= ~(ESDHC_CLOCK_SDCLKEN | ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | clock_fixup = clock;
ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK);
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
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; 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++; div++;
esdhc->div_ratio = pre_div * div;
/* Limit clock division for HS400 200MHz clock for quirk. */
if (esdhc->quirk_limited_clk_division && if (esdhc->quirk_limited_clk_division &&
clock == MMC_HS200_MAX_DTR && clock == MMC_HS200_MAX_DTR &&
(host->mmc->ios.timing == MMC_TIMING_MMC_HS400 || (host->mmc->ios.timing == MMC_TIMING_MMC_HS400 ||
host->flags & SDHCI_HS400_TUNING)) { host->flags & SDHCI_HS400_TUNING)) {
division = pre_div * div; if (esdhc->div_ratio <= 4) {
if (division <= 4) {
pre_div = 4; pre_div = 4;
div = 1; div = 1;
} else if (division <= 8) { } else if (esdhc->div_ratio <= 8) {
pre_div = 4; pre_div = 4;
div = 2; div = 2;
} else if (division <= 12) { } else if (esdhc->div_ratio <= 12) {
pre_div = 4; pre_div = 4;
div = 3; div = 3;
} else { } else {
pr_warn("%s: using unsupported clock division.\n", pr_warn("%s: using unsupported clock division.\n",
mmc_hostname(host->mmc)); 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", dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
clock, host->max_clk / pre_div / div); clock, host->mmc->actual_clock);
host->mmc->actual_clock = host->max_clk / pre_div / div;
esdhc->div_ratio = pre_div * div; /* Set clock division into register. */
pre_div >>= 1; pre_div >>= 1;
div--; div--;
esdhc_clock_enable(host, false);
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN temp &= ~ESDHC_CLOCK_MASK;
| (div << ESDHC_DIVIDER_SHIFT) temp |= ((div << ESDHC_DIVIDER_SHIFT) |
| (pre_div << ESDHC_PREDIV_SHIFT)); (pre_div << ESDHC_PREDIV_SHIFT));
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); 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 && if (host->mmc->ios.timing == MMC_TIMING_MMC_HS400 &&
clock == MMC_HS200_MAX_DTR) { clock == MMC_HS200_MAX_DTR) {
temp = sdhci_readl(host, ESDHC_TBCTL); temp = sdhci_readl(host, ESDHC_TBCTL);
...@@ -711,25 +745,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) ...@@ -711,25 +745,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
esdhc_clock_enable(host, false); esdhc_clock_enable(host, false);
esdhc_flush_async_fifo(host); esdhc_flush_async_fifo(host);
} }
esdhc_clock_enable(host, false);
/* 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);
} }
static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width) static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment