mmc: Downgrade SD/MMC from UHS/HS200/HS400 modes before boot
authorMarek Vasut <marek.vasut+renesas@gmail.com>
Tue, 29 Jan 2019 03:45:51 +0000 (04:45 +0100)
committerMarek Vasut <marex@denx.de>
Sat, 16 Feb 2019 17:12:17 +0000 (18:12 +0100)
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@gmail.com>
Cc: Tom Rini <trini@konsulko.com>
drivers/mmc/mmc-uclass.c
drivers/mmc/mmc.c
include/mmc.h

index 76225b7939bb06c3e761752d0b6e156d8c7e1d46..a9c8f335c142de56df370660986347652a51c66c 100644 (file)
@@ -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 */
 
index b04345a1e1518227c0a48845b06dfe2912954fda..1c1527cc747465ae6a2d4ec971595675b233ff7e 100644 (file)
@@ -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;
index d84e4fca73706ee008586a2f79122b653a3de0b2..1f30f71d25f8cda5ac77f6a6a9ca07fe9ed38d51 100644 (file)
@@ -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
  *