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