[Concept,3/4] mmc: rockchip_dw_mmc: Pass clock rate via bloblist

Message ID 20260329122222.3533806-4-sjg@u-boot.org
State New
Headers
Series vbe: Fix MMC clock handoff for VPL on rk3399 |

Commit Message

Simon Glass March 29, 2026, 12:22 p.m. UTC
  From: Simon Glass <sjg@chromium.org>

When the CLK framework is not available (e.g. in VPL), the DW MMC
driver cannot set the source clock rate, so get_mmc_clk() returns
the requested frequency. The DW MMC core then uses bypass mode
(divider=0), running the bus at whatever rate the CRU is configured
to from a previous phase. If that rate is high (e.g. 50 MHz from a
data transfer), card enumeration at 400 KHz fails.

Fix this by saving the clock rate to the bloblist when CLK is
available, and reading it back when CLK is not. This allows the DW
MMC core to calculate the correct clock divider based on the actual
source clock rate configured by the previous boot phase.

This is controlled by a new CONFIG_MMC_DW_ROCKCHIP_CLK_HANDOFF
Kconfig option.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 drivers/mmc/Kconfig           |  9 +++++++++
 drivers/mmc/rockchip_dw_mmc.c | 33 ++++++++++++++++++++++++++++++++-
 2 files changed, 41 insertions(+), 1 deletion(-)
  

Patch

diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 4c46df0ffb8..01d7025ce4d 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -292,6 +292,15 @@  config MMC_DW_ROCKCHIP
 	  SD 3.0, SDIO 3.0 and MMC 4.5 and supports common eMMC chips as well
 	  as removeable SD and micro-SD cards.
 
+config MMC_DW_ROCKCHIP_CLK_HANDOFF
+	bool "Pass MMC clock rate between boot phases via bloblist"
+	depends on MMC_DW_ROCKCHIP && BLOBLIST
+	help
+	  When enabled, the DW MMC source clock rate is saved to the
+	  bloblist after being configured. A later boot phase that lacks
+	  CLK support can read this rate and use it to calculate the
+	  correct clock divider, rather than assuming bypass mode.
+
 config MMC_SDHCI_ADI
 	bool "ADI SD/MMC controller support"
 	depends on ARCH_SC5XX
diff --git a/drivers/mmc/rockchip_dw_mmc.c b/drivers/mmc/rockchip_dw_mmc.c
index 7a72abaa38a..be6436dbe7d 100644
--- a/drivers/mmc/rockchip_dw_mmc.c
+++ b/drivers/mmc/rockchip_dw_mmc.c
@@ -3,6 +3,7 @@ 
  * Copyright (c) 2013 Google, Inc
  */
 
+#include <bloblist.h>
 #include <clk.h>
 #include <dm.h>
 #include <dt-structs.h>
@@ -50,8 +51,38 @@  static uint rockchip_dwmmc_get_mmc_clk(struct dwmci_host *host, uint freq)
 
 	ret = clk_set_rate(&priv->clk, freq);
 	if (ret < 0) {
+		/*
+		 * If CLK is not available (e.g. VPL), use the rate saved
+		 * by the previous phase via bloblist. The CRU is still
+		 * configured from that phase, so the DW MMC core can
+		 * calculate the correct clock divider.
+		 */
+		if (!CONFIG_IS_ENABLED(CLK) &&
+		    IS_ENABLED(CONFIG_MMC_DW_ROCKCHIP_CLK_HANDOFF)) {
+			u32 *ratep;
+
+			ratep = bloblist_find(BLOBLISTT_U_BOOT_MMC_CLK,
+					      sizeof(*ratep));
+			if (ratep)
+				return *ratep;
+		}
 		debug("%s: err=%d\n", __func__, ret);
-		return 0;
+		return freq;
+	}
+
+	/* Save the source clock rate for the next phase */
+	if (IS_ENABLED(CONFIG_XPL_BUILD) &&
+	    IS_ENABLED(CONFIG_MMC_DW_ROCKCHIP_CLK_HANDOFF)) {
+		u32 *ratep;
+
+		ratep = bloblist_ensure(BLOBLISTT_U_BOOT_MMC_CLK,
+					sizeof(*ratep));
+		if (ratep) {
+			ulong rate = clk_get_rate(&priv->clk);
+
+			if (!IS_ERR_VALUE(rate))
+				*ratep = rate;
+		}
 	}
 
 	return freq;