#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
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.
int max_streak = 0;
int end_of_streak = 0;
int i;
+ int ret;
/*
* Do not execute tuning for UHS_SDR50 or UHS_DDR50.
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,