Linux clk 模型
Linux clk模型采用面向对象的思想来设计实现的。
在porting层创建一个一个的clk节点对象,然后将所有的clk节点对象连成一个list。
当驱动层需要设置时钟的时候,通过porting层与驱动层直接的api函数进行操作。首先通过clk_get函数,根据clk节点的名字,获取clk节点。然后,使用clk_set_rate()函数设置clk节点的时钟。clk_set_rate() 函数最终将会调用clk节点对象的成员函数 clk->set_rate() 设置时钟。
层次关系如下图所示:
驱动层与porting层之间的api,定义在include/linux/clk.h文件中
// clk的结构体,空的,
// 正真的实现在arch\arm\plat-vc088x\include\plat\clock.h
struct clk;
// 根据name获得注册过的clk
struct clk *clk_get(struct device *dev, constchar *id);
// 释放clk
void clk_put(struct clk *clk);
//打开和关闭clk
int clk_enable(struct clk *clk);
void clk_disable(struct clk *clk);
// 设置clk的频率,获得clk的频率
int clk_set_rate(struct clk *clk, unsignedlong rate);
long clk_round_rate(struct clk *clk, unsignedlong rate); //无法设置精确clk
unsigned long clk_get_rate(struct clk*clk);
// 设置clk的parent,获取clk的parent
int clk_set_parent(struct clk *clk, structclk *parent);
struct clk *clk_get_parent(struct clk*clk);
clk结构体,在arch\arm\plat-vc088x\include\plat\clock.h实现。
其它函数,在arch\arm\plat-vc088x\ clock.c 中实现,供驱动层模块调用。
clk porting 层主要包括3个文件:
arch\arm\plat-vc088x\include\plat\clock.h // clk结构体的实现
arch\arm\plat-vc088x\clock.c // 驱动层与porting层之间的api的实现
arch\arm\mach-vc0882\clock-vortex.c // 建立clk 节点链表
在arch\arm\mach-vc0882\clock-vortex.c文件中定义了下面这个数组:
static struct clk* sys_clks[];
这个数组使用struct clk结构体描述了所有的时钟节点。
clk节点的数据结构,采用面向对象的思想。
下面以snr为例,介绍如何描述clk节点的。
static struct clk snr_clk = {
.name = "snr_clk", // 驱动层通过clk_get函数获取clk使用
.flags = CLK_FLAG_DIV |CLK_FLAG_GATE | RECALC_ON_ENABLE,
.parent = &XCLK,
.max_dividor = 64,
.private_data =&snr_clkdata,
.enable = v8clk_clear_gatebit, // clk_enable 最终的实现代码
.disable = v8clk_set_gatebit, // clk_ disable 最终的实现代码
.recalc =vc88x_clk_recalc,
.set_rate = v8clk_setrate,// clk_ set_rate 最终的实现代码
.init = vc88x_clk_init,
};
static struct clk_data snr_clkdata = {
.clk_cfg_reg =V8REG_CLKRST_CIF_MCLK_CFG,
.cfg_data =&snr_clkcfg,
.cfg_mask = 0x3F00,
.pfnCfg =div_calculator,
.pfnRate = div_parser,
.clk_ctrl_reg =V8REG_CLKRST_CIF_MCLK_CTRL,
.gate_bit =(1<<1),
};
struct clk_data 这个结构体,用来保存clk节点的寄存器以及相关的bit信息的。这些成员变量在clk_set_rate、clk_enable、clk_ disable等api中被使用的。比较重要的,有如下成员变量:
clk_ctrl_reg
gate_bit
bypass_bit
clk_status_reg
clk_sw_rst_reg
clk_cfg_reg
div_max
div_min
div_shift
1.建立clk 节点list
2.初始化所有clk
在时钟初始化的过程中,通过clk_register函数,将sys_clks数组中所有的时钟节点,都注册到一个list中。函数调用过程如下:
->init_machine()(vortex_init ())
-> vc088x_register_baseclocks ()
-> clk_register ()
->list_add () // 建立clk 节点list
->clk->init(clk); // 这个时候会初始化clk
-> clk->set_rate()
根据clk节点的name,通过clk_get 获取时钟节点。
clk1 = clk_get(&dev, " snr_clk");
clk2 = clk_get(&dev, " dpi_pixel_clk ");
clk3 = clk_get(&dev, " cvbs_pixel_clk ");
clk4= clk_get(&dev, " vdac_pixel_clk ");
……
然后,就可以通过驱动层与porting层之间的api来设置各个时钟节点。
clk_disable(clk1);
clk_set_rate(clk2, 24*1000000);
clk_enable(clk3);
cpu切频驱动程序包括以下几个文件:
drivers\cpufreq\
cpufreq.c
cpufreq_conservative.c // 按次序切频
cpufreq_ondemand.c // 按命令切频
cpufreq_performance.c // 最高频率
cpufreq_powersave.c // 最低频率
cpufreq_stats.c
cpufreq_userspace.c
freq_table.c
cpu切频 porting 层包括以下几个文件:
arch\arm\plat-vc088x\cpu.c // structcpufreq_driver 结构体
其中,cpufreq_driver 结构体的成员函数,基于linux clk 模型的驱动层和porting层的api来实现的。
static struct cpufreq_driver v8_driver = {
.flags = CPUFREQ_STICKY,
.verify = v8_verify_speed,
.target = v8_target, // 切频最终调用的函数
.get = v8_getspeed,
.init = v8_cpu_init,
.exit = v8_cpu_exit,
.name = "v8cpu",
.attr = v8_cpufreq_attr,
};
v8_driver .target->
v8_target()
clk_set_rate(cpu_clk, freqs.new * 1000);
v8_driver .get->
v8_getspeed()
rate = clk_get_rate(cpu_clk) / 1000;