On 10/10/16 18:42, Ritesh Harjani wrote: > Hi Adrian, > > On 10/10/2016 6:19 PM, Adrian Hunter wrote: >> On 05/10/16 17:40, Ritesh Harjani wrote: >>> From: Venkat Gopalakrishnan <venkatg@xxxxxxxxxxxxxx> >>> >>> 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> >>> --- >>> drivers/mmc/host/sdhci-msm.c | 178 >>> +++++++++++++++++++++++++++++++++++++++++++ >>> 1 file changed, 178 insertions(+) >>> >>> diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c >>> index 612fa82..dbf80a9c 100644 >>> --- a/drivers/mmc/host/sdhci-msm.c >>> +++ b/drivers/mmc/host/sdhci-msm.c >>> @@ -57,6 +57,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,8 +73,36 @@ >>> #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 >>> #define TCXO_FREQ 19200000 >>> #define SDHCI_MSM_MIN_CLOCK 400000 >>> #define CORE_FREQ_100MHZ (100 * 1000 * 1000) >>> @@ -97,6 +126,7 @@ struct sdhci_msm_host { >>> bool use_14lpp_dll_reset; >>> bool tuning_done; >>> bool calibration_done; >>> + u8 saved_tuning_phase; >>> }; >>> >>> /* Platform specific tuning */ >>> @@ -426,6 +456,136 @@ 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 wait_cnt, config; >>> + int ret; >>> + >>> + pr_debug("%s: Enter %s\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); >>> + >>> + wmb(); /* drain writebuffer */ >>> + >>> + /* Poll on CALIBRATION_DONE field in CORE_CSR_CDC_STATUS0 to be 1 */ >>> + wait_cnt = 50; >>> + while (!(readl_relaxed(host->ioaddr + CORE_CSR_CDC_STATUS0) >>> + & CORE_CALIBRATION_DONE)) { >>> + /* max. wait for 50us sec for CALIBRATION_DONE bit to be set */ >>> + if (--wait_cnt == 0) { >>> + pr_err("%s: %s: CDC Calibration was not completed\n", >>> + mmc_hostname(host->mmc), __func__); >>> + ret = -ETIMEDOUT; >>> + goto out; >>> + } >>> + /* wait for 1us before polling again */ >>> + udelay(1); >>> + } >>> + >>> + /* 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: Exit %s, 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; >>> @@ -433,6 +593,8 @@ static int sdhci_msm_execute_tuning(struct sdhci_host >>> *host, u32 opcode) >>> int rc; >>> struct mmc_host *mmc = host->mmc; >>> struct mmc_ios ios = host->mmc->ios; >>> + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); >>> + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); >>> >>> /* >>> * Tuning is required for SDR104, HS200 and HS400 cards and >>> @@ -457,6 +619,7 @@ retry: >>> if (rc) >>> return rc; >>> >>> + msm_host->saved_tuning_phase = phase; >>> rc = mmc_send_tuning(mmc, opcode, NULL); >>> if (!rc) { >>> /* Tuning is successful at this tuning point */ >>> @@ -492,6 +655,8 @@ retry: >>> rc = -EIO; >>> } >>> >>> + if (!rc) >>> + msm_host->tuning_done = true; >>> return rc; >>> } >>> >>> @@ -565,6 +730,17 @@ 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) >>> @@ -907,6 +1083,8 @@ static int sdhci_msm_probe(struct platform_device >>> *pdev) >>> >>> sdhci_msm_populate_dt(&pdev->dev, msm_host); >>> >>> + msm_host->saved_tuning_phase = INVALID_TUNING_PHASE; >> >> There is never a check for INVALID_TUNING_PHASE which begs the question: why >> have it? > phase value can be between 0x0 to 0xf. So during probe saved_tuning_phase is > getting initialized with -1 value. > > Let me know if any concern, we can remove it as well. > But wont it look incorrect if we initialize it with some valid phase value > before even tuning is completed? INVALID_TUNING_PHASE is fine, I would just expect it be checked e.g. in msm_config_cm_dll_phase if (phase > 0xf) return -EINVAL; > >> >>> + >>> /* Setup SDCC bus voter clock. */ >>> msm_host->bus_clk = devm_clk_get(&pdev->dev, "bus"); >>> if (!IS_ERR(msm_host->bus_clk)) { >>> >> > -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html