in host/mtk-sd.c [836:962]
static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
{
struct mmc_host *mmc = mmc_from_priv(host);
u32 mode;
u32 flags;
u32 div;
u32 sclk;
u32 tune_reg = host->dev_comp->pad_tune_reg;
u32 val;
if (!hz) {
dev_dbg(host->dev, "set mclk to 0\n");
host->mclk = 0;
mmc->actual_clock = 0;
sdr_clr_bits(host->base + MSDC_CFG, MSDC_CFG_CKPDN);
return;
}
flags = readl(host->base + MSDC_INTEN);
sdr_clr_bits(host->base + MSDC_INTEN, flags);
if (host->dev_comp->clk_div_bits == 8)
sdr_clr_bits(host->base + MSDC_CFG, MSDC_CFG_HS400_CK_MODE);
else
sdr_clr_bits(host->base + MSDC_CFG,
MSDC_CFG_HS400_CK_MODE_EXTRA);
if (timing == MMC_TIMING_UHS_DDR50 ||
timing == MMC_TIMING_MMC_DDR52 ||
timing == MMC_TIMING_MMC_HS400) {
if (timing == MMC_TIMING_MMC_HS400)
mode = 0x3;
else
mode = 0x2; /* ddr mode and use divisor */
if (hz >= (host->src_clk_freq >> 2)) {
div = 0; /* mean div = 1/4 */
sclk = host->src_clk_freq >> 2; /* sclk = clk / 4 */
} else {
div = (host->src_clk_freq + ((hz << 2) - 1)) / (hz << 2);
sclk = (host->src_clk_freq >> 2) / div;
div = (div >> 1);
}
if (timing == MMC_TIMING_MMC_HS400 &&
hz >= (host->src_clk_freq >> 1)) {
if (host->dev_comp->clk_div_bits == 8)
sdr_set_bits(host->base + MSDC_CFG,
MSDC_CFG_HS400_CK_MODE);
else
sdr_set_bits(host->base + MSDC_CFG,
MSDC_CFG_HS400_CK_MODE_EXTRA);
sclk = host->src_clk_freq >> 1;
div = 0; /* div is ignore when bit18 is set */
}
} else if (hz >= host->src_clk_freq) {
mode = 0x1; /* no divisor */
div = 0;
sclk = host->src_clk_freq;
} else {
mode = 0x0; /* use divisor */
if (hz >= (host->src_clk_freq >> 1)) {
div = 0; /* mean div = 1/2 */
sclk = host->src_clk_freq >> 1; /* sclk = clk / 2 */
} else {
div = (host->src_clk_freq + ((hz << 2) - 1)) / (hz << 2);
sclk = (host->src_clk_freq >> 2) / div;
}
}
sdr_clr_bits(host->base + MSDC_CFG, MSDC_CFG_CKPDN);
clk_disable_unprepare(host->src_clk_cg);
if (host->dev_comp->clk_div_bits == 8)
sdr_set_field(host->base + MSDC_CFG,
MSDC_CFG_CKMOD | MSDC_CFG_CKDIV,
(mode << 8) | div);
else
sdr_set_field(host->base + MSDC_CFG,
MSDC_CFG_CKMOD_EXTRA | MSDC_CFG_CKDIV_EXTRA,
(mode << 12) | div);
clk_prepare_enable(host->src_clk_cg);
readl_poll_timeout(host->base + MSDC_CFG, val, (val & MSDC_CFG_CKSTB), 0, 0);
sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_CKPDN);
mmc->actual_clock = sclk;
host->mclk = hz;
host->timing = timing;
/* need because clk changed. */
msdc_set_timeout(host, host->timeout_ns, host->timeout_clks);
sdr_set_bits(host->base + MSDC_INTEN, flags);
/*
* mmc_select_hs400() will drop to 50Mhz and High speed mode,
* tune result of hs200/200Mhz is not suitable for 50Mhz
*/
if (mmc->actual_clock <= 52000000) {
writel(host->def_tune_para.iocon, host->base + MSDC_IOCON);
if (host->top_base) {
writel(host->def_tune_para.emmc_top_control,
host->top_base + EMMC_TOP_CONTROL);
writel(host->def_tune_para.emmc_top_cmd,
host->top_base + EMMC_TOP_CMD);
} else {
writel(host->def_tune_para.pad_tune,
host->base + tune_reg);
}
} else {
writel(host->saved_tune_para.iocon, host->base + MSDC_IOCON);
writel(host->saved_tune_para.pad_cmd_tune,
host->base + PAD_CMD_TUNE);
if (host->top_base) {
writel(host->saved_tune_para.emmc_top_control,
host->top_base + EMMC_TOP_CONTROL);
writel(host->saved_tune_para.emmc_top_cmd,
host->top_base + EMMC_TOP_CMD);
} else {
writel(host->saved_tune_para.pad_tune,
host->base + tune_reg);
}
}
if (timing == MMC_TIMING_MMC_HS400 &&
host->dev_comp->hs400_tune)
sdr_set_field(host->base + tune_reg,
MSDC_PAD_TUNE_CMDRRDLY,
host->hs400_cmd_int_delay);
dev_dbg(host->dev, "sclk: %d, timing: %d\n", mmc->actual_clock,
timing);
}