示例#1
0
/* sdhci_msm_set_clock - Called with (host->lock) spinlock held. */
static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
{
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
	struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);

	if (!clock) {
		msm_host->clk_rate = clock;
		goto out;
	}

	sdhci_msm_hc_select_mode(host);

	msm_set_clock_rate_for_bus_mode(host, clock);
out:
	__sdhci_msm_set_clock(host, clock);
}
示例#2
0
/* sdhci_msm_set_clock - Called with (host->lock) spinlock held. */
static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
{
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
	struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
	struct mmc_ios curr_ios = host->mmc->ios;
	u32 config, dll_lock;
	int rc;

	if (!clock) {
		msm_host->clk_rate = clock;
		goto out;
	}

	spin_unlock_irq(&host->lock);
	/*
	 * The SDHC requires internal clock frequency to be double the
	 * actual clock that will be set for DDR mode. The controller
	 * uses the faster clock(100/400MHz) for some of its parts and
	 * send the actual required clock (50/200MHz) to the card.
	 */
	if (curr_ios.timing == MMC_TIMING_UHS_DDR50 ||
	    curr_ios.timing == MMC_TIMING_MMC_DDR52 ||
	    curr_ios.timing == MMC_TIMING_MMC_HS400)
		clock *= 2;
	/*
	 * In general all timing modes are controlled via UHS mode select in
	 * Host Control2 register. eMMC specific HS200/HS400 doesn't have
	 * their respective modes defined here, hence we use these values.
	 *
	 * HS200 - SDR104 (Since they both are equivalent in functionality)
	 * HS400 - This involves multiple configurations
	 *		Initially SDR104 - when tuning is required as HS200
	 *		Then when switching to DDR @ 400MHz (HS400) we use
	 *		the vendor specific HC_SELECT_IN to control the mode.
	 *
	 * In addition to controlling the modes we also need to select the
	 * correct input clock for DLL depending on the mode.
	 *
	 * HS400 - divided clock (free running MCLK/2)
	 * All other modes - default (free running MCLK)
	 */
	if (curr_ios.timing == MMC_TIMING_MMC_HS400) {
		/* Select the divided clock (free running MCLK/2) */
		config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
		config &= ~CORE_HC_MCLK_SEL_MASK;
		config |= CORE_HC_MCLK_SEL_HS400;

		writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
		/*
		 * Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC
		 * register
		 */
		if (msm_host->tuning_done && !msm_host->calibration_done) {
			/*
			 * Write 0x6 to HC_SELECT_IN and 1 to HC_SELECT_IN_EN
			 * field in VENDOR_SPEC_FUNC
			 */
			config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
			config |= CORE_HC_SELECT_IN_HS400;
			config |= CORE_HC_SELECT_IN_EN;
			writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
		}
		if (!msm_host->clk_rate && !msm_host->use_cdclp533) {
			/*
			 * Poll on DLL_LOCK or DDR_DLL_LOCK bits in
			 * CORE_DLL_STATUS to be set.  This should get set
			 * within 15 us at 200 MHz.
			 */
			rc = readl_relaxed_poll_timeout(host->ioaddr +
							CORE_DLL_STATUS,
							dll_lock,
							(dll_lock &
							(CORE_DLL_LOCK |
							CORE_DDR_DLL_LOCK)), 10,
							1000);
			if (rc == -ETIMEDOUT)
				pr_err("%s: Unable to get DLL_LOCK/DDR_DLL_LOCK, dll_status: 0x%08x\n",
				       mmc_hostname(host->mmc), dll_lock);
		}
	} else {
		if (!msm_host->use_cdclp533) {
			config = readl_relaxed(host->ioaddr +
					CORE_VENDOR_SPEC3);
			config &= ~CORE_PWRSAVE_DLL;
			writel_relaxed(config, host->ioaddr +
					CORE_VENDOR_SPEC3);
		}

		config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
		config &= ~CORE_HC_MCLK_SEL_MASK;
		config |= CORE_HC_MCLK_SEL_DFLT;
		writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);

		/*
		 * Disable HC_SELECT_IN to be able to use the UHS mode select
		 * configuration from Host Control2 register for all other
		 * modes.
		 * Write 0 to HC_SELECT_IN and HC_SELECT_IN_EN field
		 * in VENDOR_SPEC_FUNC
		 */
		config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
		config &= ~CORE_HC_SELECT_IN_EN;
		config &= ~CORE_HC_SELECT_IN_MASK;
		writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
	}

	/*
	 * Make sure above writes impacting free running MCLK are completed
	 * before changing the clk_rate at GCC.
	 */
	wmb();

	rc = clk_set_rate(msm_host->clk, clock);
	if (rc) {
		pr_err("%s: Failed to set clock at rate %u at timing %d\n",
		       mmc_hostname(host->mmc), clock,
		       curr_ios.timing);
		goto out_lock;
	}
	msm_host->clk_rate = clock;
	pr_debug("%s: Setting clock at rate %lu at timing %d\n",
		 mmc_hostname(host->mmc), clk_get_rate(msm_host->clk),
		 curr_ios.timing);

out_lock:
	spin_lock_irq(&host->lock);
out:
	__sdhci_msm_set_clock(host, clock);
}