mmc: mmci_sdmmc: Implement signal voltage callbacks
authorLudovic Barre <ludovic.barre@st.com>
Tue, 28 Jan 2020 09:06:35 +0000 (10:06 +0100)
committerUlf Hansson <ulf.hansson@linaro.org>
Tue, 24 Mar 2020 13:35:40 +0000 (14:35 +0100)
To prepare the voltage switch procedure, the VSWITCHEN bit must be set
before sending the CMD11. To confirm completion of voltage switch, the
VSWEND flag must be checked.

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
Link: https://lore.kernel.org/r/20200128090636.13689-9-ludovic.barre@st.com
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
drivers/mmc/host/mmci.h
drivers/mmc/host/mmci_stm32_sdmmc.c

index c3bc0a3..e1a9b96 100644 (file)
 /* Extended status bits for the STM32 variants */
 #define MCI_STM32_BUSYD0       BIT(20)
 #define MCI_STM32_BUSYD0END    BIT(21)
+#define MCI_STM32_VSWEND       BIT(25)
 
 #define MMCICLEAR              0x038
 #define MCI_CMDCRCFAILCLR      (1 << 0)
 #define MCI_ST_SDIOITC         (1 << 22)
 #define MCI_ST_CEATAENDC       (1 << 23)
 #define MCI_ST_BUSYENDC                (1 << 24)
+/* Extended clear bits for the STM32 variants */
+#define MCI_STM32_VSWENDC      BIT(25)
+#define MCI_STM32_CKSTOPC      BIT(26)
 
 #define MMCIMASK0              0x03c
 #define MCI_CMDCRCFAILMASK     (1 << 0)
index fa875fe..f76e82f 100644 (file)
@@ -32,6 +32,7 @@
 #define DLYB_CFGR_UNIT_MAX     127
 
 #define DLYB_LNG_TIMEOUT_US    1000
+#define SDMMC_VSWEND_TIMEOUT_US 10000
 
 struct sdmmc_lli_desc {
        u32 idmalar;
@@ -265,6 +266,7 @@ static void mmci_sdmmc_set_pwrreg(struct mmci_host *host, unsigned int pwr)
        struct mmc_ios ios = host->mmc->ios;
        struct sdmmc_dlyb *dlyb = host->variant_priv;
 
+       /* adds OF options */
        pwr = host->pwr_reg_add;
 
        sdmmc_dlyb_input_ck(dlyb);
@@ -291,6 +293,10 @@ static void mmci_sdmmc_set_pwrreg(struct mmci_host *host, unsigned int pwr)
                writel(MCI_IRQENABLE | host->variant->start_err,
                       host->base + MMCIMASK0);
 
+               /* preserves voltage switch bits */
+               pwr |= host->pwr_reg & (MCI_STM32_VSWITCHEN |
+                                       MCI_STM32_VSWITCH);
+
                /*
                 * After a power-cycle state, we must set the SDMMC in
                 * Power-off. The SDMMC_D[7:0], SDMMC_CMD and SDMMC_CK are
@@ -456,6 +462,41 @@ static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
        return sdmmc_dlyb_phase_tuning(host, opcode);
 }
 
+static void sdmmc_pre_sig_volt_vswitch(struct mmci_host *host)
+{
+       /* clear the voltage switch completion flag */
+       writel_relaxed(MCI_STM32_VSWENDC, host->base + MMCICLEAR);
+       /* enable Voltage switch procedure */
+       mmci_write_pwrreg(host, host->pwr_reg | MCI_STM32_VSWITCHEN);
+}
+
+static int sdmmc_post_sig_volt_switch(struct mmci_host *host,
+                                     struct mmc_ios *ios)
+{
+       unsigned long flags;
+       u32 status;
+       int ret = 0;
+
+       if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
+               spin_lock_irqsave(&host->lock, flags);
+               mmci_write_pwrreg(host, host->pwr_reg | MCI_STM32_VSWITCH);
+               spin_unlock_irqrestore(&host->lock, flags);
+
+               /* wait voltage switch completion while 10ms */
+               ret = readl_relaxed_poll_timeout(host->base + MMCISTATUS,
+                                                status,
+                                                (status & MCI_STM32_VSWEND),
+                                                10, SDMMC_VSWEND_TIMEOUT_US);
+
+               writel_relaxed(MCI_STM32_VSWENDC | MCI_STM32_CKSTOPC,
+                              host->base + MMCICLEAR);
+               mmci_write_pwrreg(host, host->pwr_reg &
+                                 ~(MCI_STM32_VSWITCHEN | MCI_STM32_VSWITCH));
+       }
+
+       return ret;
+}
+
 static struct mmci_host_ops sdmmc_variant_ops = {
        .validate_data = sdmmc_idma_validate_data,
        .prep_data = sdmmc_idma_prep_data,
@@ -467,6 +508,8 @@ static struct mmci_host_ops sdmmc_variant_ops = {
        .set_clkreg = mmci_sdmmc_set_clkreg,
        .set_pwrreg = mmci_sdmmc_set_pwrreg,
        .busy_complete = sdmmc_busy_complete,
+       .pre_sig_volt_switch = sdmmc_pre_sig_volt_vswitch,
+       .post_sig_volt_switch = sdmmc_post_sig_volt_switch,
 };
 
 void sdmmc_variant_init(struct mmci_host *host)