Commit e276420e authored by Loic Poulain's avatar Loic Poulain Committed by Greg Kroah-Hartman

mmc: sdhci-msm: Disable CDR function on TX

commit a89e7bcb upstream.

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: default avatarLeonid Segal <leonid.s@variscite.com>
Reported-by: default avatarManabu Igusa <migusa@arrowjapan.com>
Signed-off-by: default avatarLoic Poulain <loic.poulain@linaro.org>
Acked-by: default avatarAdrian Hunter <adrian.hunter@intel.com>
Acked-by: default avatarGeorgi Djakov <georgi.djakov@linaro.org>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
[georgi: backport to v4.19+]
Signed-off-by: default avatarGeorgi Djakov <georgi.djakov@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 6567515e
...@@ -258,6 +258,8 @@ struct sdhci_msm_host { ...@@ -258,6 +258,8 @@ struct sdhci_msm_host {
bool mci_removed; bool mci_removed;
const struct sdhci_msm_variant_ops *var_ops; const struct sdhci_msm_variant_ops *var_ops;
const struct sdhci_msm_offset *offset; const struct sdhci_msm_offset *offset;
bool use_cdr;
u32 transfer_mode;
}; };
static const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host) static const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host)
...@@ -1025,6 +1027,26 @@ static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host) ...@@ -1025,6 +1027,26 @@ static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host)
return ret; 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) static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode)
{ {
struct sdhci_host *host = mmc_priv(mmc); struct sdhci_host *host = mmc_priv(mmc);
...@@ -1042,8 +1064,14 @@ static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode) ...@@ -1042,8 +1064,14 @@ static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode)
if (host->clock <= CORE_FREQ_100MHZ || if (host->clock <= CORE_FREQ_100MHZ ||
!(ios.timing == MMC_TIMING_MMC_HS400 || !(ios.timing == MMC_TIMING_MMC_HS400 ||
ios.timing == MMC_TIMING_MMC_HS200 || 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; return 0;
}
/* Clock-Data-Recovery used to dynamically adjust RX sampling point */
msm_host->use_cdr = true;
/* /*
* For HS400 tuning in HS200 timing requires: * For HS400 tuning in HS200 timing requires:
...@@ -1525,6 +1553,19 @@ static int __sdhci_msm_check_write(struct sdhci_host *host, u16 val, int reg) ...@@ -1525,6 +1553,19 @@ static int __sdhci_msm_check_write(struct sdhci_host *host, u16 val, int reg)
case SDHCI_POWER_CONTROL: case SDHCI_POWER_CONTROL:
req_type = !val ? REQ_BUS_OFF : REQ_BUS_ON; req_type = !val ? REQ_BUS_OFF : REQ_BUS_ON;
break; 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) { if (req_type) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment