[U-Boot] [PATCH 1/2] mmc: add a driver callback for power-cycle

Yann Gautier yann.gautier at st.com
Thu Sep 19 15:56:12 UTC 2019


Some MMC peripherals require specific power cycle sequence, where some
registers need to be written between the regulator is turned off and then
back on. This is the case for the MMC IP embedded in STM32MP1 SoC.

In STM32MP157 reference manual [1], the power cycle sequence is:
1. 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.
2. Disable the Vcc power to the card.
3. 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.
4. After minimum 1ms enable the Vcc power to the card.
5. After the power ramp period set the SDMMC to the power-off state for
minimum 1ms. The SDMMC_D[7:0], SDMMC_CMD and SDMMC_CK are set to
drive “1”.
6. After the 1ms delay set the SDMMC to power-on state in which the
SDMMC_CK clock will be enabled.
7. After 74 SDMMC_CK cycles the first command can be sent to the card.

The step 3. cannot be handled by the current framework implementation.
A new callback (host_power_cycle) is created, and called in
mmc_power_cycle(), after mmc_power_off().

The incorrect power cycle sequence has shown some boot failures on
STM32MP1 with some SD-cards, especially on cold boots when the input
frequency is low (<= 25MHz).
Those failures are no more seen with this correct power cycle sequence.

[1] https://www.st.com/resource/en/reference_manual/DM00327659.pdf

Signed-off-by: Yann Gautier <yann.gautier at st.com>
---

 drivers/mmc/mmc-uclass.c | 14 ++++++++++++++
 drivers/mmc/mmc.c        | 15 +++++++++++++++
 include/mmc.h            | 13 +++++++++++++
 3 files changed, 42 insertions(+)

diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c
index 37c3843902..c7a832ca90 100644
--- a/drivers/mmc/mmc-uclass.c
+++ b/drivers/mmc/mmc-uclass.c
@@ -122,6 +122,20 @@ int mmc_set_enhanced_strobe(struct mmc *mmc)
 }
 #endif
 
+int dm_mmc_host_power_cycle(struct udevice *dev)
+{
+	struct dm_mmc_ops *ops = mmc_get_ops(dev);
+
+	if (ops->host_power_cycle)
+		return ops->host_power_cycle(dev);
+	return 0;
+}
+
+int mmc_host_power_cycle(struct mmc *mmc)
+{
+	return dm_mmc_host_power_cycle(mmc->dev);
+}
+
 int mmc_of_parse(struct udevice *dev, struct mmc_config *cfg)
 {
 	int val;
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index c8f71cd0c1..2e68d21c51 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -1546,6 +1546,16 @@ static int mmc_set_ios(struct mmc *mmc)
 
 	return ret;
 }
+
+static int mmc_host_power_cycle(struct mmc *mmc)
+{
+	int ret = 0;
+
+	if (mmc->cfg->ops->host_power_cycle)
+		ret = mmc->cfg->ops->host_power_cycle(mmc);
+
+	return ret;
+}
 #endif
 
 int mmc_set_clock(struct mmc *mmc, uint clock, bool disable)
@@ -2715,6 +2725,11 @@ static int mmc_power_cycle(struct mmc *mmc)
 	ret = mmc_power_off(mmc);
 	if (ret)
 		return ret;
+
+	ret = mmc_host_power_cycle(mmc);
+	if (ret)
+		return ret;
+
 	/*
 	 * SD spec recommends at least 1ms of delay. Let's wait for 2ms
 	 * to be on the safer side.
diff --git a/include/mmc.h b/include/mmc.h
index 686ba00656..54629bf45d 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -466,6 +466,16 @@ struct dm_mmc_ops {
 	/* set_enhanced_strobe() - set HS400 enhanced strobe */
 	int (*set_enhanced_strobe)(struct udevice *dev);
 #endif
+
+	/**
+	 * host_power_cycle - host specific tasks in power cycle sequence
+	 *		      Called between mmc_power_off() and
+	 *		      mmc_power_on()
+	 *
+	 * @dev:	Device to check
+	 * @return 0 if not present, 1 if present, -ve on error
+	 */
+	int (*host_power_cycle)(struct udevice *dev);
 };
 
 #define mmc_get_ops(dev)        ((struct dm_mmc_ops *)(dev)->driver->ops)
@@ -477,6 +487,7 @@ int dm_mmc_get_cd(struct udevice *dev);
 int dm_mmc_get_wp(struct udevice *dev);
 int dm_mmc_execute_tuning(struct udevice *dev, uint opcode);
 int dm_mmc_wait_dat0(struct udevice *dev, int state, int timeout_us);
+int dm_mmc_host_power_cycle(struct udevice *dev);
 
 /* Transition functions for compatibility */
 int mmc_set_ios(struct mmc *mmc);
@@ -485,6 +496,7 @@ int mmc_getwp(struct mmc *mmc);
 int mmc_execute_tuning(struct mmc *mmc, uint opcode);
 int mmc_wait_dat0(struct mmc *mmc, int state, int timeout_us);
 int mmc_set_enhanced_strobe(struct mmc *mmc);
+int mmc_host_power_cycle(struct mmc *mmc);
 
 #else
 struct mmc_ops {
@@ -494,6 +506,7 @@ struct mmc_ops {
 	int (*init)(struct mmc *mmc);
 	int (*getcd)(struct mmc *mmc);
 	int (*getwp)(struct mmc *mmc);
+	int (*host_power_cycle)(struct mmc *mmc);
 };
 #endif
 
-- 
2.17.1



More information about the U-Boot mailing list