diff options
author | Patrick Delaunay <patrick.delaunay@st.com> | 2018-06-27 10:15:33 +0200 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2018-07-19 16:31:35 -0400 |
commit | 7d118161bbfbeb21ff2f6bf8ccc3cc0a8dd6ddc6 (patch) | |
tree | 818cf8221f8ebb8175308f51f83c33f979c24014 /drivers/mmc | |
parent | 14acea0244e33c4522fa3ea774bb3e76fa9e4fb4 (diff) |
mmc: stm32_sdmmc2: update pwron management
Correctly manage the SDMMC reset and card cycle power
to fully handle the power cycle added in the MMC uclass
and avoid issue with level-shifter with some uSDCARD.
3 states managed in driver:
1/ reset: SDMMC disable, signal HiZ
2/ power-cycle: SDMMC disable, signals drive to 0
3/ power-on: SDMMC enabled
Signed-off-by: Patrick Delaunay <patrick.delaunay@st.com>
Signed-off-by: Patrice Chotard <patrice.chotard@st.com>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/stm32_sdmmc2.c | 78 |
1 files changed, 67 insertions, 11 deletions
diff --git a/drivers/mmc/stm32_sdmmc2.c b/drivers/mmc/stm32_sdmmc2.c index e8292c438d..a36612dd93 100644 --- a/drivers/mmc/stm32_sdmmc2.c +++ b/drivers/mmc/stm32_sdmmc2.c @@ -56,7 +56,10 @@ struct stm32_sdmmc2_ctx { #define SDMMC_IDMABASE0 0x58 /* SDMMC DMA buffer 0 base address */ /* SDMMC_POWER register */ -#define SDMMC_POWER_PWRCTRL GENMASK(1, 0) +#define SDMMC_POWER_PWRCTRL_MASK GENMASK(1, 0) +#define SDMMC_POWER_PWRCTRL_OFF 0 +#define SDMMC_POWER_PWRCTRL_CYCLE 2 +#define SDMMC_POWER_PWRCTRL_ON 3 #define SDMMC_POWER_VSWITCH BIT(2) #define SDMMC_POWER_VSWITCHEN BIT(3) #define SDMMC_POWER_DIRPOL BIT(4) @@ -440,23 +443,74 @@ retry_cmd: return ret; } -static void stm32_sdmmc2_pwron(struct stm32_sdmmc2_priv *priv) +/* + * Reset the SDMMC with the RCC.SDMMCxRST register bit. + * This will reset the SDMMC to the reset state and the CPSM and DPSM + * to the Idle state. SDMMC is disabled, Signals Hiz. + */ +static void stm32_sdmmc2_reset(struct stm32_sdmmc2_priv *priv) { /* Reset */ reset_assert(&priv->reset_ctl); udelay(2); reset_deassert(&priv->reset_ctl); - udelay(1000); + /* init the needed SDMMC register after reset */ + writel(priv->pwr_reg_msk, priv->base + SDMMC_POWER); +} + +/* + * Set the SDMMC in power-cycle state. + * This will make that the SDMMC_D[7:0], + * SDMMC_CMD and SDMMC_CK are driven low, to prevent the card from being + * supplied through the signal lines. + */ +static void stm32_sdmmc2_pwrcycle(struct stm32_sdmmc2_priv *priv) +{ + if ((readl(priv->base + SDMMC_POWER) & SDMMC_POWER_PWRCTRL_MASK) == + SDMMC_POWER_PWRCTRL_CYCLE) + return; - /* Set Power State to ON */ - writel(SDMMC_POWER_PWRCTRL | priv->pwr_reg_msk, priv->base + SDMMC_POWER); + stm32_sdmmc2_reset(priv); + writel(SDMMC_POWER_PWRCTRL_CYCLE | priv->pwr_reg_msk, + priv->base + SDMMC_POWER); +} + +/* + * set the SDMMC state Power-on: the card is clocked + * manage the SDMMC state control: + * Reset => Power-Cycle => Power-Off => Power + * PWRCTRL=10 PWCTRL=00 PWCTRL=11 + */ +static void stm32_sdmmc2_pwron(struct stm32_sdmmc2_priv *priv) +{ + u32 pwrctrl = + readl(priv->base + SDMMC_POWER) & SDMMC_POWER_PWRCTRL_MASK; + + if (pwrctrl == SDMMC_POWER_PWRCTRL_ON) + return; + + /* warning: same PWRCTRL value after reset and for power-off state + * it is the reset state here = the only managed by the driver + */ + if (pwrctrl == SDMMC_POWER_PWRCTRL_OFF) { + writel(SDMMC_POWER_PWRCTRL_CYCLE | priv->pwr_reg_msk, + priv->base + SDMMC_POWER); + } /* - * 1ms: required power up waiting time before starting the - * SD initialization sequence + * the remaining case is SDMMC_POWER_PWRCTRL_CYCLE + * switch to Power-Off state: SDMCC disable, signals drive 1 */ - udelay(1000); + writel(SDMMC_POWER_PWRCTRL_OFF | priv->pwr_reg_msk, + priv->base + SDMMC_POWER); + + /* After the 1ms delay set the SDMMC to power-on */ + mdelay(1); + writel(SDMMC_POWER_PWRCTRL_ON | priv->pwr_reg_msk, + priv->base + SDMMC_POWER); + + /* during the first 74 SDMMC_CK cycles the SDMMC is still disabled. */ } #define IS_RISING_EDGE(reg) (reg & SDMMC_CLKCR_NEGEDGE ? 0 : 1) @@ -464,8 +518,6 @@ static int stm32_sdmmc2_set_ios(struct udevice *dev) { struct mmc *mmc = mmc_get_mmc_dev(dev); struct stm32_sdmmc2_priv *priv = dev_get_priv(dev); - struct stm32_sdmmc2_plat *plat = dev_get_platdata(dev); - struct mmc_config *cfg = &plat->cfg; u32 desired = mmc->clock; u32 sys_clock = clk_get_rate(&priv->clk); u32 clk = 0; @@ -473,7 +525,9 @@ static int stm32_sdmmc2_set_ios(struct udevice *dev) debug("%s: bus_with = %d, clock = %d\n", __func__, mmc->bus_width, mmc->clock); - if ((mmc->bus_width == 1) && (desired == cfg->f_min)) + if (mmc->clk_disable) + stm32_sdmmc2_pwrcycle(priv); + else stm32_sdmmc2_pwron(priv); /* @@ -577,6 +631,8 @@ static int stm32_sdmmc2_probe(struct udevice *dev) upriv->mmc = &plat->mmc; + /* SDMMC init */ + stm32_sdmmc2_reset(priv); return 0; clk_disable: |