[PATCH 4/9] mmc: dw_mmc: add voltage switch command flag
Kaustabh Chakraborty
kauschluss at disroot.org
Wed Oct 22 22:25:54 CEST 2025
On 2025-10-22 08:14, Peng Fan wrote:
> Hi Kaustabh,
>
> On Fri, Oct 17, 2025 at 08:54:09PM +0530, Kaustabh Chakraborty wrote:
>> During a voltage switch command (CMD11, opcode: SD_CMD_SWITCH_UHS18V),
>> certain hosts tend to stop responding to subsequent commands. This is
>> addressed by introducing an additional command flag,
>> DWMCI_CMD_VOLT_SWITCH.
>
> is there any errata or spec have this information public?
I'm not aware of any. However, this behavior is found in the Linux
kernel
driver. See commit 0173055842cd1 ("mmc: dw_mmc: Support voltage
changes").
The state tracking in the kernel is however much more complex.
>
>>
>> The associated interrupt bit is defined as DWMCI_INTMSK_VOLTSW. This
>> is
>> set high when a voltage switch is issued, this needs to be waited for
>> and set to low. Implement the same in the timeout loop. Do note that
>> since DWMCI_INTMSK_VOLTSW shares the same bit as DWMCI_INTMSK_HTO (bit
>> 10), the interrupt bit needs to be polled for only if the volt switch
>> command is issued.
>>
>> DWMCI_CMD_VOLT_SWITCH also needs to be set for subsequent clken
>> commands
>> after the volt switch. To ensure this, add a boolean member in the
>> host
>> private struct (herein named volt_switching), which informs if the
>> last
>> command issued was for volt switching or not.
>>
>> Signed-off-by: Kaustabh Chakraborty <kauschluss at disroot.org>
>> ---
>> drivers/mmc/dw_mmc.c | 15 +++++++++++++--
>> include/dwmmc.h | 4 ++++
>> 2 files changed, 17 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c
>> index
>> 1aa992c352c3f11ccdd1c02745fa988646952261..94b6641c44c39e67aac453c027d519c0e1580de6
>> 100644
>> --- a/drivers/mmc/dw_mmc.c
>> +++ b/drivers/mmc/dw_mmc.c
>> @@ -419,6 +419,10 @@ static int dwmci_send_cmd_common(struct
>> dwmci_host *host, struct mmc_cmd *cmd,
>> if (cmd->resp_type & MMC_RSP_CRC)
>> flags |= DWMCI_CMD_CHECK_CRC;
>>
>> + host->volt_switching = (cmd->cmdidx == SD_CMD_SWITCH_UHS18V);
[1]
>> + if (host->volt_switching)
>> + flags |= DWMCI_CMD_VOLT_SWITCH;
>> +
>> flags |= cmd->cmdidx | DWMCI_CMD_START | DWMCI_CMD_USE_HOLD_REG;
>>
>> debug("Sending CMD%d\n", cmd->cmdidx);
>> @@ -427,6 +431,10 @@ static int dwmci_send_cmd_common(struct
>> dwmci_host *host, struct mmc_cmd *cmd,
>>
>> for (i = 0; i < retry; i++) {
>> mask = dwmci_readl(host, DWMCI_RINTSTS);
>> + if (host->volt_switching && (mask & DWMCI_INTMSK_VOLTSW)) {
>> + dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_VOLTSW);
>> + break;
>> + }
>> if (mask & DWMCI_INTMSK_CDONE) {
>> if (!data)
>> dwmci_writel(host, DWMCI_RINTSTS, mask);
>> @@ -508,12 +516,15 @@ static int dwmci_control_clken(struct dwmci_host
>> *host, bool on)
>> const u32 val = on ? DWMCI_CLKEN_ENABLE | DWMCI_CLKEN_LOW_PWR : 0;
>> const u32 cmd_only_clk = DWMCI_CMD_PRV_DAT_WAIT | DWMCI_CMD_UPD_CLK;
>> int i, timeout = 10000;
>> - u32 mask;
>> + u32 flags, mask;
>>
>> dwmci_writel(host, DWMCI_CLKENA, val);
>>
>> /* Inform CIU */
>> - dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_START | cmd_only_clk);
>> + flags = DWMCI_CMD_START | cmd_only_clk;
>> + if (host->volt_switching)
>> + flags |= DWMCI_CMD_VOLT_SWITCH;
>> + dwmci_writel(host, DWMCI_CMD, flags);
>>
>> for (i = 0; i < timeout; i++) {
>> mask = dwmci_readl(host, DWMCI_RINTSTS);
>> diff --git a/include/dwmmc.h b/include/dwmmc.h
>> index
>> 639a2d28e7860f2ceb09955ee11550e406fd1bd2..47e3220985e900050d9db9d80e0d45efe6c2e545
>> 100644
>> --- a/include/dwmmc.h
>> +++ b/include/dwmmc.h
>> @@ -72,6 +72,7 @@
>> #define DWMCI_INTMSK_RTO BIT(8)
>> #define DWMCI_INTMSK_DRTO BIT(9)
>> #define DWMCI_INTMSK_HTO BIT(10)
>> +#define DWMCI_INTMSK_VOLTSW BIT(10) /* overlap! */
>> #define DWMCI_INTMSK_FRUN BIT(11)
>> #define DWMCI_INTMSK_HLE BIT(12)
>> #define DWMCI_INTMSK_SBE BIT(13)
>> @@ -104,6 +105,7 @@
>> #define DWMCI_CMD_ABORT_STOP BIT(14)
>> #define DWMCI_CMD_PRV_DAT_WAIT BIT(13)
>> #define DWMCI_CMD_UPD_CLK BIT(21)
>> +#define DWMCI_CMD_VOLT_SWITCH BIT(28)
>> #define DWMCI_CMD_USE_HOLD_REG BIT(29)
>> #define DWMCI_CMD_START BIT(31)
>>
>> @@ -190,6 +192,7 @@ struct dwmci_idmac_regs {
>> * @cfg: Internal MMC configuration, for !CONFIG_BLK cases
>> * @fifo_mode: Use FIFO mode (not DMA) to read and write data
>> * @dma_64bit_address: Whether DMA supports 64-bit address mode or not
>> + * @volt_switching: Whether SD voltage switching is in process or not
>
> Since volt_switching y means in process, I not see it is cleared when
> it is
> not needed. Is this expected?
See [1] marked above. Its enabled when SD_CMD_SWITCH_UHS18V is the
opcode,
disabled otherwise.
>
> Thanks,
> Peng
>
>> * @regs: Registers that can vary for different DW MMC block versions
>> */
>> struct dwmci_host {
>> @@ -229,6 +232,7 @@ struct dwmci_host {
>>
>> bool fifo_mode;
>> bool dma_64bit_address;
>> + bool volt_switching;
>> const struct dwmci_idmac_regs *regs;
>> };
>>
>>
>> --
>> 2.51.0
>>
More information about the U-Boot
mailing list