[PATCH] mmc: sd: Handle UHS-I voltage signaling without power cycle

Peng Fan peng.fan at oss.nxp.com
Sat May 16 11:44:31 CEST 2026


Revisit this patch, since it break one board [1].

[1] https://lore.kernel.org/all/52ec8007-ce50-4f12-b796-4b8c2aa1822e@ti.com/

On Tue, Oct 21, 2025 at 01:45:26PM -0700, Tanmay Kathpalia wrote:
>Some boards have SD card connectors where the power rail cannot be switched
>off by the driver. However there are various circumstances when a card
>might be re-initialized, such as after system resume, warm re-boot, or
>error handling. However, a UHS card will continue to use 1.8V signaling
>unless it is power cycled.
>
>If the card has not been power cycled, it may still be using 1.8V
>signaling. According to the SD spec., the Bus Speed Mode (function group 1)
>bits 2 to 4 are zero if the card is initialized at 3.3V signal level. Thus
>they can be used to determine if the card has already switched to 1.8V
>signaling. Detect that situation and try to initialize a UHS-I (1.8V)
>transfer mode.
>
>Signed-off-by: Tanmay Kathpalia <tanmay.kathpalia at altera.com>
>---
> drivers/mmc/mmc.c | 55 ++++++++++++++++++++++++++++++++++++++---------
> include/mmc.h     |  3 +++
> 2 files changed, 48 insertions(+), 10 deletions(-)
>
>diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
>index ec61ed92e86..e1f62a5d0ad 100644
>--- a/drivers/mmc/mmc.c
>+++ b/drivers/mmc/mmc.c
>@@ -643,6 +643,19 @@ static int mmc_switch_voltage(struct mmc *mmc, int signal_voltage)
> 
> 	return 0;
> }
>+
>+static bool mmc_sd_card_using_v18(struct mmc *mmc)
>+{
>+	/*
>+	 * According to the SD spec., the Bus Speed Mode (function group 1) bits
>+	 * 2 to 4 are zero if the card is initialized at 3.3V signal level. Thus
>+	 * they can be used to determine if the card has already switched to
>+	 * 1.8V signaling.
>+	 */
>+	bool volt = mmc->sd3_bus_mode &
>+	       (SD_MODE_UHS_SDR50 | SD_MODE_UHS_SDR104 | SD_MODE_UHS_DDR50);

This is wrong.
sd3_bus_mode is sd supported bits, not the current running bits.

To detect the sd card running bits, need to use: 
(__be32_to_cpu(switch_status[4]) >> 24) & 0xF

>+	return volt;
>+}
> #endif
> 
> static int sd_send_op_cond(struct mmc *mmc, bool uhs_en)
>@@ -1369,9 +1382,6 @@ static int sd_get_capabilities(struct mmc *mmc)
> 	ALLOC_CACHE_ALIGN_BUFFER(__be32, switch_status, 16);
> 	struct mmc_data data;
> 	int timeout;
>-#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
>-	u32 sd3_bus_mode;
>-#endif
> 
> 	mmc->card_caps = MMC_MODE_1BIT | MMC_CAP(MMC_LEGACY);
> 
>@@ -1451,16 +1461,16 @@ static int sd_get_capabilities(struct mmc *mmc)
> 	if (mmc->version < SD_VERSION_3)
> 		return 0;
> 
>-	sd3_bus_mode = __be32_to_cpu(switch_status[3]) >> 16 & 0x1f;
>-	if (sd3_bus_mode & SD_MODE_UHS_SDR104)
>+	mmc->sd3_bus_mode = __be32_to_cpu(switch_status[3]) >> 16 & 0x1f;
>+	if (mmc->sd3_bus_mode & SD_MODE_UHS_SDR104)
> 		mmc->card_caps |= MMC_CAP(UHS_SDR104);
>-	if (sd3_bus_mode & SD_MODE_UHS_SDR50)
>+	if (mmc->sd3_bus_mode & SD_MODE_UHS_SDR50)
> 		mmc->card_caps |= MMC_CAP(UHS_SDR50);
>-	if (sd3_bus_mode & SD_MODE_UHS_SDR25)
>+	if (mmc->sd3_bus_mode & SD_MODE_UHS_SDR25)
> 		mmc->card_caps |= MMC_CAP(UHS_SDR25);
>-	if (sd3_bus_mode & SD_MODE_UHS_SDR12)
>+	if (mmc->sd3_bus_mode & SD_MODE_UHS_SDR12)
> 		mmc->card_caps |= MMC_CAP(UHS_SDR12);
>-	if (sd3_bus_mode & SD_MODE_UHS_DDR50)
>+	if (mmc->sd3_bus_mode & SD_MODE_UHS_DDR50)
> 		mmc->card_caps |= MMC_CAP(UHS_DDR50);
> #endif
> 
>@@ -1830,7 +1840,11 @@ static int sd_select_mode_and_width(struct mmc *mmc, uint card_caps)
> 	uint widths[] = {MMC_MODE_4BIT, MMC_MODE_1BIT};
> 	const struct mode_width_tuning *mwt;
> #if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
>-	bool uhs_en = (mmc->ocr & OCR_S18R) ? true : false;
>+	/*
>+	 * Enable UHS mode if the card advertises 1.8V support (S18R in OCR)
>+	 * or is already operating at 1.8V signaling.
>+	 */
>+	bool uhs_en = (mmc->ocr & OCR_S18R) || mmc_sd_card_using_v18(mmc);

In theory, 

bool uhs_en = (mmc->ocr & OCR_S18R) ? true : false; is correct.

OCR (including S18R/S18A) only reflects voltage switch negotiation
capability and intent, not the current signaling voltage. So your sd card
should have OCR_S18R returned per my understanding.

> #else
> 	bool uhs_en = false;
> #endif
>@@ -2701,6 +2715,27 @@ static int mmc_startup(struct mmc *mmc)
> 		err = sd_get_capabilities(mmc);
> 		if (err)
> 			return err;
>+
>+#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
>+		/*
>+		 * If the card has already switched to 1.8V signaling, then
>+		 * set the signal voltage to 1.8V.
>+		 */
>+		if (mmc_sd_card_using_v18(mmc)) {

To switch voltage, ACMD41 is required, see mmc_switch_voltage.
Forcing switch to 1.8 here has some risk.

>+			/*
>+			 * During a signal voltage level switch, the clock must be gated
>+			 * for 5 ms according to the SD spec.
>+			 */
>+			mmc_set_clock(mmc, mmc->clock, MMC_CLK_DISABLE);
>+			err = mmc_set_signal_voltage(mmc, MMC_SIGNAL_VOLTAGE_180);
>+			if (err)
>+				return err;
>+			/* Keep clock gated for at least 10 ms, though spec only says 5 ms */
>+			mdelay(10);
>+			mmc_set_clock(mmc, mmc->clock, MMC_CLK_ENABLE);
>+		}
>+#endif

A proper redesign is required. So I am going to revert this patch.

Thanks
Peng


More information about the U-Boot mailing list