aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLudovic Barre <ludovic.barre@st.com>2020-01-28 10:06:35 +0100
committerUlf Hansson <ulf.hansson@linaro.org>2020-03-24 14:35:40 +0100
commit94b94a93e3554f291316263a91ada93390befa83 (patch)
tree7222639aafac922029c2a26a58b04c91b8d74b34
parentmmc: mmci: Add callbacks for to manage signal voltage switch (diff)
downloadlinux-dev-94b94a93e3554f291316263a91ada93390befa83.tar.xz
linux-dev-94b94a93e3554f291316263a91ada93390befa83.zip
mmc: mmci_sdmmc: Implement signal voltage callbacks
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>
-rw-r--r--drivers/mmc/host/mmci.h4
-rw-r--r--drivers/mmc/host/mmci_stm32_sdmmc.c43
2 files changed, 47 insertions, 0 deletions
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index c3bc0a38d4cb..e1a9b96a3396 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -165,6 +165,7 @@
/* 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)
@@ -182,6 +183,9 @@
#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)
diff --git a/drivers/mmc/host/mmci_stm32_sdmmc.c b/drivers/mmc/host/mmci_stm32_sdmmc.c
index fa875febcc85..f76e82f8f12f 100644
--- a/drivers/mmc/host/mmci_stm32_sdmmc.c
+++ b/drivers/mmc/host/mmci_stm32_sdmmc.c
@@ -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)