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)
|
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)
|
||||||
{
|
{
|
||||||
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)
|
if (clock_fixup == 0 || clock < clock_fixup)
|
||||||
clock = fixup;
|
clock_fixup = clock;
|
||||||
|
|
||||||
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
|
/* Calculate pre_div and div. */
|
||||||
temp &= ~(ESDHC_CLOCK_SDCLKEN | ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN |
|
while (host->max_clk / pre_div / 16 > clock_fixup && pre_div < 256)
|
||||||
ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK);
|
|
||||||
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
|
|
||||||
|
|
||||||
while (host->max_clk / pre_div / 16 > clock && 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)
|
||||||
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)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user