|
@@ -20,6 +20,7 @@
|
|
|
#include <linux/mmc/mmc.h>
|
|
|
#include <linux/pm_runtime.h>
|
|
|
#include <linux/slab.h>
|
|
|
+#include <linux/iopoll.h>
|
|
|
|
|
|
#include "sdhci-pltfm.h"
|
|
|
|
|
@@ -58,6 +59,7 @@
|
|
|
#define CORE_DLL_PDN BIT(29)
|
|
|
#define CORE_DLL_RST BIT(30)
|
|
|
#define CORE_DLL_CONFIG 0x100
|
|
|
+#define CORE_CMD_DAT_TRACK_SEL BIT(0)
|
|
|
#define CORE_DLL_STATUS 0x108
|
|
|
|
|
|
#define CORE_DLL_CONFIG_2 0x1b4
|
|
@@ -73,6 +75,33 @@
|
|
|
#define CORE_HC_SELECT_IN_HS400 (6 << 19)
|
|
|
#define CORE_HC_SELECT_IN_MASK (7 << 19)
|
|
|
|
|
|
+#define CORE_CSR_CDC_CTLR_CFG0 0x130
|
|
|
+#define CORE_SW_TRIG_FULL_CALIB BIT(16)
|
|
|
+#define CORE_HW_AUTOCAL_ENA BIT(17)
|
|
|
+
|
|
|
+#define CORE_CSR_CDC_CTLR_CFG1 0x134
|
|
|
+#define CORE_CSR_CDC_CAL_TIMER_CFG0 0x138
|
|
|
+#define CORE_TIMER_ENA BIT(16)
|
|
|
+
|
|
|
+#define CORE_CSR_CDC_CAL_TIMER_CFG1 0x13C
|
|
|
+#define CORE_CSR_CDC_REFCOUNT_CFG 0x140
|
|
|
+#define CORE_CSR_CDC_COARSE_CAL_CFG 0x144
|
|
|
+#define CORE_CDC_OFFSET_CFG 0x14C
|
|
|
+#define CORE_CSR_CDC_DELAY_CFG 0x150
|
|
|
+#define CORE_CDC_SLAVE_DDA_CFG 0x160
|
|
|
+#define CORE_CSR_CDC_STATUS0 0x164
|
|
|
+#define CORE_CALIBRATION_DONE BIT(0)
|
|
|
+
|
|
|
+#define CORE_CDC_ERROR_CODE_MASK 0x7000000
|
|
|
+
|
|
|
+#define CORE_CSR_CDC_GEN_CFG 0x178
|
|
|
+#define CORE_CDC_SWITCH_BYPASS_OFF BIT(0)
|
|
|
+#define CORE_CDC_SWITCH_RC_EN BIT(1)
|
|
|
+
|
|
|
+#define CORE_DDR_200_CFG 0x184
|
|
|
+#define CORE_CDC_T4_DLY_SEL BIT(0)
|
|
|
+#define CORE_START_CDC_TRAFFIC BIT(6)
|
|
|
+
|
|
|
#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c
|
|
|
|
|
|
#define INVALID_TUNING_PHASE -1
|
|
@@ -427,6 +456,119 @@ static int msm_init_cm_dll(struct sdhci_host *host)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int sdhci_msm_cdclp533_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);
|
|
|
+ u32 config, calib_done;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ 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);
|
|
|
+
|
|
|
+ config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG);
|
|
|
+ config &= ~CORE_CDC_T4_DLY_SEL;
|
|
|
+ writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG);
|
|
|
+
|
|
|
+ config = readl_relaxed(host->ioaddr + CORE_CSR_CDC_GEN_CFG);
|
|
|
+ config &= ~CORE_CDC_SWITCH_BYPASS_OFF;
|
|
|
+ writel_relaxed(config, host->ioaddr + CORE_CSR_CDC_GEN_CFG);
|
|
|
+
|
|
|
+ config = readl_relaxed(host->ioaddr + CORE_CSR_CDC_GEN_CFG);
|
|
|
+ config |= CORE_CDC_SWITCH_RC_EN;
|
|
|
+ writel_relaxed(config, host->ioaddr + CORE_CSR_CDC_GEN_CFG);
|
|
|
+
|
|
|
+ config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG);
|
|
|
+ config &= ~CORE_START_CDC_TRAFFIC;
|
|
|
+ writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Perform CDC Register Initialization Sequence
|
|
|
+ *
|
|
|
+ * CORE_CSR_CDC_CTLR_CFG0 0x11800EC
|
|
|
+ * CORE_CSR_CDC_CTLR_CFG1 0x3011111
|
|
|
+ * CORE_CSR_CDC_CAL_TIMER_CFG0 0x1201000
|
|
|
+ * CORE_CSR_CDC_CAL_TIMER_CFG1 0x4
|
|
|
+ * CORE_CSR_CDC_REFCOUNT_CFG 0xCB732020
|
|
|
+ * CORE_CSR_CDC_COARSE_CAL_CFG 0xB19
|
|
|
+ * CORE_CSR_CDC_DELAY_CFG 0x3AC
|
|
|
+ * CORE_CDC_OFFSET_CFG 0x0
|
|
|
+ * CORE_CDC_SLAVE_DDA_CFG 0x16334
|
|
|
+ */
|
|
|
+
|
|
|
+ writel_relaxed(0x11800EC, host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
|
|
|
+ writel_relaxed(0x3011111, host->ioaddr + CORE_CSR_CDC_CTLR_CFG1);
|
|
|
+ writel_relaxed(0x1201000, host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG0);
|
|
|
+ writel_relaxed(0x4, host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG1);
|
|
|
+ writel_relaxed(0xCB732020, host->ioaddr + CORE_CSR_CDC_REFCOUNT_CFG);
|
|
|
+ writel_relaxed(0xB19, host->ioaddr + CORE_CSR_CDC_COARSE_CAL_CFG);
|
|
|
+ writel_relaxed(0x3AC, host->ioaddr + CORE_CSR_CDC_DELAY_CFG);
|
|
|
+ writel_relaxed(0x0, host->ioaddr + CORE_CDC_OFFSET_CFG);
|
|
|
+ writel_relaxed(0x16334, host->ioaddr + CORE_CDC_SLAVE_DDA_CFG);
|
|
|
+
|
|
|
+ /* CDC HW Calibration */
|
|
|
+
|
|
|
+ config = readl_relaxed(host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
|
|
|
+ config |= CORE_SW_TRIG_FULL_CALIB;
|
|
|
+ writel_relaxed(config, host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
|
|
|
+
|
|
|
+ config = readl_relaxed(host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
|
|
|
+ config &= ~CORE_SW_TRIG_FULL_CALIB;
|
|
|
+ writel_relaxed(config, host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
|
|
|
+
|
|
|
+ config = readl_relaxed(host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
|
|
|
+ config |= CORE_HW_AUTOCAL_ENA;
|
|
|
+ writel_relaxed(config, host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
|
|
|
+
|
|
|
+ config = readl_relaxed(host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG0);
|
|
|
+ config |= CORE_TIMER_ENA;
|
|
|
+ writel_relaxed(config, host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG0);
|
|
|
+
|
|
|
+ ret = readl_relaxed_poll_timeout(host->ioaddr + CORE_CSR_CDC_STATUS0,
|
|
|
+ calib_done,
|
|
|
+ (calib_done & CORE_CALIBRATION_DONE),
|
|
|
+ 1, 50);
|
|
|
+
|
|
|
+ if (ret == -ETIMEDOUT) {
|
|
|
+ pr_err("%s: %s: CDC calibration was not completed\n",
|
|
|
+ mmc_hostname(host->mmc), __func__);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = readl_relaxed(host->ioaddr + CORE_CSR_CDC_STATUS0)
|
|
|
+ & CORE_CDC_ERROR_CODE_MASK;
|
|
|
+ if (ret) {
|
|
|
+ pr_err("%s: %s: CDC error code %d\n",
|
|
|
+ mmc_hostname(host->mmc), __func__, ret);
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG);
|
|
|
+ config |= CORE_START_CDC_TRAFFIC;
|
|
|
+ writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG);
|
|
|
+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;
|
|
@@ -567,6 +709,15 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
|
|
|
dev_dbg(mmc_dev(mmc), "%s: clock=%u uhs=%u ctrl_2=0x%x\n",
|
|
|
mmc_hostname(host->mmc), host->clock, uhs, ctrl_2);
|
|
|
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
|
|
+
|
|
|
+ spin_unlock_irq(&host->lock);
|
|
|
+ /* CDCLP533 HW calibration is only required for HS400 mode*/
|
|
|
+ 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))
|
|
|
+ msm_host->calibration_done = true;
|
|
|
+ spin_lock_irq(&host->lock);
|
|
|
}
|
|
|
|
|
|
static void sdhci_msm_voltage_switch(struct sdhci_host *host)
|