/* 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); }
/* 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); }