return 0;
}
+static void msm_hc_select_default(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+ u32 config;
+
+ 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();
+}
+
+static void msm_hc_select_hs400(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+ u32 config, dll_lock;
+ int rc;
+
+ /* 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) {
+ 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);
+ }
+ /*
+ * Make sure above writes impacting free running MCLK are completed
+ * before changing the clk_rate at GCC.
+ */
+ wmb();
+}
+
+/*
+ * sdhci_msm_hc_select_mode :- 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)
+ */
+void sdhci_msm_hc_select_mode(struct sdhci_host *host)
+{
+ struct mmc_ios ios = host->mmc->ios;
+
+ if (ios.timing == MMC_TIMING_MMC_HS400)
+ msm_hc_select_hs400(host);
+ else
+ msm_hc_select_default(host);
+}
+
static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
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) {
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();
+ sdhci_msm_hc_select_mode(host);
rc = clk_set_rate(msm_host->clk, clock);
if (rc) {