On 07/11/16 13:24, Ritesh Harjani wrote: > In HS400 mode a new RCLK is introduced on the interface for read data > transfers. The eMMC5.0 device transmits the read data to the host with > respect to rising and falling edges of RCLK. In order to ensure correct > operation of read data transfers in HS400 mode, the incoming RX data > needs to be sampled by delayed version of RCLK. > > The CDCLP533 delay circuit shifts the RCLK by T/4. It needs to be > initialized, configured and enabled once during HS400 mode switch and > when operational voltage/clock is changed. > > Signed-off-by: Venkat Gopalakrishnan <venkatg@xxxxxxxxxxxxxx> > Signed-off-by: Ritesh Harjani <riteshh@xxxxxxxxxxxxxx> Acked-by: Adrian Hunter <adrian.hunter@xxxxxxxxx> > --- > drivers/mmc/host/sdhci-msm.c | 163 +++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 163 insertions(+) > > diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c > index 6431bb8..ae19d1a 100644 > --- a/drivers/mmc/host/sdhci-msm.c > +++ b/drivers/mmc/host/sdhci-msm.c > @@ -19,6 +19,7 @@ > #include <linux/delay.h> > #include <linux/mmc/mmc.h> > #include <linux/slab.h> > +#include <linux/iopoll.h> > > #include "sdhci-pltfm.h" > > @@ -57,6 +58,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 > @@ -72,6 +74,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 > @@ -429,6 +458,131 @@ 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; > + > + /* Write 1 to CMD_DAT_TRACK_SEL field in DLL_CONFIG */ > + config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); > + config |= CORE_CMD_DAT_TRACK_SEL; > + writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); > + > + /* Write 0 to CDC_T4_DLY_SEL field in VENDOR_SPEC_DDR200_CFG */ > + config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG); > + config &= ~CORE_CDC_T4_DLY_SEL; > + writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG); > + > + /* Write 0 to CDC_SWITCH_BYPASS_OFF field in CORE_CSR_CDC_GEN_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); > + > + /* Write 1 to CDC_SWITCH_RC_EN field in 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); > + > + /* Write 0 to START_CDC_TRAFFIC field in CORE_DDR200_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 */ > + > + /* Write 1 to SW_TRIG_FULL_CALIB field in 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); > + > + /* Write 0 to SW_TRIG_FULL_CALIB field in 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); > + > + /* Write 1 to HW_AUTOCAL_ENA field in 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); > + > + /* Write 1 to TIMER_ENA field in CORE_CSR_CDC_CAL_TIMER_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); > + > + /* Poll on CALIBRATION_DONE field in CORE_CSR_CDC_STATUS0 to be 1 */ > + 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; > + } > + > + /* Verify CDC_ERROR_CODE field in CORE_CSR_CDC_STATUS0 is 0 */ > + 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; > + } > + > + /* Write 1 to START_CDC_TRAFFIC field in CORE_DDR200_CFG */ > + 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; > @@ -571,6 +725,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) > -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html