Linux clk 模型

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() 设置时钟。

层次关系如下图所示:

Linux clk 模型_第1张图片

1. 驱动层与porting层之间的api

驱动层与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 中实现,供驱动层模块调用。

 

2. clk porting层

2.1 clk porting 层包含3个文件

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 节点链表

 

2.2 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

                       

2.3 clk的注册以及初始化过程

              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()

 

3. 驱动程序如何使用clk模型的api

根据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);

 

4. cpu切频如何使用clk模型的api

       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 结构体

       Linux clk 模型_第2张图片

其中,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;



你可能感兴趣的:(Linux clk 模型)