跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

【EMMC】MSM8953里时钟是如何分频的

时钟源 MSM8953振荡频率为19.2 MHz的单晶振荡器(XO)。

XO作为所有pll的源,也可以作为其他时钟的源。

MSM8953没有单独的休眠晶体,使用586的除数从XO生成睡眠时钟32.768khz作为时钟信号源。

分析代码 在host中probe函数里有在初始化阶段获取clk信息

/* Setup SDC MMC clock */

msm_host->clk = devm_clk_get(&pdev->dev, "core_clk"); if (IS_ERR(msm_host->clk)) { ret = PTR_ERR(msm_host->clk); goto bus_aggr_clk_disable; }

跳转到设备树获取设备树中时钟信息,以下分别是mmc和sd的。

下面只示例mmc,sd同理。

clocks = <&clock_gcc clk_gcc_sdcc1_ahb_clk>, <&clock_gcc clk_gcc_sdcc1_apps_clk>, <&clock_gcc clk_gcc_sdcc1_ice_core_clk>;

clock-names = "iface_clk", "core_clk", "ice_core_clk"; ------------------------------------------------------------------------ clocks = <&clock_gcc clk_gcc_sdcc2_ahb_clk>,

<&clock_gcc clk_gcc_sdcc2_apps_clk>;

clock-names = "iface_clk", "core_clk";

查看clock-gcc-msm8953文件的probe函数,解析设备树资源

res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cc_base");

if (!res) { dev_err(&pdev->dev, "Register base not defined\n"); return -ENOMEM; }

获取寄存器基地址以及偏移量

clock_gcc_gfx: qcom,gcc-gfx@1800000 {

compatible = "qcom,gcc-gfx-8953"; reg = <0x1800000 0x80000>; reg-names = "cc_base"; vdd_gfx-supply = <&gfx_vreg_corner>; clocks = <&clock_gcc clk_xo_clk_src>; clock-names = "xo"; qcom,gcc_oxili_gfx3d_clk-opp-handle = <&msm_gpu>; qcom,gfxfreq-corner =

< 0 0 >, < 133330000 1 >, /* Min SVS */ < 216000000 2 >, /* Low SVS */ < 320000000 3 >, /* SVS */ < 400000000 4 >, /* SVS Plus */ < 510000000 5 >, /* NOM */ < 560000000 6 >, /* Nom Plus */ < 650000000 7 >; /* Turbo */

#clock-cells = <1>; };

寄存器基地址映射

virt_bases[GCC_BASE] = devm_ioremap(&pdev->dev, res->start,

resource_size(res));

获取时钟源table

static struct clk_freq_tbl ftbl_sdcc1_apps_clk_src[] = {

F( 144000, xo, 16, 3, 25), F( 400000, xo, 12, 1, 4), F( 20000000, gpll0_main_div2, 5, 1, 4), F( 25000000, gpll0_main_div2, 16, 0, 0), F( 50000000, gpll0, 16, 0, 0), F( 100000000, gpll0, 8, 0, 0), F( 177770000, gpll0, 4.5, 0, 0), F( 192000000, gpll4, 6, 0, 0), F( 384000000, gpll4, 3, 0, 0), F_END };

static struct rcg_clk sdcc1_apps_clk_src = { .cmd_rcgr_reg = SDCC1_APPS_CMD_RCGR, .set_rate = set_rate_mnd, .freq_tbl = ftbl_sdcc1_apps_clk_src, .current_freq = &rcg_dummy_freq, .base = &virt_bases[GCC_BASE], .c = { .dbg_name = "sdcc1_apps_clk_src", .ops = &clk_ops_rcg_mnd, VDD_DIG_FMAX_MAP3(LOW_SVS, 25000000, SVS, 100000000, NOM, 400000000), CLK_INIT(sdcc1_apps_clk_src.c), }, };

这里的table值是有对应的计算公式,可以根据相应spec分析。

F(f, s, d, m, n)

f = Frequency s = PLL d = SRC_DIV m = Multiplier n = Divisor

举个例子:

F(100000000, gpll0, 8, 0, 0),

f = 800 MHz *1/8 = 100000000 Hz m、n和d的规则如下: 0>=m<=2 0>=n<=255 0>=d<=15, 其中d的值可以有0.5,例如0.5、1.0、1.5、2.0等。

注: 若m和n不存在,它们等于0。

因此,要添加100MHz作为频率,必须将其作为上表中的函数添加,然后可以将100MHz添加到设备树中,以便驱动程序可以选择此频率。

接着分析,这些值是如何写到寄存器中的。

上面的src中有个set_rate_mnd函数。

这里要提一下spec中的寄存器。

分别代表上面公式的mnd寄存器。

SDCC1_APPS_CMD_RCGR寄存器

将mnd的值写到上述的寄存器中

void set_rate_mnd(struct rcg_clk *rcg, struct clk_freq_tbl *nf)

{ unsigned long flags;

spin_lock_irqsave(&local_clock_reg_lock, flags); __set_rate_mnd(rcg, nf); spin_unlock_irqrestore(&local_clock_reg_lock, flags); } static void __set_rate_mnd(struct rcg_clk *rcg, struct clk_freq_tbl *nf) { u32 cfg_regval;

cfg_regval = readl_relaxed(CFG_RCGR_REG(rcg)); writel_relaxed(nf->m_val, M_REG(rcg)); writel_relaxed(nf->n_val, N_REG(rcg)); writel_relaxed(nf->d_val, D_REG(rcg));

cfg_regval = readl_relaxed(CFG_RCGR_REG(rcg)); cfg_regval &= ~(CFG_RCGR_DIV_MASK | CFG_RCGR_SRC_SEL_MASK); cfg_regval |= nf->div_src_val;

/* Activate or disable the M/N:D divider as necessary */ cfg_regval &= ~MND_MODE_MASK; if (nf->n_val != 0) cfg_regval |= MND_DUAL_EDGE_MODE_BVAL; writel_relaxed(cfg_regval, CFG_RCGR_REG(rcg));

rcg_update_config(rcg); }

相关文章