[U-Boot] [PATCH] mmc: Downgrade SD/MMC from UHS/HS200/HS400 modes before boot

Marek Vasut marek.vasut at gmail.com
Thu Feb 14 13:26:55 UTC 2019


Older kernel versions or systems which do not connect eMMC reset line
properly may not be able to handle situations where either the eMMC
is left in HS200/HS400 mode or SD card in UHS modes by the bootloader
and may misbehave. Downgrade the eMMC to HS/HS52 mode and/or SD card
to non-UHS mode before booting the kernel to allow such older kernels
to work with modern U-Boot.

Signed-off-by: Marek Vasut <marek.vasut+renesas at gmail.com>
Cc: Tom Rini <trini at konsulko.com>
---
 drivers/mmc/mmc-uclass.c | 19 +++++++++++++++++++
 drivers/mmc/mmc.c        | 26 ++++++++++++++++++++++++++
 include/mmc.h            |  6 ++++++
 3 files changed, 51 insertions(+)

diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c
index 76225b7939..a9c8f335c1 100644
--- a/drivers/mmc/mmc-uclass.c
+++ b/drivers/mmc/mmc-uclass.c
@@ -368,6 +368,19 @@ static int mmc_blk_probe(struct udevice *dev)
 	return 0;
 }
 
+#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || \
+    CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || \
+    CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)
+static int mmc_blk_remove(struct udevice *dev)
+{
+	struct udevice *mmc_dev = dev_get_parent(dev);
+	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(mmc_dev);
+	struct mmc *mmc = upriv->mmc;
+
+	return mmc_deinit(mmc);
+}
+#endif
+
 static const struct blk_ops mmc_blk_ops = {
 	.read	= mmc_bread,
 #if CONFIG_IS_ENABLED(MMC_WRITE)
@@ -382,6 +395,12 @@ U_BOOT_DRIVER(mmc_blk) = {
 	.id		= UCLASS_BLK,
 	.ops		= &mmc_blk_ops,
 	.probe		= mmc_blk_probe,
+#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || \
+    CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || \
+    CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)
+	.remove		= mmc_blk_remove,
+	.flags		= DM_FLAG_OS_PREPARE,
+#endif
 };
 #endif /* CONFIG_BLK */
 
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index b04345a1e1..1c1527cc74 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -2781,6 +2781,32 @@ int mmc_init(struct mmc *mmc)
 	return err;
 }
 
+#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || \
+    CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || \
+    CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)
+int mmc_deinit(struct mmc *mmc)
+{
+	u32 caps_filtered;
+
+	if (!mmc->has_init)
+		return 0;
+
+	if (IS_SD(mmc)) {
+		caps_filtered = mmc->card_caps &
+			~(MMC_CAP(UHS_SDR12) | MMC_CAP(UHS_SDR25) |
+			  MMC_CAP(UHS_SDR50) | MMC_CAP(UHS_DDR50) |
+			  MMC_CAP(UHS_SDR104));
+
+		return sd_select_mode_and_width(mmc, caps_filtered);
+	} else {
+		caps_filtered = mmc->card_caps &
+			~(MMC_CAP(MMC_HS_200) | MMC_CAP(MMC_HS_400));
+
+		return mmc_select_mode_and_width(mmc, caps_filtered);
+	}
+}
+#endif
+
 int mmc_set_dsr(struct mmc *mmc, u16 val)
 {
 	mmc->dsr = val;
diff --git a/include/mmc.h b/include/mmc.h
index d84e4fca73..1f30f71d25 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -690,6 +690,12 @@ int mmc_initialize(bd_t *bis);
 int mmc_init(struct mmc *mmc);
 int mmc_send_tuning(struct mmc *mmc, u32 opcode, int *cmd_error);
 
+#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || \
+    CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || \
+    CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)
+int mmc_deinit(struct mmc *mmc);
+#endif
+
 /**
  * mmc_of_parse() - Parse the device tree to get the capabilities of the host
  *
-- 
2.19.2



More information about the U-Boot mailing list