时钟源
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);
}