diff options
Diffstat (limited to 'drivers/mmc')
| -rw-r--r-- | drivers/mmc/host/sdhci-cadence.c | 63 |
1 files changed, 62 insertions, 1 deletions
diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c index 2d823e158c59..a2a4a5b0ab96 100644 --- a/drivers/mmc/host/sdhci-cadence.c +++ b/drivers/mmc/host/sdhci-cadence.c @@ -36,6 +36,24 @@ #define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5 #define SDHCI_CDNS_HRS06_MODE_MMC_HS400ES 0x6 +/* Read block gap */ +#define SDHCI_CDNS_HRS37 0x94 /* interface mode select */ +#define SDHCI_CDNS_HRS37_MODE_DS 0x0 +#define SDHCI_CDNS_HRS37_MODE_HS 0x1 +#define SDHCI_CDNS_HRS37_MODE_UDS_SDR12 0x8 +#define SDHCI_CDNS_HRS37_MODE_UDS_SDR25 0x9 +#define SDHCI_CDNS_HRS37_MODE_UDS_SDR50 0xa +#define SDHCI_CDNS_HRS37_MODE_UDS_SDR104 0xb +#define SDHCI_CDNS_HRS37_MODE_UDS_DDR50 0xc +#define SDHCI_CDNS_HRS37_MODE_MMC_LEGACY 0x20 +#define SDHCI_CDNS_HRS37_MODE_MMC_SDR 0x21 +#define SDHCI_CDNS_HRS37_MODE_MMC_DDR 0x22 +#define SDHCI_CDNS_HRS37_MODE_MMC_HS200 0x23 +#define SDHCI_CDNS_HRS37_MODE_MMC_HS400 0x24 +#define SDHCI_CDNS_HRS37_MODE_MMC_HS400ES 0x25 +#define SDHCI_CDNS_HRS38 0x98 /* Read block gap coefficient */ +#define SDHCI_CDNS_HRS38_BLKGAP_MAX 0xf + /* SRS - Slot Register Set (SDHCI-compatible) */ #define SDHCI_CDNS_SRS_BASE 0x200 @@ -251,6 +269,44 @@ static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val) return 0; } +/** + * sdhci_cdns_tune_blkgap() - tune multi-block read gap + * @mmc: MMC host + * + * Tune delay used in multi block read. To do so, + * try sending multi-block read command with incremented gap, unless + * it succeeds. + * + * Return: error code + */ +static int sdhci_cdns_tune_blkgap(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_cdns_priv *priv = sdhci_pltfm_priv(pltfm_host); + void __iomem *hrs37_reg = priv->hrs_addr + SDHCI_CDNS_HRS37; + void __iomem *hrs38_reg = priv->hrs_addr + SDHCI_CDNS_HRS38; + int ret; + u32 gap; + u32 hrs37_mode; + + /* Currently only needed in HS200 mode */ + if (host->timing != MMC_TIMING_MMC_HS200) + return 0; + + writel(hrs37_mode, hrs37_reg); + + for (gap = 0; gap <= SDHCI_CDNS_HRS38_BLKGAP_MAX; gap++) { + writel(gap, hrs38_reg); + ret = mmc_read_tuning(mmc, 512, 32); + if (!ret) + break; + } + + dev_dbg(mmc_dev(mmc), "read block gap tune %s, gap %d\n", ret ? "failed" : "OK", gap); + return ret; +} + /* * In SD mode, software must not use the hardware tuning and instead perform * an almost identical procedure to eMMC. @@ -261,6 +317,7 @@ static int sdhci_cdns_execute_tuning(struct sdhci_host *host, u32 opcode) int max_streak = 0; int end_of_streak = 0; int i; + int ret; /* * Do not execute tuning for UHS_SDR50 or UHS_DDR50. @@ -288,7 +345,11 @@ static int sdhci_cdns_execute_tuning(struct sdhci_host *host, u32 opcode) return -EIO; } - return sdhci_cdns_set_tune_val(host, end_of_streak - max_streak / 2); + ret = sdhci_cdns_set_tune_val(host, end_of_streak - max_streak / 2); + if (ret) + return ret; + + return sdhci_cdns_tune_blkgap(host->mmc); } static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host, |
