|
@@ -464,6 +464,119 @@ static int msm_init_cm_dll(struct sdhci_host *host)
|
|
|
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);
|
|
@@ -894,7 +1007,6 @@ 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) {
|
|
@@ -913,93 +1025,8 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int 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) {
|