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

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.com


Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent 2aa3d826
Loading
Loading
Loading
Loading
+72 −56
Original line number Diff line number Diff line
@@ -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;
		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)