[PATCH v5 06/20] mmc: am654_sdhci: Add support for input tap delay

Aswath Govindraju a-govindraju at ti.com
Fri Jan 29 15:47:28 CET 2021


From: Faiz Abbas <faiz_abbas at ti.com>

DLL need only be enabled for speed modes and clock frequencies at or
above 50 MHz. For speed modes that don't enable the DLL, we need to
configure a static input delay value. This involves reading an optional
itap-del-sel-* value from the device tree and configuring it for the
appropriate speed mode.

Therefore, move all dll configurations to their own functions and gate it
with 50 MHz speed and a minimum mode. If both these conditions are not
satisfied then configure delay chain modes.

Signed-off-by: Faiz Abbas <faiz_abbas at ti.com>
Signed-off-by: Aswath Govindraju <a-govindraju at ti.com>
Reviewed-by: Jaehoon Chung <jh80.chung at samsung.com>
---
 drivers/mmc/am654_sdhci.c | 241 +++++++++++++++++++++++++-------------
 1 file changed, 161 insertions(+), 80 deletions(-)

diff --git a/drivers/mmc/am654_sdhci.c b/drivers/mmc/am654_sdhci.c
index 5790fa3d0dbf..b82558254ebb 100644
--- a/drivers/mmc/am654_sdhci.c
+++ b/drivers/mmc/am654_sdhci.c
@@ -62,6 +62,16 @@
 #define CALDONE_MASK		BIT(CALDONE_SHIFT)
 #define RETRIM_SHIFT		17
 #define RETRIM_MASK		BIT(RETRIM_SHIFT)
+#define SELDLYTXCLK_SHIFT	17
+#define SELDLYTXCLK_MASK	BIT(SELDLYTXCLK_SHIFT)
+#define SELDLYRXCLK_SHIFT	16
+#define SELDLYRXCLK_MASK	BIT(SELDLYRXCLK_SHIFT)
+#define ITAPDLYSEL_SHIFT	0
+#define ITAPDLYSEL_MASK		GENMASK(4, 0)
+#define ITAPDLYENA_SHIFT	8
+#define ITAPDLYENA_MASK		BIT(ITAPDLYENA_SHIFT)
+#define ITAPCHGWIN_SHIFT	9
+#define ITAPCHGWIN_MASK		BIT(ITAPCHGWIN_SHIFT)
 
 #define DRIVER_STRENGTH_50_OHM	0x0
 #define DRIVER_STRENGTH_33_OHM	0x1
@@ -70,6 +80,7 @@
 #define DRIVER_STRENGTH_40_OHM	0x4
 
 #define AM654_SDHCI_MIN_FREQ	400000
+#define CLOCK_TOO_SLOW_HZ	50000000
 
 struct am654_sdhci_plat {
 	struct mmc_config cfg;
@@ -77,6 +88,7 @@ struct am654_sdhci_plat {
 	struct regmap *base;
 	bool non_removable;
 	u32 otap_del_sel[MMC_MODES_END];
+	u32 itap_del_sel[MMC_MODES_END];
 	u32 trm_icp;
 	u32 drv_strength;
 	u32 strb_sel;
@@ -89,22 +101,45 @@ struct am654_sdhci_plat {
 };
 
 struct timing_data {
-	const char *binding;
+	const char *otap_binding;
+	const char *itap_binding;
 	u32 capability;
 };
 
 static const struct timing_data td[] = {
-	[MMC_LEGACY] = {"ti,otap-del-sel-legacy", 0},
-	[MMC_HS] = {"ti,otap-del-sel-mmc-hs", MMC_CAP(MMC_HS)},
-	[SD_HS]  = {"ti,otap-del-sel-sd-hs", MMC_CAP(SD_HS)},
-	[UHS_SDR12] = {"ti,otap-del-sel-sdr12", MMC_CAP(UHS_SDR12)},
-	[UHS_SDR25] = {"ti,otap-del-sel-sdr25", MMC_CAP(UHS_SDR25)},
-	[UHS_SDR50] = {"ti,otap-del-sel-sdr50", MMC_CAP(UHS_SDR50)},
-	[UHS_SDR104] = {"ti,otap-del-sel-sdr104", MMC_CAP(UHS_SDR104)},
-	[UHS_DDR50] = {"ti,otap-del-sel-ddr50", MMC_CAP(UHS_DDR50)},
-	[MMC_DDR_52] = {"ti,otap-del-sel-ddr52", MMC_CAP(MMC_DDR_52)},
-	[MMC_HS_200] = {"ti,otap-del-sel-hs200", MMC_CAP(MMC_HS_200)},
-	[MMC_HS_400] = {"ti,otap-del-sel-hs400", MMC_CAP(MMC_HS_400)},
+	[MMC_LEGACY]	= {"ti,otap-del-sel-legacy",
+			   "ti,itap-del-sel-legacy",
+			   0},
+	[MMC_HS]	= {"ti,otap-del-sel-mmc-hs",
+			   "ti,itap-del-sel-mms-hs",
+			   MMC_CAP(MMC_HS)},
+	[SD_HS]		= {"ti,otap-del-sel-sd-hs",
+			   "ti,itap-del-sel-sd-hs",
+			   MMC_CAP(SD_HS)},
+	[UHS_SDR12]	= {"ti,otap-del-sel-sdr12",
+			   "ti,itap-del-sel-sdr12",
+			   MMC_CAP(UHS_SDR12)},
+	[UHS_SDR25]	= {"ti,otap-del-sel-sdr25",
+			   "ti,itap-del-sel-sdr25",
+			   MMC_CAP(UHS_SDR25)},
+	[UHS_SDR50]	= {"ti,otap-del-sel-sdr50",
+			   NULL,
+			   MMC_CAP(UHS_SDR50)},
+	[UHS_SDR104]	= {"ti,otap-del-sel-sdr104",
+			   NULL,
+			   MMC_CAP(UHS_SDR104)},
+	[UHS_DDR50]	= {"ti,otap-del-sel-ddr50",
+			   NULL,
+			   MMC_CAP(UHS_DDR50)},
+	[MMC_DDR_52]	= {"ti,otap-del-sel-ddr52",
+			   "ti,itap-del-sel-ddr52",
+			   MMC_CAP(MMC_DDR_52)},
+	[MMC_HS_200]	= {"ti,otap-del-sel-hs200",
+			   NULL,
+			   MMC_CAP(MMC_HS_200)},
+	[MMC_HS_400]	= {"ti,otap-del-sel-hs400",
+			   NULL,
+			   MMC_CAP(MMC_HS_400)},
 };
 
 struct am654_driver_data {
@@ -127,12 +162,99 @@ static void am654_sdhci_set_control_reg(struct sdhci_host *host)
 	sdhci_set_uhs_timing(host);
 }
 
+static int am654_sdhci_setup_dll(struct am654_sdhci_plat *plat,
+				 unsigned int speed)
+{
+	int sel50, sel100, freqsel;
+	u32 mask, val;
+	int ret;
+
+	/* Disable delay chain mode */
+	regmap_update_bits(plat->base, PHY_CTRL5,
+			   SELDLYTXCLK_MASK | SELDLYRXCLK_MASK, 0);
+
+	if (plat->flags & FREQSEL_2_BIT) {
+		switch (speed) {
+		case 200000000:
+			sel50 = 0;
+			sel100 = 0;
+			break;
+		case 100000000:
+			sel50 = 0;
+			sel100 = 1;
+			break;
+		default:
+			sel50 = 1;
+			sel100 = 0;
+		}
+
+		/* Configure PHY DLL frequency */
+		mask = SEL50_MASK | SEL100_MASK;
+		val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT);
+		regmap_update_bits(plat->base, PHY_CTRL5, mask, val);
+	} else {
+		switch (speed) {
+		case 200000000:
+			freqsel = 0x0;
+			break;
+		default:
+			freqsel = 0x4;
+		}
+		regmap_update_bits(plat->base, PHY_CTRL5, FREQSEL_MASK,
+				   freqsel << FREQSEL_SHIFT);
+	}
+
+	/* Configure DLL TRIM */
+	mask = DLL_TRIM_ICP_MASK;
+	val = plat->trm_icp << DLL_TRIM_ICP_SHIFT;
+
+	/* Configure DLL driver strength */
+	mask |= DR_TY_MASK;
+	val |= plat->drv_strength << DR_TY_SHIFT;
+	regmap_update_bits(plat->base, PHY_CTRL1, mask, val);
+
+	/* Enable DLL */
+	regmap_update_bits(plat->base, PHY_CTRL1, ENDLL_MASK,
+			   0x1 << ENDLL_SHIFT);
+	/*
+	 * Poll for DLL ready. Use a one second timeout.
+	 * Works in all experiments done so far
+	 */
+	ret = regmap_read_poll_timeout(plat->base, PHY_STAT1, val,
+				       val & DLLRDY_MASK, 1000, 1000000);
+
+	return ret;
+}
+
+static void am654_sdhci_write_itapdly(struct am654_sdhci_plat *plat,
+				      u32 itapdly)
+{
+	/* Set ITAPCHGWIN before writing to ITAPDLY */
+	regmap_update_bits(plat->base, PHY_CTRL4, ITAPCHGWIN_MASK,
+			   1 << ITAPCHGWIN_SHIFT);
+	regmap_update_bits(plat->base, PHY_CTRL4, ITAPDLYSEL_MASK,
+			   itapdly << ITAPDLYSEL_SHIFT);
+	regmap_update_bits(plat->base, PHY_CTRL4, ITAPCHGWIN_MASK, 0);
+}
+
+static void am654_sdhci_setup_delay_chain(struct am654_sdhci_plat *plat,
+					  int mode)
+{
+	u32 mask, val;
+
+	val = 1 << SELDLYTXCLK_SHIFT | 1 << SELDLYRXCLK_SHIFT;
+	mask = SELDLYTXCLK_MASK | SELDLYRXCLK_MASK;
+	regmap_update_bits(plat->base, PHY_CTRL5, mask, val);
+
+	am654_sdhci_write_itapdly(plat, plat->itap_del_sel[mode]);
+}
+
 static int am654_sdhci_set_ios_post(struct sdhci_host *host)
 {
 	struct udevice *dev = host->mmc->dev;
 	struct am654_sdhci_plat *plat = dev_get_plat(dev);
 	unsigned int speed = host->mmc->clock;
-	int sel50, sel100, freqsel;
+	int mode = host->mmc->selected_mode;
 	u32 otap_del_sel;
 	u32 mask, val;
 	int ret;
@@ -148,75 +270,29 @@ static int am654_sdhci_set_ios_post(struct sdhci_host *host)
 	sdhci_set_clock(host->mmc, speed);
 
 	/* switch phy back on */
-	if (speed > AM654_SDHCI_MIN_FREQ) {
-		otap_del_sel = plat->otap_del_sel[host->mmc->selected_mode];
-		mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
-		val = (1 << OTAPDLYENA_SHIFT) |
-		      (otap_del_sel << OTAPDLYSEL_SHIFT);
-
-		/* Write to STRBSEL for HS400 speed mode */
-		if (host->mmc->selected_mode == MMC_HS_400) {
-			if (plat->flags & STRBSEL_4_BIT)
-				mask |= STRBSEL_4BIT_MASK;
-			else
-				mask |= STRBSEL_8BIT_MASK;
-
-			val |= plat->strb_sel << STRBSEL_SHIFT;
-		}
+	otap_del_sel = plat->otap_del_sel[mode];
+	mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
+	val = (1 << OTAPDLYENA_SHIFT) |
+	      (otap_del_sel << OTAPDLYSEL_SHIFT);
 
-		regmap_update_bits(plat->base, PHY_CTRL4, mask, val);
-
-		if (plat->flags & FREQSEL_2_BIT) {
-			switch (speed) {
-			case 200000000:
-				sel50 = 0;
-				sel100 = 0;
-				break;
-			case 100000000:
-				sel50 = 0;
-				sel100 = 1;
-				break;
-			default:
-				sel50 = 1;
-				sel100 = 0;
-			}
-
-			/* Configure PHY DLL frequency */
-			mask = SEL50_MASK | SEL100_MASK;
-			val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT);
-			regmap_update_bits(plat->base, PHY_CTRL5, mask, val);
-		} else {
-			switch (speed) {
-			case 200000000:
-				freqsel = 0x0;
-				break;
-			default:
-				freqsel = 0x4;
-			}
-			regmap_update_bits(plat->base, PHY_CTRL5, FREQSEL_MASK,
-					   freqsel << FREQSEL_SHIFT);
-		}
+	/* Write to STRBSEL for HS400 speed mode */
+	if (host->mmc->selected_mode == MMC_HS_400) {
+		if (plat->flags & STRBSEL_4_BIT)
+			mask |= STRBSEL_4BIT_MASK;
+		else
+			mask |= STRBSEL_8BIT_MASK;
 
-		/* Configure DLL TRIM */
-		mask = DLL_TRIM_ICP_MASK;
-		val = plat->trm_icp << DLL_TRIM_ICP_SHIFT;
-
-		/* Configure DLL driver strength */
-		mask |= DR_TY_MASK;
-		val |= plat->drv_strength << DR_TY_SHIFT;
-		regmap_update_bits(plat->base, PHY_CTRL1, mask, val);
-
-		/* Enable DLL */
-		regmap_update_bits(plat->base, PHY_CTRL1, ENDLL_MASK,
-				   0x1 << ENDLL_SHIFT);
-		/*
-		 * Poll for DLL ready. Use a one second timeout.
-		 * Works in all experiments done so far
-		 */
-		ret = regmap_read_poll_timeout(plat->base, PHY_STAT1, val,
-					 val & DLLRDY_MASK, 1000, 1000000);
+		val |= plat->strb_sel << STRBSEL_SHIFT;
+	}
+
+	regmap_update_bits(plat->base, PHY_CTRL4, mask, val);
+
+	if (mode > UHS_SDR25 && speed >= CLOCK_TOO_SLOW_HZ) {
+		ret = am654_sdhci_setup_dll(plat, speed);
 		if (ret)
 			return ret;
+	} else {
+		am654_sdhci_setup_delay_chain(plat, mode);
 	}
 
 	return 0;
@@ -354,15 +430,20 @@ static int sdhci_am654_get_otap_delay(struct udevice *dev,
 	 * value is not found
 	 */
 	for (i = MMC_HS; i <= MMC_HS_400; i++) {
-		ret = dev_read_u32(dev, td[i].binding, &plat->otap_del_sel[i]);
+		ret = dev_read_u32(dev, td[i].otap_binding,
+				   &plat->otap_del_sel[i]);
 		if (ret) {
-			dev_dbg(dev, "Couldn't find %s\n", td[i].binding);
+			dev_dbg(dev, "Couldn't find %s\n", td[i].otap_binding);
 			/*
 			 * Remove the corresponding capability
 			 * if an otap-del-sel value is not found
 			 */
 			cfg->host_caps &= ~td[i].capability;
 		}
+
+		if (td[i].itap_binding)
+			dev_read_u32(dev, td[i].itap_binding,
+				     &plat->itap_del_sel[i]);
 	}
 
 	return 0;
-- 
2.17.1



More information about the U-Boot mailing list