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:
@@ -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)
|
||||
|
@@ -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;
|
||||
|
||||
|
Reference in New Issue
Block a user