[PATCH v2] mmc: sunxi: mask DATA0 during H6-family clock updates
James Hilliard
james.hilliard1 at gmail.com
Tue Jul 7 19:26:29 CEST 2026
On an H616 board using SPL DM MMC to boot from SD, the initial clock
update command can time out while disabling the card clock. This happens
before SPL can load the FIT from the card, so MMC init fails and SPL has
no bootable MMC device.
The timeout happens in mmc_update_clk() while issuing the
UPCLK_ONLY/WAIT_PRE_OVER command from mmc_config_clock(). Linux marks the
H6/A100/NCAT2-style controllers with mask_data0 and sets CLKCR MASK_DATA0
around clock update commands so the controller does not sample DAT0 as
busy while updating the clock.
Mirror that behavior for H6-family and NCAT2-family controllers in
mmc_config_clock(). The reproduced failure used SPL DM MMC, but the
affected clock-update path is shared with the non-DM SPL MMC path, so
apply the mask in the common code. Leave the command-error recovery
mmc_update_clk() unchanged, matching Linux which applies mask_data0 in the
clock on/off path.
Also clear LOW_POWER_ON while rewriting CLKCR for the update sequence, as
Linux does. U-Boot does not set this bit today, but clearing stale state
keeps the clock update sequence aligned with Linux.
Use the existing SoC-family test style here, like the calibration support
check, so the legacy SPL path is covered without relying on DM compatible
data.
Signed-off-by: James Hilliard <james.hilliard1 at gmail.com>
---
Changes v1 -> v2:
- Describe the reproduced H616 SPL DM MMC clock-update timeout.
- Explain that the affected clock path is shared with non-DM SPL MMC.
- Fold the DATA0 masking into mmc_config_clock().
- Leave the command-error recovery clock update unchanged.
- Justify LOW_POWER_ON clearing as Linux clock-update parity.
- Explain the SoC-family check for the legacy SPL path.
---
drivers/mmc/sunxi_mmc.c | 35 ++++++++++++++++++++++++++++++++---
drivers/mmc/sunxi_mmc.h | 1 +
2 files changed, 33 insertions(+), 3 deletions(-)
diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c
index e28c81afffe..a00a1e45978 100644
--- a/drivers/mmc/sunxi_mmc.c
+++ b/drivers/mmc/sunxi_mmc.c
@@ -64,6 +64,12 @@ static bool sunxi_mmc_can_calibrate(void)
IS_ENABLED(CONFIG_MACH_SUN8I_R40);
}
+static bool sunxi_mmc_needs_data0_mask(void)
+{
+ return IS_ENABLED(CONFIG_SUN50I_GEN_H6) ||
+ IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2);
+}
+
static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
{
unsigned int pll, pll_hz, div, n, oclk_dly, sclk_dly;
@@ -204,12 +210,24 @@ static int mmc_update_clk(struct sunxi_mmc_priv *priv)
static int mmc_config_clock(struct sunxi_mmc_priv *priv, struct mmc *mmc)
{
unsigned rval = readl(&priv->reg->clkcr);
+ bool mask_data0 = sunxi_mmc_needs_data0_mask();
/* Disable Clock */
- rval &= ~SUNXI_MMC_CLK_ENABLE;
+ rval &= ~(SUNXI_MMC_CLK_ENABLE | SUNXI_MMC_CLK_POWERSAVE |
+ SUNXI_MMC_CLK_MASK_DATA0);
+ if (mask_data0)
+ rval |= SUNXI_MMC_CLK_MASK_DATA0;
writel(rval, &priv->reg->clkcr);
- if (mmc_update_clk(priv))
+ if (mmc_update_clk(priv)) {
+ if (mask_data0)
+ writel(rval & ~SUNXI_MMC_CLK_MASK_DATA0,
+ &priv->reg->clkcr);
return -1;
+ }
+ if (mask_data0) {
+ rval &= ~SUNXI_MMC_CLK_MASK_DATA0;
+ writel(rval, &priv->reg->clkcr);
+ }
/* Set mod_clk to new rate */
if (mmc_set_mod_clk(priv, mmc->clock))
@@ -231,10 +249,21 @@ static int mmc_config_clock(struct sunxi_mmc_priv *priv, struct mmc *mmc)
#endif
/* Re-enable Clock */
+ rval &= ~(SUNXI_MMC_CLK_POWERSAVE | SUNXI_MMC_CLK_MASK_DATA0);
rval |= SUNXI_MMC_CLK_ENABLE;
+ if (mask_data0)
+ rval |= SUNXI_MMC_CLK_MASK_DATA0;
writel(rval, &priv->reg->clkcr);
- if (mmc_update_clk(priv))
+ if (mmc_update_clk(priv)) {
+ if (mask_data0)
+ writel(rval & ~SUNXI_MMC_CLK_MASK_DATA0,
+ &priv->reg->clkcr);
return -1;
+ }
+ if (mask_data0) {
+ rval &= ~SUNXI_MMC_CLK_MASK_DATA0;
+ writel(rval, &priv->reg->clkcr);
+ }
return 0;
}
diff --git a/drivers/mmc/sunxi_mmc.h b/drivers/mmc/sunxi_mmc.h
index 71865160319..c9a0f3833a2 100644
--- a/drivers/mmc/sunxi_mmc.h
+++ b/drivers/mmc/sunxi_mmc.h
@@ -58,6 +58,7 @@ struct sunxi_mmc {
#define SUNXI_MMC_CLK_POWERSAVE (0x1 << 17)
#define SUNXI_MMC_CLK_ENABLE (0x1 << 16)
+#define SUNXI_MMC_CLK_MASK_DATA0 (0x1U << 31)
#define SUNXI_MMC_CLK_DIVIDER_MASK (0xff)
#define SUNXI_MMC_GCTRL 0x000
--
2.53.0
More information about the U-Boot
mailing list