[PATCH] mmc: sd: Handle UHS-I voltage signaling without power cycle
Judith Mendez
jm at ti.com
Thu May 28 01:22:19 CEST 2026
Hi Kathpalia,
On 5/16/26 6:44 AM, Kathpalia, Tanmay wrote:
> Hi Peng,
> Thank you for reviewing and for the detailed feedback. Apologies for
> the resend — my previous reply had an incorrect timestamp due to a
> timezone misconfiguration, which caused it to appear out of order in
> the thread.
>
> On 5/16/2026 3:14 PM, Peng Fan wrote:
>> Revisit this patch, since it break one board [1].
>>
>> [1] https://lore.kernel.org/all/52ec8007-ce50-4f12-
>> b796-4b8c2aa1822e at 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
>
> You are correct that sd3_bus_mode stores the supported bits - as can be
> seen in sd_get_capabilities() where it is filled from the CMD6 status
> response:
>
> mmc->sd3_bus_mode = __be32_to_cpu(switch_status[3]) >> 16 & 0x1f;
>
> However, the intent here is not to read the currently active function
> code, but to use the supported bits as a proxy for the signaling voltage
> level.
>
> According to the SD Physical Layer Simplified Specification v9.0,
> Section 4.3.10.4 (Switch Function Command, CMD6): the UHS-I speed modes
> SDR50, SDR104, and DDR50 (function group 1 bits 2–4) are only available
> when the card is operating at 1.8V signaling. When the card is
> initialized at 3.3V, those bits read as zero. Therefore, if any of bits
> 2-4 are set in the supported field, we can safely infer the card is a
> UHS-I card and, when not power cycled, retains 1.8V signaling.
>
> Using switch_status[4] to read the currently active function would
> actually NOT work for this scenario. After a warm reboot, the card
> receives CMD0 (GO_IDLE_STATE), which resets the card's selected function
> back to 0 (SDR12/default) — even though the 1.8V signaling level is
> retained. So switch_status[4] would always return 0 in this path,
> making it unsuitable as a 1.8V indicator here.
>
>>> + 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.
>
> I think there is a gap in understanding here. The scenario this patch
> targets is warm reboot or system resume - the card was never power cycled,
> so it is still operating at 1.8V signaling.
>
> In that situation, when ACMD41 is re-issued, the card will NOT assert
> S18A (Switching to 1.8V Accepted) in the OCR response, because the
> voltage negotiation already happened in the previous session and the card
> has no need to re-negotiate. Per the SD Physical Layer Simplified
> Specification v9.0, S18A is set only during the initial 1.8V request
> handshake. A card already running at 1.8V will not set S18A again on a
> subsequent ACMD41 after warm reset.
>
> This is exactly the corner case: OCR_S18R/S18A will be zero, yet the card
> is already at 1.8V. The existing code misses this entirely.
>
>>> #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.
>
> Agreed - ACMD41 is required to initiate a voltage switch on a card that is
> currently at 3.3V. However, in this path the card has already completed
> the voltage switch in a prior session and is still running at 1.8V. There
> is no card-side voltage transition happening; only the host controller
> needs to be reconfigured to match the 1.8V signaling level the card
> retained. Sending ACMD41 again in this context would be incorrect, as the
> card is not in a state to re-negotiate voltage.
>
>>> + /*
>>> + * 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.
>>
>
> I would respectfully ask to reconsider before reverting. This patch is
> specifically targeted at the warm reboot / system resume / error recovery
> scenario where the card is not power cycled. The check in
> mmc_sd_card_using_v18() acts as a guard - it only triggers when the
> card is already at 1.8V and the normal OCR path did not catch it. On
> a cold boot with a fresh 3.3V card, the UHS supported bits will be zero
> and this path is never entered, so it cannot regress cold-boot behavior
> for any board.
>
> Regarding the reported regression [1], Judith mentioned that he will
> debug once he is back. If possible, let us wait for him to conclude
> before deciding to revert.
So I started looking at this. So far, I have found that on AM65 u-boot,
we had been enumerating to SDR104 mode once during boot and finally
resolve to HS mode right before loading the kernel.
Also, sd3_bus_mode=0x1f at u-boot stage.
Something weird: from the schematics, its does not seem like the IOs are
switching to 1.8V with on board hardware PMIC.
It seems like the call stack has changed where previously
uhs_en=0 in u-boot and now with your commit uhs_en=1, right before
loading the kernel. After this, we call am654_sdhci_set_ios_post
multiple times at mode=0 & signal_voltage=0x2 until mmc_init failure.
I realize this is not the most stable platform and there could
be hardware issues for SD. Perhaps it is a good idea to create
a quirk to skip over this section for these kinds of platforms?
What are your thoughts? I will continue on this debug
and come back if I find more useful information.
~ Judith
>
> This problem is not unique to U-Boot. Similar discussions and solutions
> have been proposed in the Linux community:
>
> https://lore.kernel.org/linux-mmc/a367a679-28f7-898a-
> c043-27df8c9c9aba at intel.com/
> https://lore.kernel.org/linux-mmc/1506328144-13666-1-git-send-email-
> adrian.hunter at intel.com/
>
> I am happy to rework this in v2 with a tighter guard condition (already
> addressed in [2]) once we have more information from the regression
> report.
>
> Please let me know your thoughts.
>
> Thanks,
> Tanmay
More information about the U-Boot
mailing list