1
0
mirror of https://xff.cz/git/u-boot/ synced 2025-08-31 16:22:36 +02:00

mmc: sunxi: Cleanup, add support for DDR

With this, we can boot from eMMC with read speeds around 47.5MiB/s.

Signed-off-by: Ondrej Jirman <megous@megous.com>
This commit is contained in:
Ondrej Jirman
2019-09-12 18:35:08 +02:00
parent b4a41c07c2
commit cae566a075
2 changed files with 69 additions and 16 deletions

View File

@@ -64,6 +64,7 @@ struct sunxi_mmc {
SUNXI_MMC_GCTRL_FIFO_RESET|\
SUNXI_MMC_GCTRL_DMA_RESET)
#define SUNXI_MMC_GCTRL_DMA_ENABLE (0x1 << 5)
#define SUNXI_MMC_GCTRL_DDR_MODE (0x1 << 10)
#define SUNXI_MMC_GCTRL_ACCESS_BY_AHB (0x1 << 31)
#define SUNXI_MMC_CMD_RESP_EXPIRE (0x1 << 6)

View File

@@ -125,7 +125,47 @@ static int mmc_resource_init(int sdc_no)
}
#endif
static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
/*
* New timing modes usable on controllers:
*
* A83T - 2
* H3 - 1, 2
* H5 - all
* H6 - all
*
* SMHC_NTSR 0x005C: (new mode is default)
*
* H5, H6 yes
* A83T, H3 no
*
* Clock sources for SMHC clock:
*
* H3: PLL_PERIPH0 600MHz
* H5: PLL_PERIPH0(2x) 1200MHz
* H6: PLL_PERIPH0(2x) 1200MHz
* A83T: PLL_PERIPH 600MHz
*
* In new mode MMC clock for the module is post-divided by 2
*
* - SDMMCx SCLK = SRC_CLK (see above) / M / N / 2 (new mode)
* - SDMMCx SCLK = PLL_PERIPH / M / N (old mode)
*
* New mode needs to be also selected in CCU reg:
* H3, A83T
*
* Clk phase can be selected in CCU reg (for old mode):
* H3, A83T
*
* When using DDR, we need to double the SCLK and set post-div
* in CLKCR (in addition to previous requirements).
*
* H5, despite having new mode and double the clock incidentally
* works because new mode is the default, and u-boot thinks incorrectly
* that the source clock is half the speed, so it choses the right
* dividers.
*/
static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, bool is_ddr,
unsigned int hz)
{
unsigned int pll, pll_hz, div, n, oclk_dly, sclk_dly;
bool new_mode = true;
@@ -143,8 +183,11 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
calibrate = true;
#endif
/* A83T in new mode requires double the clock */
if (new_mode)
hz = hz * 2;
hz *= 2;
if (is_ddr)
hz *= 2;
if (hz <= 24000000) {
pll = CCM_MMC_CTRL_OSCM24;
@@ -220,9 +263,15 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
CCM_MMC_CTRL_SCLK_DLY(sclk_dly);
}
writel(CCM_MMC_CTRL_ENABLE| pll | CCM_MMC_CTRL_N(n) |
writel(CCM_MMC_CTRL_ENABLE | pll | CCM_MMC_CTRL_N(n) |
CCM_MMC_CTRL_M(div) | val, priv->mclkreg);
/* set internal divider if DDR */
val = readl(&priv->reg->clkcr);
val &= ~SUNXI_MMC_CLK_DIVIDER_MASK;
val |= is_ddr ? 1 : 0;
writel(val, &priv->reg->clkcr);
debug("mmc %u set mod-clk req %u parent %u n %u m %u rate %u\n",
priv->mmc_no, hz, pll_hz, 1u << n, div, pll_hz / (1u << n) / div);
@@ -253,22 +302,16 @@ 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);
/* Disable Clock */
rval &= ~SUNXI_MMC_CLK_ENABLE;
writel(rval, &priv->reg->clkcr);
clrbits_le32(&priv->reg->clkcr, SUNXI_MMC_CLK_ENABLE);
if (mmc_update_clk(priv))
return -1;
/* Set mod_clk to new rate */
if (mmc_set_mod_clk(priv, mmc->clock))
if (mmc_set_mod_clk(priv, mmc->selected_mode == MMC_DDR_52, mmc->clock))
return -1;
/* Clear internal divider */
rval &= ~SUNXI_MMC_CLK_DIVIDER_MASK;
writel(rval, &priv->reg->clkcr);
#if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN50I_H6)
/* A64 supports calibration of delays on MMC controller and we
* have to set delay of zero before starting calibration.
@@ -280,8 +323,8 @@ static int mmc_config_clock(struct sunxi_mmc_priv *priv, struct mmc *mmc)
#endif
/* Re-enable Clock */
rval |= SUNXI_MMC_CLK_ENABLE;
writel(rval, &priv->reg->clkcr);
setbits_le32(&priv->reg->clkcr, SUNXI_MMC_CLK_ENABLE);
if (mmc_update_clk(priv))
return -1;
@@ -300,6 +343,12 @@ static int sunxi_mmc_set_ios_common(struct sunxi_mmc_priv *priv,
return -EINVAL;
}
/* Timing */
if (mmc->selected_mode == MMC_DDR_52)
setbits_le32(&priv->reg->gctrl, SUNXI_MMC_GCTRL_DDR_MODE);
else
clrbits_le32(&priv->reg->gctrl, SUNXI_MMC_GCTRL_DDR_MODE);
/* Change bus width */
if (mmc->bus_width == 8)
writel(0x2, &priv->reg->width);
@@ -707,7 +756,7 @@ struct mmc *sunxi_mmc_init(int sdc_no)
/* unassert reset */
setbits_le32(&ccm->sd_gate_reset, 1 << (RESET_SHIFT + sdc_no));
#endif
ret = mmc_set_mod_clk(priv, 24000000);
ret = mmc_set_mod_clk(priv, false, 24000000);
if (ret)
return NULL;
@@ -800,6 +849,9 @@ static int sunxi_mmc_probe(struct udevice *dev)
priv->mclkreg = (void *)ccu_reg +
(priv->variant->mclk_offset + (priv->mmc_no * 4));
if (priv->mmc_no == 2)
cfg->host_caps |= MMC_MODE_DDR_52MHz;
ret = clk_get_by_name(dev, "ahb", &gate_clk);
if (!ret)
clk_enable(&gate_clk);
@@ -808,7 +860,7 @@ static int sunxi_mmc_probe(struct udevice *dev)
if (!ret)
reset_deassert_bulk(&reset_bulk);
ret = mmc_set_mod_clk(priv, 24000000);
ret = mmc_set_mod_clk(priv, false, 24000000);
if (ret)
return ret;