Linux marks the H6/A100-style controllers with mask_data0 and sets the CLKCR MASK_DATA0 bit while issuing update-clock commands. Without this, the controller can sample DAT0 as busy around clock updates before the first card command, so card detection may fail with -ETIMEDOUT.
Mirror the Linux behavior for H6-family and NCAT2-family controllers while updating the card clock. Signed-off-by: James Hilliard <[email protected]> --- drivers/mmc/sunxi_mmc.c | 30 +++++++++++++++++++++++++++--- drivers/mmc/sunxi_mmc.h | 1 + 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c index e28c81afffe..145d6bd7c9d 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; @@ -201,14 +207,31 @@ static int mmc_update_clk(struct sunxi_mmc_priv *priv) return 0; } +static int mmc_update_clk_with_data0_mask(struct sunxi_mmc_priv *priv) +{ + bool mask_data0 = sunxi_mmc_needs_data0_mask(); + int ret; + + if (mask_data0) + setbits_le32(&priv->reg->clkcr, SUNXI_MMC_CLK_MASK_DATA0); + + ret = mmc_update_clk(priv); + + if (mask_data0) + clrbits_le32(&priv->reg->clkcr, SUNXI_MMC_CLK_MASK_DATA0); + + return ret; +} + static int mmc_config_clock(struct sunxi_mmc_priv *priv, struct mmc *mmc) { unsigned rval = readl(&priv->reg->clkcr); /* Disable Clock */ - rval &= ~SUNXI_MMC_CLK_ENABLE; + rval &= ~(SUNXI_MMC_CLK_ENABLE | SUNXI_MMC_CLK_POWERSAVE | + SUNXI_MMC_CLK_MASK_DATA0); writel(rval, &priv->reg->clkcr); - if (mmc_update_clk(priv)) + if (mmc_update_clk_with_data0_mask(priv)) return -1; /* Set mod_clk to new rate */ @@ -231,9 +254,10 @@ 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; writel(rval, &priv->reg->clkcr); - if (mmc_update_clk(priv)) + if (mmc_update_clk_with_data0_mask(priv)) return -1; 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

