[PATCH] Revert "mmc: sdhci-cadence: trigger tuning for SD HS mode on SD6HC (v6) PHY"

Tanmay Kathpalia tanmay.kathpalia at altera.com
Sun Jun 28 20:57:51 CEST 2026


This reverts commit b42c67188c14 ("mmc: sdhci-cadence: trigger tuning
for SD HS mode on SD6HC (v6) PHY").

The reverted patch added runtime DLL delay calibration for SD High
Speed mode on the Cadence SD6HC (v6) PHY, gated by the device tree
property "cdns,sd-hs-tuning". Reviewer feedback identified issues with
the implementation, but the patch was merged without those concerns
being adequately addressed or justified.

Reverting to realign the driver with the IP documentation and the SD
Physical Layer Specification, as the underlying issue requires a
different solution approach.

Signed-off-by: Tanmay Kathpalia <tanmay.kathpalia at altera.com>
---
 drivers/mmc/sdhci-cadence.c  | 108 +----------------------------------
 drivers/mmc/sdhci-cadence.h  |   5 --
 drivers/mmc/sdhci-cadence6.c |  45 +--------------
 3 files changed, 2 insertions(+), 156 deletions(-)

diff --git a/drivers/mmc/sdhci-cadence.c b/drivers/mmc/sdhci-cadence.c
index a76f9e8d6bd..5bbc18dfa51 100644
--- a/drivers/mmc/sdhci-cadence.c
+++ b/drivers/mmc/sdhci-cadence.c
@@ -39,9 +39,6 @@ static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = {
 	{ "cdns,phy-dll-delay-strobe", SDHCI_CDNS_PHY_DLY_STROBE, },
 };
 
-static int __maybe_unused sdhci_cdns_execute_tuning(struct udevice *dev,
-						    unsigned int opcode);
-
 static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_plat *plat,
 				    u8 addr, u8 data)
 {
@@ -158,93 +155,8 @@ static void sdhci_cdns_set_control_reg(struct sdhci_host *host)
 		sdhci_cdns6_phy_adj(mmc->dev, plat, mmc->selected_mode);
 }
 
-static __maybe_unused bool sdhci_cdns_sd_needs_tuning(struct mmc *mmc)
-{
-	struct sdhci_cdns_plat *plat = dev_get_plat(mmc->dev);
-
-	if (!IS_SD(mmc))
-		return false;
-
-	if (!dev_read_bool(mmc->dev, "cdns,sd-hs-tuning"))
-		return false;
-
-	/* Already tuned for this mode */
-	if (plat->tuned_mode == mmc->selected_mode)
-		return false;
-
-	switch (mmc->selected_mode) {
-	case SD_HS:
-		return mmc->bus_width == 4;
-	/* Add future modes here, e.g.:
-	 * case UHS_SDR50:
-	 *	return true;
-	 */
-	default:
-		return false;
-	}
-}
-
-static int sdhci_cdns_set_ios_post(struct sdhci_host *host)
-{
-	struct mmc *mmc = host->mmc;
-	struct sdhci_cdns_plat *plat = dev_get_plat(mmc->dev);
-	int ret __maybe_unused;
-	/*
-	 * The SD6HC soft PHY requires runtime DLL delay calibration
-	 * for SD High Speed mode. The default PHY_DLL_SLAVE_CTRL_REG
-	 * values (READ_DQS_CMD_DELAY and READ_DQS_DELAY = 0) do not
-	 * provide sufficient timing margin due to PVT and board trace
-	 * variations.
-	 *
-	 * Tuning is performed once per entry into SD_HS mode
-	 * (tracked by plat->tuned_mode state). The calibrated PHY delay
-	 * values remain valid while the card stays in SD_HS mode, and
-	 * leaving that tuned mode clears the state so re-entering SD_HS
-	 * triggers tuning again.
-	 *
-	 * This must be done in set_ios_post (not set_control_reg)
-	 * because the SDHCI controller must already be operating at
-	 * the target bus width, clock, and speed mode before CMD19
-	 * tuning commands can succeed.
-	 */
-
-	if (IS_ENABLED(CONFIG_MMC_SUPPORTS_TUNING)) {
-		if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_420 &&
-		    sdhci_cdns_sd_needs_tuning(mmc)) {
-			ret = sdhci_cdns_execute_tuning(mmc->dev,
-							MMC_CMD_SEND_TUNING_BLOCK);
-			if (ret) {
-				dev_err(mmc->dev,
-					"SD_HS tuning failed (ret=%d), using default PHY\n",
-					ret);
-				/* Restore default PHY settings and avoid retrying in this mode */
-				sdhci_cdns6_phy_adj(mmc->dev, plat,
-						    mmc->selected_mode);
-				plat->tuned_mode = mmc->selected_mode;
-				plat->tuned_dll_slave_ctrl = sdhci_cdns6_phy_get_dll_slave(plat);
-				return 0;
-			}
-			/*
-			 * Tuning succeeded. The tuned_mode is already set by
-			 * execute_tuning(), so the tuned value will be preserved
-			 * across subsequent PHY reconfigurations.
-			 */
-			dev_dbg(mmc->dev, "SD_HS tuning successful\n");
-		}
-
-		/* Reset when mode changes away from a tuned mode */
-		if (mmc->selected_mode != plat->tuned_mode) {
-			plat->tuned_mode = MMC_MODES_END;
-			plat->tuned_dll_slave_ctrl = 0;
-		}
-	}
-
-	return 0;
-}
-
 static const struct sdhci_ops sdhci_cdns_ops = {
 	.set_control_reg = sdhci_cdns_set_control_reg,
-	.set_ios_post = sdhci_cdns_set_ios_post,
 };
 
 static int sdhci_cdns_set_tune_val(struct sdhci_cdns_plat *plat,
@@ -292,7 +204,6 @@ static int __maybe_unused sdhci_cdns_execute_tuning(struct udevice *dev,
 	int cur_streak = 0;
 	int max_streak = 0;
 	int end_of_streak = 0;
-	int ret;
 	int i;
 
 	/*
@@ -318,24 +229,7 @@ static int __maybe_unused sdhci_cdns_execute_tuning(struct udevice *dev,
 		return -EIO;
 	}
 
-	ret = sdhci_cdns_set_tune_val(plat, end_of_streak - max_streak / 2);
-	if (ret)
-		return ret;
-
-	/*
-	 * Mark this mode as tuned. This is critical for both driver tuning
-	 * (SD_HS via set_ios_post) and framework tuning (UHS_SDR104, MMC_HS_200,
-	 * MMC_HS_400) so that subsequent PHY reconfigurations restore the
-	 * calibrated DLL value instead of overwriting with DT defaults.
-	 *
-	 * For HS400, tuning is performed while the controller is in HS200 mode
-	 * (mmc->selected_mode == MMC_HS_200 and mmc->hs400_tuning == true).
-	 * Record the tuned mode as MMC_HS_400 so the calibrated DLL value is
-	 * preserved across the HS200→HS400 transition.
-	 */
-	plat->tuned_mode = mmc->hs400_tuning ? MMC_HS_400 : mmc->selected_mode;
-
-	return 0;
+	return sdhci_cdns_set_tune_val(plat, end_of_streak - max_streak / 2);
 }
 
 static struct dm_mmc_ops sdhci_cdns_mmc_ops;
diff --git a/drivers/mmc/sdhci-cadence.h b/drivers/mmc/sdhci-cadence.h
index ea517491860..7101f00b75b 100644
--- a/drivers/mmc/sdhci-cadence.h
+++ b/drivers/mmc/sdhci-cadence.h
@@ -7,8 +7,6 @@
 #ifndef SDHCI_CADENCE_H_
 #define SDHCI_CADENCE_H_
 
-#include <mmc.h>
-
 /* HRS - Host Register Set (specific to Cadence) */
 /* PHY access port */
 #define SDHCI_CDNS_HRS04		0x10
@@ -62,13 +60,10 @@ struct sdhci_cdns_plat {
 	struct mmc_config cfg;
 	struct mmc mmc;
 	void __iomem *hrs_addr;
-	enum bus_mode tuned_mode;
-	u32 tuned_dll_slave_ctrl;
 };
 
 int sdhci_cdns6_phy_adj(struct udevice *dev, struct sdhci_cdns_plat *plat, u32 mode);
 int sdhci_cdns6_phy_init(struct udevice *dev, struct sdhci_cdns_plat *plat);
 int sdhci_cdns6_set_tune_val(struct sdhci_cdns_plat *plat, unsigned int val);
-u32 sdhci_cdns6_phy_get_dll_slave(struct sdhci_cdns_plat *plat);
 
 #endif
diff --git a/drivers/mmc/sdhci-cadence6.c b/drivers/mmc/sdhci-cadence6.c
index c8b42532e17..ca1086e2359 100644
--- a/drivers/mmc/sdhci-cadence6.c
+++ b/drivers/mmc/sdhci-cadence6.c
@@ -173,30 +173,6 @@ static void sdhci_cdns6_write_phy_reg(struct sdhci_cdns_plat *plat, u32 addr, u3
 	writel(val, plat->hrs_addr + SDHCI_CDNS_HRS05);
 }
 
-static bool sdhci_cdns6_mode_is_tuned(struct sdhci_cdns_plat *plat, u32 mode)
-{
-	/*
-	 * Check if the given mode has a valid tuned DLL value.
-	 * Only modes that support tuning (driver or framework) can have
-	 * valid tuned values. This prevents the initial state (tuned_mode=0)
-	 * from falsely matching MMC_LEGACY.
-	 */
-	if (plat->tuned_mode != mode)
-		return false;
-
-	switch (mode) {
-	case SD_HS:		/* Driver tuning via set_ios_post */
-	case UHS_SDR50:		/* Future driver tuning support */
-	case UHS_SDR104:	/* Framework tuning */
-	case MMC_HS_200:	/* Framework tuning */
-	case MMC_HS_400:	/* Framework tuning */
-	case MMC_HS_400_ES:	/* Framework tuning */
-		return true;
-	default:
-		return false;
-	}
-}
-
 static int sdhci_cdns6_reset_phy_dll(struct sdhci_cdns_plat *plat, bool reset)
 {
 	void __iomem *reg = plat->hrs_addr + SDHCI_CDNS_HRS09;
@@ -283,18 +259,7 @@ int sdhci_cdns6_phy_adj(struct udevice *dev, struct sdhci_cdns_plat *plat, u32 m
 	sdhci_cdns6_write_phy_reg(plat, PHY_DQS_TIMING_REG_ADDR, sdhci_cdns6_phy_cfgs[0].val);
 	sdhci_cdns6_write_phy_reg(plat, PHY_GATE_LPBK_CTRL_REG_ADDR, sdhci_cdns6_phy_cfgs[1].val);
 	sdhci_cdns6_write_phy_reg(plat, PHY_DLL_MASTER_CTRL_REG_ADDR, sdhci_cdns6_phy_cfgs[4].val);
-	if (sdhci_cdns6_mode_is_tuned(plat, mode)) {
-		/*
-		 * Use previously saved tuned DLL slave control value.
-		 * Note: 0 is a valid tuned value (e.g., optimal tap at position 0),
-		 * so we check both mode match AND that it's a tunable mode.
-		 */
-		sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR,
-					  plat->tuned_dll_slave_ctrl);
-	} else {
-		sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR,
-					  sdhci_cdns6_phy_cfgs[2].val);
-	}
+	sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR, sdhci_cdns6_phy_cfgs[2].val);
 
 	/* Switch Off the DLL Reset */
 	ret = sdhci_cdns6_reset_phy_dll(plat, false);
@@ -353,9 +318,6 @@ int sdhci_cdns6_set_tune_val(struct sdhci_cdns_plat *plat, unsigned int val)
 
 	sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR, tmp);
 
-	/* Store tuned DLL slave control value which will be reapplied via set_ios(). */
-	plat->tuned_dll_slave_ctrl = tmp;
-
 	/* Switch Off the DLL Reset */
 	ret = sdhci_cdns6_reset_phy_dll(plat, false);
 	if (ret) {
@@ -365,8 +327,3 @@ int sdhci_cdns6_set_tune_val(struct sdhci_cdns_plat *plat, unsigned int val)
 
 	return 0;
 }
-
-u32 sdhci_cdns6_phy_get_dll_slave(struct sdhci_cdns_plat *plat)
-{
-	return sdhci_cdns6_read_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR);
-}
-- 
2.43.7



More information about the U-Boot mailing list