clock子系统的总结

工作中接触到的clock代码的学习和总结。clock子系统。阅读了工作中的代码,参考了http://www.wowotech.net/tag/clock 里的一系列文章。

clock设备,包括root clock,如外部晶振;PLL,倍频; divider, 分频; gate , 只有开和关两种,还有mux,从多个上级clock输入中选择其中一种。

工作代码,clock设备没有放在dts中,只是把两个clock的基地址放在了dts里,clock的配置信息以结构体数组的table形式,放在了源代码中。

在项目自己的clk.c文件中,
有一个static struct clk **clk_table; 
clock系统初始化时
clk_data.clks = clk_table;
clk_data.clk_num = nr_clks;
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
会把这个clk_table注册为clock provider
每初始化一个clock,会把这个clock加入到clk_table中:
void XXX_clk_add_lookup(struct clk *clk, unsigned int id)
{
if (clk_table && id)
clk_table[id] = clk;
}


clock初始化,包括了 fixed root clock, pll, mux, divider, gate 的初始化,每种clock基本上是一张表,表中的每一项就是一个clock的配置信息。比如fixed root clock的表中:
FRATE(CLK_XTAL, "xtal", NULL, CLK_IS_ROOT, 24000000),
FRATE(0, "32Khz", NULL, CLK_IS_ROOT, 32000),
FRATE(CLK_81, "clk81", NULL, CLK_IS_ROOT, 166666666),
FRATE(CLK_FIXED_PLL, "fixed_pll", NULL, CLK_IS_ROOT, 2000000000),
FRATE(CLK_FPLL_DIV2, "fclk_div2", NULL, CLK_IS_ROOT, 1000000000),
FRATE(CLK_FPLL_DIV3, "fclk_div3", NULL, CLK_IS_ROOT,  666666666),
FRATE(CLK_FPLL_DIV4, "fclk_div4", NULL, CLK_IS_ROOT,  500000000),
FRATE(CLK_FPLL_DIV5, "fclk_div5", NULL, CLK_IS_ROOT,  400000000),
FRATE(CLK_FPLL_DIV7, "fclk_div7", NULL, CLK_IS_ROOT,  285714285),
通过for循环,分别读取这些配置信息,每读取一项,就调用一次clk_register.
register 过程:
实现每个clock的ops回调函数,如recalc_rate, enable, disable,把回调函数赋值到ops域,填充初始化结构体,比如前面说的ops域, parent_names, num_parents, 然后调用clk_register,参数是struct clk_hw, 初始化需要用到的信息都放在这个结构体参数重。在clk_register中,又调用了int __clk_init(struct device *dev, struct clk *clk)
, 在__clk_init中,首先检查clk结构体中的各个域是否合法,如果已注册的clock中有待注册的clock的parent, 则建立parent child关联,如果没有parent,并且是root,就加入到clk_root_list链表中,如果既没有parent,也不是root, 就加入到clk_orphan_list中。如果clock实现了clk->ops->recalc_rate这个回调函数,则调用这个函数计算clock rate,并且把rate保存在clk->rate中,如果没有实现clk->ops->recalc_rate,就以该clock的parent clock->rate作为该clock的clock->rate. 遍历clk_orphan_list,调用list中的clk的get_parent回调,确认待注册的clock是否为orphan clock的parent, 如果是,则调用__clk_parent,建立关系。完成clk_register之后。clk_table[id] = clk;把这个clock保存到项目的静态的数据表格中。调用ret = clk_register_clkdev(clk, list->name, NULL);用于之后获取clock。clk_register_clkdev已经过时了,我们的项目中还没有使用dts来配置clock,是使用的静态数组,所以还需要调用这个函数。

fix rate clock, pll, mux, gate, 等类型的clock,都会调用到clk_register. 不同的是,每种类型的clock的ops不一样,像mux,主要是选择parent, 有get parent, set parent的ops, gate类型的,有enable, disable. 还有的clock有计算clock rate和set clock rate的回调函数。

recalc_rate回调函数:看了一个给cpu提供clock的ops, 主要是读取相关的寄存器,根据寄存器数值,进行倍频,分频,计算得到clock频率。
set_rate: 对于给cpu提供clock的clock,
irq_flags = arch_local_irq_save();
preempt_disable();
/*CPU switch to xtal*/
cpu clock rate有一个clock rate表格,如果目标rate小于最小rate,就按最小rate那一项来设置,如果目标rate大于表格中的最大rate,就按最大rate来设置。
查找到待设置的rate属于表格中的哪一项,按照表格中的配置项,配置pll,配置寄存器。
  /*cpu switch to sys pll*/
preempt_enable();
arch_local_irq_restore(irq_flags);
切换cpu频率之后,需要对LPJ loop_per_jiffiy 校准,用于udelay延时。





















發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章