forked from luck/tmp_suning_uos_patched
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: 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>
This commit is contained in:
parent
2aa3d826ad
commit
1b21a701ae
|
@ -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];
|
||||
clock_fixup = esdhc->clk_fixup->max_clk[host->mmc->ios.timing];
|
||||
|
||||
if (fixup && clock > fixup)
|
||||
clock = fixup;
|
||||
if (clock_fixup == 0 || clock < clock_fixup)
|
||||
clock_fixup = clock;
|
||||
|
||||
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);
|
||||
|
||||
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)
|
||||
|
|
Loading…
Reference in New Issue
Block a user