1. 程式人生 > >linux下基於vc088x開發板分析CLK模型(時鐘管理)

linux下基於vc088x開發板分析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() 設定時鐘。

層次關係如下圖所示:


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 結構體

       

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