On 22/11/18 12:18 PM, Loic Poulain wrote: > The Clock Data Recovery (CDR) circuit allows to automatically adjust > the RX sampling-point/phase for high frequency cards (SDR104, HS200...). > CDR is automatically enabled during DLL configuration. > However, according to the APQ8016 reference manual, this function > must be disabled during TX and tuning phase in order to prevent any > interferences during tuning challenges and unexpected phase alteration > during TX transfers. > > This patch enables/disables CDR according to the current transfer mode. > > This fixes sporadic write transfer issues observed with some SDR104 and > HS200 cards. > > Inspired by sdhci-msm downstream patch: > https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/432516/ > > Reported-by: Leonid Segal <leonid.s@xxxxxxxxxxxxx> > Reported-by: Manabu Igusa <migusa@xxxxxxxxxxxxxx> > Signed-off-by: Loic Poulain <loic.poulain@xxxxxxxxxx> Acked-by: Adrian Hunter <adrian.hunter@xxxxxxxxx> > --- > v2: reword: previous commit message was not the good version > > drivers/mmc/host/sdhci-msm.c | 44 +++++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 43 insertions(+), 1 deletion(-) > > diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c > index a28f5fe..7495854 100644 > --- a/drivers/mmc/host/sdhci-msm.c > +++ b/drivers/mmc/host/sdhci-msm.c > @@ -260,6 +260,8 @@ struct sdhci_msm_host { > const struct sdhci_msm_variant_ops *var_ops; > const struct sdhci_msm_offset *offset; > struct icc_path *path; > + bool use_cdr; > + u32 transfer_mode; > }; > > static const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host) > @@ -1027,6 +1029,27 @@ static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host) > return ret; > } > > +static void sdhci_msm_set_cdr(struct sdhci_host *host, bool enable) > +{ > + const struct sdhci_msm_offset *msm_offset = sdhci_priv_msm_offset(host); > + u32 config, oldconfig = readl_relaxed(host->ioaddr + > + msm_offset->core_dll_config); > + > + config = oldconfig; > + if (enable) { > + config |= CORE_CDR_EN; > + config &= ~CORE_CDR_EXT_EN; > + } else { > + config &= ~CORE_CDR_EN; > + config |= CORE_CDR_EXT_EN; > + } > + > + if (config != oldconfig) { > + writel_relaxed(config, host->ioaddr + > + msm_offset->core_dll_config); > + } > +} > + > static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode) > { > struct sdhci_host *host = mmc_priv(mmc); > @@ -1044,8 +1067,14 @@ static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode) > if (host->clock <= CORE_FREQ_100MHZ || > !(ios.timing == MMC_TIMING_MMC_HS400 || > ios.timing == MMC_TIMING_MMC_HS200 || > - ios.timing == MMC_TIMING_UHS_SDR104)) > + ios.timing == MMC_TIMING_UHS_SDR104)) { > + msm_host->use_cdr = false; > + sdhci_msm_set_cdr(host, false); > return 0; > + } > + > + /* Clock-Data-Recovery used to dynamically adjust RX sampling point */ > + msm_host->use_cdr = true; > > /* > * For HS400 tuning in HS200 timing requires: > @@ -1527,6 +1556,19 @@ static int __sdhci_msm_check_write(struct sdhci_host *host, u16 val, int reg) > case SDHCI_POWER_CONTROL: > req_type = !val ? REQ_BUS_OFF : REQ_BUS_ON; > break; > + case SDHCI_TRANSFER_MODE: > + msm_host->transfer_mode = val; > + break; > + case SDHCI_COMMAND: > + if (!msm_host->use_cdr) > + break; > + if ((msm_host->transfer_mode & SDHCI_TRNS_READ) && > + (SDHCI_GET_CMD(val) != MMC_SEND_TUNING_BLOCK_HS200) && > + (SDHCI_GET_CMD(val) != MMC_SEND_TUNING_BLOCK)) > + sdhci_msm_set_cdr(host, true); > + else > + sdhci_msm_set_cdr(host, false); > + break; > } > > if (req_type) { >