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

Kathpalia, Tanmay tanmay.kathpalia at altera.com
Fri May 29 19:21:16 CEST 2026


Hi Judith,

Thank you for the information. Let me share my analysis based on
what you have described.

On 5/28/2026 4:52 AM, Judith Mendez wrote:
> 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.

This seems to be the root of the issue. Entering SDR104 mode transitions
the SD card, the IO signal lines, and the host controller all to 1.8V 
signaling.
Once that transition happens, attempting to switch back to HS mode (which
requires 3.3V) is not possible without a full power cycle of the card.

This is explicitly stated in the SD Physical Layer Simplified Specification,
Section "UHS-I Bus Speed Modes Selection Sequence":

   "Once the card enters 1.8V signaling mode, the card cannot be switched
    to SPI mode or 3.3V signaling without power cycle. If the card receives
    CMD0, card returns to Idle state but still works with SDR12 timing."

So the failure you are seeing is not introduced by my patch — the card
was already non-recoverable to 3.3V from the moment it entered SDR104
earlier in the boot.

>
> Also, sd3_bus_mode=0x1f at u-boot stage.

This confirms the card was already operating in 1.8V signaling when your
debug point was reached. For reference, on a fresh cold boot at 3.3V, the
CMD6 available functions table shows sd3_bus_mode should read 0x07
(only SDR12/SDR25/HS bits set). The value 0x1f means bits for SDR50,
SDR104, and DDR50 are also set, which per the spec only happens when the
card is initialized at 1.8V signaling. This is exactly what
mmc_sd_card_using_v18() is designed to detect.

>
> Something weird: from the schematics, its does not seem like the IOs are
> switching to 1.8V with on board hardware PMIC.

This is worth investigating further. If the IO lines are confirmed to be
stuck at 3.3V while the card is operating at 1.8V signaling, that would
itself be a pre-existing hardware or driver issue — the IO voltage and the
card signaling voltage must always be in sync. I would suggest:

1. Verify the voltage regulator driving the IO lines (Vccq) and confirm
whether am654_sdhci_set_ios_post correctly switches it to 1.8V when
UHS modes are selected. If Vccq is not switching, that is a separate
bug in the am654 driver or the regulator configuration.

2. Try using UHS_SDR12 or UHS_SDR25 as the operating mode instead
of HS. Since the card has already transitioned to 1.8V, the UHS-I modes
are the correct and spec-compliant modes to use. Running HS mode at
1.8V is not a valid combination.

>
> 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. 

Correct. Previously, even though the card was operating at 1.8V (as
evidenced by sd3_bus_mode=0x1f), the code was not checking the card's
function group 1 to detect this. So the host stayed at 3.3V while the
card was at 1.8V — an incorrect but silently failing combination on
platforms where the mismatch happens to be tolerated.

With the patch, we correctly detect the 1.8V state and instruct the host
to match. The subsequent failure you observe is because the host-side
IO voltage switch (Vccq) appears to not be working.

> After this, we call am654_sdhci_set_ios_post
> multiple times at mode=0 & signal_voltage=0x2 until mmc_init failure.

mode=0 corresponds to MMC_LEGACY, which is a 3.3V mode. Having
signal_voltage=0x2 (1.8V) alongside MMC_LEGACY is not a valid
combination. This suggests the mode selection logic is landing on
MMC_LEGACY while the voltage has already been switched to 1.8V, which
will always fail. The fix should be to ensure the card operates in a
UHS-I mode when at 1.8V, not fall back to MMC_LEGACY.

>
> 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?

I would prefer to avoid a quirk at this stage, as the underlying behavior
— the card being at 1.8V after a prior boot — is spec-compliant and
real. A quirk would just mask the issue on AM65 rather than fix the actual
IO voltage switching problem.

>
> What are your thoughts? I will continue on this debug
> and come back if I find more useful information.
>
> ~ Judith
>

Hope this helps, looking forward to your further findings.

Thanks,
Tanmay


More information about the U-Boot mailing list