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?

>
>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 <[email protected]>
>---
> 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);
>+      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?

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
>

Reply via email to