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 <[email protected]> --- 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

