|
@@ -52,6 +52,7 @@
|
|
|
#define INT_MASK 0xf
|
|
|
#define MAX_PHASES 16
|
|
|
#define CORE_DLL_LOCK BIT(7)
|
|
|
+#define CORE_DDR_DLL_LOCK BIT(11)
|
|
|
#define CORE_DLL_EN BIT(16)
|
|
|
#define CORE_CDR_EN BIT(17)
|
|
|
#define CORE_CK_OUT_EN BIT(18)
|
|
@@ -63,6 +64,7 @@
|
|
|
#define CORE_DLL_STATUS 0x108
|
|
|
|
|
|
#define CORE_DLL_CONFIG_2 0x1b4
|
|
|
+#define CORE_DDR_CAL_EN BIT(0)
|
|
|
#define CORE_FLL_CYCLE_CNT BIT(18)
|
|
|
#define CORE_DLL_CLOCK_DISABLE BIT(21)
|
|
|
|
|
@@ -101,6 +103,11 @@
|
|
|
#define CORE_DDR_200_CFG 0x184
|
|
|
#define CORE_CDC_T4_DLY_SEL BIT(0)
|
|
|
#define CORE_START_CDC_TRAFFIC BIT(6)
|
|
|
+#define CORE_VENDOR_SPEC3 0x1b0
|
|
|
+#define CORE_PWRSAVE_DLL BIT(3)
|
|
|
+
|
|
|
+#define CORE_DDR_CONFIG 0x1b8
|
|
|
+#define DDR_CONFIG_POR_VAL 0x80040853
|
|
|
|
|
|
#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c
|
|
|
|
|
@@ -128,6 +135,7 @@ struct sdhci_msm_host {
|
|
|
bool tuning_done;
|
|
|
bool calibration_done;
|
|
|
u8 saved_tuning_phase;
|
|
|
+ bool use_cdclp533;
|
|
|
};
|
|
|
|
|
|
/* Platform specific tuning */
|
|
@@ -569,6 +577,87 @@ out:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host)
|
|
|
+{
|
|
|
+ u32 dll_status, config;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ pr_debug("%s: %s: Enter\n", mmc_hostname(host->mmc), __func__);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Currently the CORE_DDR_CONFIG register defaults to desired
|
|
|
+ * configuration on reset. Currently reprogramming the power on
|
|
|
+ * reset (POR) value in case it might have been modified by
|
|
|
+ * bootloaders. In the future, if this changes, then the desired
|
|
|
+ * values will need to be programmed appropriately.
|
|
|
+ */
|
|
|
+ writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + CORE_DDR_CONFIG);
|
|
|
+
|
|
|
+ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2);
|
|
|
+ config |= CORE_DDR_CAL_EN;
|
|
|
+ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2);
|
|
|
+
|
|
|
+ ret = readl_relaxed_poll_timeout(host->ioaddr + CORE_DLL_STATUS,
|
|
|
+ dll_status,
|
|
|
+ (dll_status & CORE_DDR_DLL_LOCK),
|
|
|
+ 10, 1000);
|
|
|
+
|
|
|
+ if (ret == -ETIMEDOUT) {
|
|
|
+ pr_err("%s: %s: CM_DLL_SDC4 calibration was not completed\n",
|
|
|
+ mmc_hostname(host->mmc), __func__);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC3);
|
|
|
+ config |= CORE_PWRSAVE_DLL;
|
|
|
+ writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC3);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Drain writebuffer to ensure above DLL calibration
|
|
|
+ * and PWRSAVE DLL is enabled.
|
|
|
+ */
|
|
|
+ wmb();
|
|
|
+out:
|
|
|
+ pr_debug("%s: %s: Exit, ret %d\n", mmc_hostname(host->mmc),
|
|
|
+ __func__, ret);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host)
|
|
|
+{
|
|
|
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
|
|
+ struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
|
|
+ int ret;
|
|
|
+ u32 config;
|
|
|
+
|
|
|
+ pr_debug("%s: %s: Enter\n", mmc_hostname(host->mmc), __func__);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Retuning in HS400 (DDR mode) will fail, just reset the
|
|
|
+ * tuning block and restore the saved tuning phase.
|
|
|
+ */
|
|
|
+ ret = msm_init_cm_dll(host);
|
|
|
+ if (ret)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /* Set the selected phase in delay line hw block */
|
|
|
+ ret = msm_config_cm_dll_phase(host, msm_host->saved_tuning_phase);
|
|
|
+ if (ret)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
|
|
|
+ config |= CORE_CMD_DAT_TRACK_SEL;
|
|
|
+ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
|
|
|
+ if (msm_host->use_cdclp533)
|
|
|
+ ret = sdhci_msm_cdclp533_calibration(host);
|
|
|
+ else
|
|
|
+ ret = sdhci_msm_cm_dll_sdc4_calibration(host);
|
|
|
+out:
|
|
|
+ pr_debug("%s: %s: Exit, ret %d\n", mmc_hostname(host->mmc),
|
|
|
+ __func__, ret);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
|
|
|
{
|
|
|
int tuning_seq_cnt = 3;
|
|
@@ -715,7 +804,7 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
|
|
|
if (host->clock > CORE_FREQ_100MHZ &&
|
|
|
msm_host->tuning_done && !msm_host->calibration_done &&
|
|
|
mmc->ios.timing == MMC_TIMING_MMC_HS400)
|
|
|
- if (!sdhci_msm_cdclp533_calibration(host))
|
|
|
+ if (!sdhci_msm_hs400_dll_calibration(host))
|
|
|
msm_host->calibration_done = true;
|
|
|
spin_lock_irq(&host->lock);
|
|
|
}
|
|
@@ -805,7 +894,7 @@ 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;
|
|
|
+ u32 config, dll_lock;
|
|
|
int rc;
|
|
|
|
|
|
if (!clock) {
|
|
@@ -862,7 +951,32 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
|
|
|
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;
|
|
@@ -1054,6 +1168,13 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|
|
if (core_major == 1 && core_minor >= 0x42)
|
|
|
msm_host->use_14lpp_dll_reset = true;
|
|
|
|
|
|
+ /*
|
|
|
+ * SDCC 5 controller with major version 1, minor version 0x34 and later
|
|
|
+ * with HS 400 mode support will use CM DLL instead of CDC LP 533 DLL.
|
|
|
+ */
|
|
|
+ if (core_major == 1 && core_minor < 0x34)
|
|
|
+ msm_host->use_cdclp533 = true;
|
|
|
+
|
|
|
/*
|
|
|
* Support for some capabilities is not advertised by newer
|
|
|
* controller versions and must be explicitly enabled.
|