From fceea9926804c8e72bdcde27bf64bebedcf0a754 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 29 Jan 2019 04:45:51 +0100 Subject: [PATCH 1/3] mmc: Downgrade SD/MMC from UHS/HS200/HS400 modes before boot 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 Cc: Tom Rini --- 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 * From 4c80f111c06f9aa258b9e55db1ee99572428c105 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 14 Feb 2019 15:16:24 +0100 Subject: [PATCH 2/3] mmc: tmio: Configure HOST_MODE WMODE according to bus width Set the HOST_MODE register WMODE bit according to the SDHI bus width, that is 0 for 64bit bus and 1 for 16/32bit bus. Signed-off-by: Marek Vasut Cc: Masahiro Yamada --- drivers/mmc/tmio-common.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/tmio-common.c b/drivers/mmc/tmio-common.c index 2421915a07..6e656e5a9b 100644 --- a/drivers/mmc/tmio-common.c +++ b/drivers/mmc/tmio-common.c @@ -705,10 +705,14 @@ static void tmio_sd_host_init(struct tmio_sd_priv *priv) * This register dropped backward compatibility at version 0x10. * Write an appropriate value depending on the IP version. */ - if (priv->version >= 0x10) - tmio_sd_writel(priv, 0x101, TMIO_SD_HOST_MODE); - else + if (priv->version >= 0x10) { + if (priv->caps & TMIO_SD_CAP_64BIT) + tmio_sd_writel(priv, 0x100, TMIO_SD_HOST_MODE); + else + tmio_sd_writel(priv, 0x101, TMIO_SD_HOST_MODE); + } else { tmio_sd_writel(priv, 0x0, TMIO_SD_HOST_MODE); + } if (priv->caps & TMIO_SD_CAP_DMA_INTERNAL) { tmp = tmio_sd_readl(priv, TMIO_SD_DMA_MODE); From 261445dfafdce252f91fea16517aba5830dd1930 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 14 Feb 2019 15:16:15 +0100 Subject: [PATCH 3/3] mmc: tmio: sdhi: Configure DT2FF register for HS400 mode The DT2FF register must be configured differently for HS400 mode and for HS200/SDR104 mode. Configure the DT2FF register according to the recommended datasheet settings for each mode. Signed-off-by: Marek Vasut Cc: Masahiro Yamada --- drivers/mmc/renesas-sdhi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mmc/renesas-sdhi.c b/drivers/mmc/renesas-sdhi.c index a556acd5cb..923f846370 100644 --- a/drivers/mmc/renesas-sdhi.c +++ b/drivers/mmc/renesas-sdhi.c @@ -148,6 +148,9 @@ static int renesas_sdhi_hs400(struct udevice *dev) tmio_sd_writel(priv, priv->tap_set, RENESAS_SDHI_SCC_TAPSET); } + tmio_sd_writel(priv, hs400 ? 0x704 : 0x300, + RENESAS_SDHI_SCC_DT2FF); + reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_CKSEL); reg |= RENESAS_SDHI_SCC_CKSEL_DTSEL; tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_CKSEL);