在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

深度解析linux時鐘子系統

嵌入式小生 ? 來源:嵌入式小生 ? 2024-09-29 16:46 ? 次閱讀

一、clk框架簡介

linux內核中實現了一個CLK子系統,用于對上層提供各模塊(例如需要時鐘信號的外設,USB等)的時鐘驅動接口,對下層提供具體SOC的時鐘操作細節:

wKgZoma8gtGAF233AAEuUivEFwE050.jpg

一般情況下,在可運行linux的處理器平臺中,都存在非常復雜的時鐘樹(clock tree)關系,也一定會有一個非常龐大和復雜的樹狀圖,用于描述與時鐘相關的器件,以及這些器件輸出的clock關系。查看手冊都會存在類似下列的內容:

wKgZoma8gtGAXRgmAAIKxHdlzq0254.jpg

一款處理器中與時鐘相關的器件主要包括有:

用于產生 CLOCK 的 Oscillator(有源振蕩器,也稱作諧振蕩器)或者Crystal(無源振蕩器,也稱晶振)。

用于倍頻的 PLL(鎖相環,Phase Locked Loop)。

用于分頻的Divider。

用于多路選擇的 MUX。

用于CLOCK ENABLE控制的與門。

使用 CLOCK 的硬件模塊(也可稱為CONSUMER)。wKgZoma8guSAcNGKAAEU-84XAUM859.png

linux內核中與clk框架相關源文件如下:

/include/linux/clk.h
/include/linux/clkdev.h
/include/linux/clk-provider.h
/include/linux/clk-conf.h
------------------------------------------------------
/drivers/clk/clk-devres.c
/drivers/clk-bulk.c
/drivers/clkdev.c
/drivers/clk.c
/drivers/clk-divider.c
/drivers/clk-fixed-factor.c
/drivers/clk-fixed-rate.c
/drivers/clk-gate.c
/drivers/clk-multiplier.c
/drivers/clk-mux.c
/drivers/clk-composite.c
/drivers/clk-fractional-divider.c
/drivers/clk-gpio.c
/drivers/clk-conf.c

二、clk框架接口

1、基于linux時鐘子系統對接底層時鐘操作的API

linux時鐘子系統對接底層,也就是具體硬件常用的API可視為clk provider常用的接口函數,定義在linux/include/linux/clk-provider.h文件中。不同版本linux內核中對于clk-probider.h實現的而接口存在出入,參見源碼更進一步獲取接口和使用方法。

注冊/注銷時鐘

//注冊一個新的時鐘。通常在設備驅動程序中使用,以向時鐘框架添加新的時鐘源。
structclk*clk_register(structdevice*dev,structclk_hw*hw);

//帶資源管理注冊時鐘
structclk*devm_clk_register(structdevice*dev,structclk_hw*hw);

//卸載時鐘
voidclk_unregister(structclk*clk);
//帶資源管理卸載時鐘
voiddevm_clk_unregister(structdevice*dev,structclk*clk);

2、驅動中常使用的API

芯片廠家會根據clk框架,對下層(即底層硬件)設計出接口,以供上層驅動接口調用,在內核中,提供的接口主要由/include/linux/clk.h文件導出,使用這些API接口時,需包含linux/clk.h頭文件:

#include

獲取struct clk指針:

structclk*devm_clk_get(structdevice*dev,constchar*id)(推薦使用,可以自動釋放)
structclk*clk_get(structdevice*dev,constchar*id)
staticinlinestructclk*devm_clk_get_optional(structdevice*dev,constchar*id)

//(推薦使用,整組獲取,整組開關)
staticinlineint__must_checkdevm_clk_bulk_get(structdevice*dev,intnum_clks,structclk_bulk_data*clks)
staticinlineint__must_checkdevm_clk_bulk_get_optional(structdevice*dev,intnum_clks,structclk_bulk_data*clks)
staticinlineint__must_checkdevm_clk_bulk_get_all(structdevice*dev,structclk_bulk_data**clks)

獲取/設置時鐘頻率

//根據給定的目標頻率計算最接近的可實現頻率。這個函數通常在設置時鐘頻率之前調用,以確保設置的頻率是硬件支持的頻率之一。
longclk_round_rate(structclk*clk,unsignedlongrate)

//獲取時鐘頻率
unsignedlongclk_get_rate(structclk*clk)

//設置時鐘頻率
intclk_set_rate(structclk*clk,unsignedlongrate)

準備/使能clk:

/*開時鐘前調用,可能會造成休眠,所以把休眠部分放到這里,可以原子操作的放到enable里*/
intclk_prepare(structclk*clk)

/*停止clock后的善后工作,可能會睡眠。*/
voidclk_unprepare(structclk*clk)

/*原子操作,打開時鐘,這個函數必須在產生實際可用的時鐘信號后才能返回,不會睡眠*/
intclk_enable(structclk*clk)

/*原子操作,關閉時鐘,不會睡眠*/
voidclk_disable(structclk*clk)

上述兩套API的本質,是把CLOCK的啟動/停止分為Atomic和Non-atomic兩個階段,以方便實現和調用。因此上面所說的“不會睡眠/可能會睡眠”,有兩個角度的含義:

一是告訴底層的CLOCK Driver,需把可能引起睡眠的操作,放到Prepare()/Unprepare()中實現,一定不能放到Enable()/Disable()中;

二是提醒上層使用CLOCK的Driver,調用Prepare/Unprepare 接口時可能會睡眠,千萬不能在Atomic上下文(例如內部包含Mutex 鎖、中斷關閉、Spinlock 鎖保護的區域)調用,而調用Enable()/Disable()接口則可放心。

另外,CLOCK的Enable()/Disable()為什么需要睡眠呢?例如Enable PLL CLOCK,在啟動PLL后,需要等待它穩定,然而PLL的穩定時間是很長的,因此這段時間要需要把CPU讓出(進程睡眠),不然就會浪費CPU了。

最后,為什么會實現合在一起的clk_prepare_enable()/clk_disable_unprepare()接口呢?如果調用者能確保是在Non-atomic上下文中調用,就可以順序調用prepare()/enable()、disable()/unprepared(),為了方便Colck框架就封裝了這兩個接口。

備注:使用clk_prepare_enable / clk_disable_unprepare,clk_prepare_enable / clk_disable_unprepare(或者clk_enable / clk_disable) 必須成對,以使引用計數正確。

三、CLK核心的數據結構和API

1、struct clk_notifier

stuct clk_notifier用于將CLK與通知器進行關聯,也就是定義clk的通知器,基于srcu實現。該結構實現如下(/linux/include/linux/clk.h):

structclk_notifier{
structclk*clk;//與該通知器關聯的clk。
structsrcu_notifier_headnotifier_head;//用于這個CLK的blocking_notifier_head通知器。
structlist_headnode;
};

常用API:

//注冊一個通知塊(notifierblock),以便在指定時鐘發生事件(例如頻率變化)時接收通知。
intclk_notifier_register(structclk*clk,structnotifier_block*nb);

//注銷一個通知塊
intclk_notifier_unregister(structclk*clk,structnotifier_block*nb);

//帶資源管理注冊一個通知塊(notifierblock),以便在指定時鐘發生事件(如頻率變化)時接收通知。確保在設備驅動程序卸載時自動注銷通知塊。
intdevm_clk_notifier_register(structdevice*dev,structclk*clk,structnotifier_block*nb);

2、struct clk_core

struct clk_core為clk框架的私有結構,定義如下:

structclk_core{
constchar*name;//clk核心名稱。
conststructclk_ops*ops;//該clk核心對應的ops。
structclk_hw*hw;
structmodule*owner;
structdevice*dev;
structdevice_node*of_node;
structclk_core*parent;
structclk_parent_map*parents;
u8num_parents;
u8new_parent_index;
unsignedlongrate;
unsignedlongreq_rate;
unsignedlongnew_rate;
structclk_core*new_parent;
structclk_core*new_child;
unsignedlongflags;
boolorphan;
boolrpm_enabled;
unsignedintenable_count;
unsignedintprepare_count;
unsignedintprotect_count;
unsignedlongmin_rate;
unsignedlongmax_rate;
unsignedlongaccuracy;
intphase;
structclk_dutyduty;
structhlist_headchildren;
structhlist_nodechild_node;
structhlist_headclks;
unsignedintnotifier_count;
#ifdefCONFIG_DEBUG_FS
structdentry*dentry;
structhlist_nodedebug_node;
#endif
structkrefref;
};

從上述結構的組成元素可知,struct clk_core是struct device的子類,因為一款SOC的時鐘關系一般以“樹狀”進行組織,在struct clk_core中提供描述父clk_core和子clk_core的組成元素。

3、struct clk_ops

struct clk_ops描述硬件時鐘的回調操作,并由驅動程序通過clk_*API調用。該結構定義如下:

structclk_ops{
int(*prepare)(structclk_hw*hw);
void(*unprepare)(structclk_hw*hw);
int(*is_prepared)(structclk_hw*hw);
void(*unprepare_unused)(structclk_hw*hw);
int(*enable)(structclk_hw*hw);
void(*disable)(structclk_hw*hw);
int(*is_enabled)(structclk_hw*hw);
void(*disable_unused)(structclk_hw*hw);
int(*save_context)(structclk_hw*hw);
void(*restore_context)(structclk_hw*hw);
unsignedlong(*recalc_rate)(structclk_hw*hw,
unsignedlongparent_rate);
long(*round_rate)(structclk_hw*hw,unsignedlongrate,
unsignedlong*parent_rate);
int(*determine_rate)(structclk_hw*hw,
structclk_rate_request*req);
int(*set_parent)(structclk_hw*hw,u8index);
u8(*get_parent)(structclk_hw*hw);
int(*set_rate)(structclk_hw*hw,unsignedlongrate,
unsignedlongparent_rate);
int(*set_rate_and_parent)(structclk_hw*hw,
unsignedlongrate,
unsignedlongparent_rate,u8index);
unsignedlong(*recalc_accuracy)(structclk_hw*hw,
unsignedlongparent_accuracy);
int(*get_phase)(structclk_hw*hw);
int(*set_phase)(structclk_hw*hw,intdegrees);
int(*get_duty_cycle)(structclk_hw*hw,
structclk_duty*duty);
int(*set_duty_cycle)(structclk_hw*hw,
structclk_duty*duty);
int(*init)(structclk_hw*hw);
void(*terminate)(structclk_hw*hw);
void(*debug_init)(structclk_hw*hw,structdentry*dentry);
};

prepare:準備啟動時鐘。該回調直到時鐘完全準備好才會返回,調用clk_enable是安全的。這個回調的目的是允許時鐘實現執行任何可能休眠的初始化。在prepare_lock被持有的情況下調用。

unprepare:將時鐘從準備狀態中釋放出來。該函數通常會撤銷在.prepare回調中完成的工作。在prepare_lock持有的情況下調用。

is_prepared:查詢硬件以確定時鐘是否準備好。允許此函數休眠,如果此操作不是設置后,將使用prepare計數。(可選的)

unprepare_unused:自動取消時鐘準備。只從clk_disable_unused調用,用于特殊需要的時鐘準備。在持有prepare互斥鎖的情況下調用。這個函數可能會休眠。

enable:自動啟用時鐘。該函數直到時鐘正在生成一個有效的時鐘信號之前不能返回,供消費者設備驅動使用。在enable_lock持有情況下調用,該函數必須不能睡眠。

disable:自動禁用時鐘。在enable_lock持有情況下調用,該函數必須不能睡眠。

is_enabled:查詢硬件以確定時鐘是否開啟。這個函數不能休眠。如果此操作不是設置,則enable計數將被使用,該函數可選。

disable_unused:自動禁用時鐘。只從clk_disable_unused調用用于特殊需要的gate時鐘。在enable_lock持有的情況下調用,這個函數不能睡眠。

save_context:保存時鐘上下文,為斷電做準備。

restore_context:在電源恢復后恢復時鐘上下文。

recalc_rate:通過查詢硬件重新計算該時鐘的速率。如果驅動程序不能計算出這個時鐘的速率,它必須返回0。如果此callback未設置,則時鐘速率將初始化為0(可選的)。

round_rate:給定一個目標速率作為輸入,實際上返回由時鐘支持最接近的速率,父速率是一個input/output參數

determine_rate:給定目標速率作為輸入,返回實際上是由時鐘支撐的最接近的速率。

set_parent:改變這個時鐘的輸入源,設置父時鐘。

get_parent:查詢硬件以確定時鐘的父時鐘。

set_rate:改變這個時鐘的速率。

set_rate_and_parent:更改此時鐘的速率和父時鐘。

recalc_accuracy:重新計算一下這個鐘的精度。時鐘的準確性以PPB(十億分之一)表示。父精度為輸入參數。

get_phase:查詢硬件以獲取時鐘的當前相位。

set_phase:將時鐘信號的相位按指定的度數移位。

get_duty_cycle:查詢硬件,獲取時鐘當前占空比。

set_duty_cycle:將占空比應用于由分子(第二個參數)和分母(第三個參數)指定的時鐘信號。

init:執行特定于平臺的初始化魔術。

terminate:釋放由init分配的資源。

debug_init:為這個時鐘設置特定類型的debugfs條目,在為這個時鐘創建了debugfs目錄條目之后,調用它一次。上述結構中的回調函數的實現需根據具體情況而定,每個callback的具體含義根據名稱可以知道,CLK時鐘框架對上層開放的API都會間接調用到這些callback,下表是一個clock硬件矩陣表,用于描述特定應用場景下需要實現的callback:

wKgZoma8gvWAG7_QAAE0yaH1VE0799.png

參數說明:

y表示強制,必須實現。

n表示包含該回調無效或沒有必要實現。

空單元格表示是可選的,或者必須根據具體情況評估實現。

4、struct clk_gate

struct clk_gate用于描述門控時鐘,該結構定義如下:

structclk_gate{
structclk_hwhw;//處理公共接口和特定于硬件的接口。
void__iomem*reg;//寄存器控制門。
u8bit_idx;//單比特控制門。
u8flags;//特定硬件的falg標志。
spinlock_t*lock;//自旋鎖。
};

常用API:

to_clk_gate()

clk_register_gate()/clk_unregister_gate()

5、struct clk

struct clk用于描述一個clk設備,該結構定義如下:

structclk{
structclk_core*core;//表示clk核心。
structdevice*dev;//clk設備的父設備。
constchar*dev_id;//設備id。
constchar*con_id;
unsignedlongmin_rate;//最小頻率。
unsignedlongmax_rate;//最大頻率。
unsignedintexclusive_count;//獨占計數。
structhlist_nodeclks_node;//clk鏈表。
};

6、struct clk_hw

struct clk_hw用于描述特定硬件實列的結構,該結構定義如下:

structclk_hw{
structclk_core*core;//clk核心。
structclk*clk;//clk設備。
conststructclk_init_data*init;//描述clk初始化數據
};

struct clk_hw中包含了struct clk_core和struct clk。可以看成是clk框架中對clk核心和clk設備的封裝。

7、struct clk_divider

struct clk_divider描述可調的分頻時鐘,該結構定義如下:

structclk_divider{
structclk_hwhw;//處理公共接口和特定硬件的接口
void__iomem*reg;//分頻器的寄存器
u8shift;//分頻位域的偏移量
u8width;//分頻位域的寬度
u8flags;//標志
conststructclk_div_table*table;//數組的值/除數對,最后一項div=0。
spinlock_t*lock;//注冊鎖
};

具有影響其輸出頻率的可調分壓器的時鐘。實現.recalc_rate,.set_rate和.round_rate。

常用API:

clk_register_divider()/clk_unregister_divider()

clk_hw_register_divider()/clk_hw_unregister_divider()

8、struct clk_mux

struct clk_mux用于描述多路復用器的時鐘,該結構定義如下:

structclk_mux{
structclk_hwhw;
void__iomem*reg;
constu32*table;
u32mask;
u8shift;
u8flags;
spinlock_t*lock;
};

上述結構中組成元素幾乎與struct clk_divider一樣。

常用API:

voidclk_unregister_mux(structclk*clk);
voidclk_hw_unregister_mux(structclk_hw*hw);

9、struct clk_fixed_factor

struct clk_fixed_factor用于倍頻和分頻時鐘。該結構定義如下:

structclk_fixed_factor{
structclk_hwhw;//處理公共接口和特定硬件的接口。
unsignedintmult;//倍頻器
unsignedintdiv;//分頻器
};

具有固定乘法器和除法器的時鐘。輸出頻率為父時鐘速率除以div再乘以mult。在.recalc_rate,.set_rate和.round_rate中實現。

10、struct clk_fractional_divider

struct clk_fractional_divider用于描述可調分數的分頻時鐘,該結構定義如下:

structclk_fractional_divider{
structclk_hwhw;//處理公共接口和特定硬件的接口
void__iomem*reg;//用于分頻器的寄存器
u8mshift;//分頻位域分子的偏移量
u8mwidth;//分頻位域分子的寬度
u8nshift;//分頻位域分母的偏移量
u8nwidth;//分頻位域分母的寬度
u8flags;//標志位
void(*approximation)(structclk_hw*hw,//近似方法的callback
unsignedlongrate,unsignedlong*parent_rate,
unsignedlong*m,unsignedlong*n);
spinlock_t*lock;//注冊鎖
};

11、struct clk_multiplier

struct clk_multiplier結構用于描述可調的倍頻時鐘,該結構定義如下:

structclk_multiplier{
structclk_hwhw;//處理公共接口和特定硬件的接口
void__iomem*reg;//倍頻器的寄存器
u8shift;//乘法位域的偏移量
u8width;//乘法位域的寬度
u8flags;//標志
spinlock_t*lock;//注冊鎖
};

12、struct clk_composite

struct clk_composite結構用于描述多路復用器、分頻器和門控時鐘的組合時鐘。該結構定義如下:

structclk_composite{
structclk_hwhw;//處理公共接口和特定硬件的接口
structclk_opsops;//clk對應的ops的callback

structclk_hw*mux_hw;//處理復合和硬件特定多路復用時鐘
structclk_hw*rate_hw;//處理復合和硬件特定的頻率時鐘
structclk_hw*gate_hw;//處理之間的組合和硬件特定的門控時鐘

conststructclk_ops*mux_ops;//對mux的時鐘ops
conststructclk_ops*rate_ops;//對rate的時鐘ops
conststructclk_ops*gate_ops;//對gate的時鐘ops
};

常用API:

to_clk_composite()

clk_register_composite()/clk_unregister_composite()

clk核心數據結構如下圖所示:

wKgaoma8gwOABKaMAAEX0TwU8g0922.png

四、CLK調試

參見debugfs文件系統下的文件可推知目前系統中存在的clk情況,使用如下命令:

cat/sys/debug/kernel/clk/clk_summary

查看目前系統的時鐘樹(clk_tree)。例如:wKgZoma8gxCATfrpAAItCtHu2dE808.png

可以在用戶空間通過/sys設置時鐘節點:

//getrate:
cat/sys/kernel/debug/aclk_gmac0/clk_rate

//setrate:
echo24000000>/sys/kernel/debug/aclk_gmac0/clk_rate

//打開clk:
echo1>/sys/kernel/debug/aclk_gmac0/clk_enable_count

//關閉clk:
echo0>/sys/kernel/debug/aclk_gmac0/clk_enable_count

五、CLK信息導出

1、與debugfs調試信息相關的初始化

當內核支持debugfs且開啟對clk的調試支持,我們可以在/sys文件系統路徑中的clk目錄下查看關于系統中所有注冊的clk信息,例如:

wKgZoma8gx2ARW_3AAO5S-iOTWE257.png

每個目錄代表一個clk信息,其目錄下包含如下信息:

wKgZomb5FouAUo8EAABj7IrREB4451.png

從內核源碼角度,創建debugfs調試目錄或文件由clk_debug_init()完成:

staticint__initclk_debug_init(void)
{
structclk_core*core;

#ifdefCLOCK_ALLOW_WRITE_DEBUGFS
pr_warn("
");
pr_warn("********************************************************************
");
pr_warn("**NOTICENOTICENOTICENOTICENOTICENOTICENOTICE**
");
pr_warn("****
");
pr_warn("**WRITEABLEclkDebugFSSUPPORTHASBEENENABLEDINTHISKERNEL**
");
pr_warn("****
");
pr_warn("**Thismeansthatthiskernelisbuilttoexposeclkoperations**
");
pr_warn("**suchasparentorratesetting,enabling,disabling,etc.**
");
pr_warn("**touserspace,whichmaycompromisesecurityonyoursystem.**
");
pr_warn("****
");
pr_warn("**Ifyouseethismessageandyouarenotdebuggingthe**
");
pr_warn("**kernel,reportthisimmediatelytoyourvendor!**
");
pr_warn("****
");
pr_warn("**NOTICENOTICENOTICENOTICENOTICENOTICENOTICE**
");
pr_warn("********************************************************************
");
#endif

rootdir=debugfs_create_dir("clk",NULL);

debugfs_create_file("clk_summary",0444,rootdir,&all_lists,
&clk_summary_fops);
debugfs_create_file("clk_dump",0444,rootdir,&all_lists,
&clk_dump_fops);
debugfs_create_file("clk_orphan_summary",0444,rootdir,&orphan_list,
&clk_summary_fops);
debugfs_create_file("clk_orphan_dump",0444,rootdir,&orphan_list,
&clk_dump_fops);

mutex_lock(&clk_debug_lock);
hlist_for_each_entry(core,&clk_debug_list,debug_node)
clk_debug_create_one(core,rootdir);

inited=1;
mutex_unlock(&clk_debug_lock);

return0;
}

clk_debug_init()函數由late_initcall()(/drivers/clk.c)導出。

六、clk驅動設計

1、底層驅動(clk-provider)

對于一款SOC,特定廠家都會針對時鐘編寫對應的驅動。包括用于以下功能的文件:

用于倍頻的 PLL(鎖相環,Phase Locked Loop)。

用于分頻的Divider。

用于多路選擇的 MUX。

用于CLOCK ENABLE控制的與門。

使用 CLOCK 的硬件模塊(也可稱為CONSUMER)。

在設計這些clk驅動時,本質上是實現對應struct clk_ops下的callback后,調用clk_register注冊進linux內核的時鐘框架。不同的時鐘器件在內核中都存在與之對應的數據結構,且開放有對應的API接口,將其注冊到內核中。

例如Nxp的Imx6ul這款SOC,在/arch/arm/mach-imx/clk-imx6ull.c中則實現了對應時鐘框架的底層驅動,由imx6ul_clocks_init()實現:

CLK_OF_DECLARE(imx6ul,"fsl,imx6ul-ccm",imx6ul_clocks_init);

存在下圖類似的SOC時鐘描述語句:

wKgZoma8gyqAPVRyAANMrtVtnM0887.png

上述語句中無論是imx_clk_mux()還是imx_clk_pllv3()都會調用clk_register()向內核注冊時鐘資源。

2、驅動層clk

當底層(clk-provider)設計完成后,在驅動層(也稱為消費者(Consumer))則可以很方便的獲取對應的clk句柄,并可以進行enable/disable時鐘等操作了。

常用API有:

//查找并獲取對時鐘產生器的引用
structclk*clk_get(structdevice*dev,constchar*id);
structclk*devm_clk_get(structdevice*dev,constchar*id);

//當時鐘源處于運行狀態時,通知系統
intclk_enable(structclk*clk);

//當時鐘源不再使用時,通知系統
voidclk_disable(structclk*clk);

clk_prepare_enable()

在內核源碼中,可以發現很多的驅動設計都會使用到時鐘子系統,用于對外設的控制和開啟/停止。

例如,在Nxp提供的一個名為imx.c(/drivers/tty/serial)的通用uart驅動中,在.probe中則會首先進行時鐘相關的操作:

staticintserial_imx_probe(structplatform_device*pdev)
{
structimx_port*sport;
void__iomem*base;
intret=0;
structresource*res;
inttxirq,rxirq,rtsirq;

sport=devm_kzalloc(&pdev->dev,sizeof(*sport),GFP_KERNEL);
if(!sport)
return-ENOMEM;

ret=serial_imx_probe_dt(sport,pdev);
if(ret>0)
serial_imx_probe_pdata(sport,pdev);
elseif(retdev,res);
if(IS_ERR(base))
returnPTR_ERR(base);

rxirq=platform_get_irq(pdev,0);
txirq=platform_get_irq(pdev,1);
rtsirq=platform_get_irq(pdev,2);

sport->port.dev=&pdev->dev;
sport->port.mapbase=res->start;
sport->port.membase=base;
sport->port.type=PORT_IMX,
sport->port.iotype=UPIO_MEM;
sport->port.irq=rxirq;
sport->port.fifosize=32;
sport->port.ops=&imx_pops;
sport->port.rs485_config=imx_rs485_config;
sport->port.rs485.flags=
SER_RS485_RTS_ON_SEND|SER_RS485_RX_DURING_TX;
sport->port.flags=UPF_BOOT_AUTOCONF;
init_timer(&sport->timer);
sport->timer.function=imx_timeout;
sport->timer.data=(unsignedlong)sport;

sport->clk_ipg=devm_clk_get(&pdev->dev,"ipg");
if(IS_ERR(sport->clk_ipg)){
ret=PTR_ERR(sport->clk_ipg);
dev_err(&pdev->dev,"failedtogetipgclk:%d
",ret);
returnret;
}

sport->clk_per=devm_clk_get(&pdev->dev,"per");
if(IS_ERR(sport->clk_per)){
ret=PTR_ERR(sport->clk_per);
dev_err(&pdev->dev,"failedtogetperclk:%d
",ret);
returnret;
}

sport->port.uartclk=clk_get_rate(sport->clk_per);
if(sport->port.uartclk>IMX_MODULE_MAX_CLK_RATE){
ret=clk_set_rate(sport->clk_per,IMX_MODULE_MAX_CLK_RATE);
if(retdev,"clk_set_rate()failed
");
returnret;
}
}
sport->port.uartclk=clk_get_rate(sport->clk_per);

/*
*AllocatetheIRQ(s)i.MX1hasthreeinterruptswhereaslater
*chipsonlyhaveoneinterrupt.
*/
if(txirq>0){
ret=devm_request_irq(&pdev->dev,rxirq,imx_rxint,0,
dev_name(&pdev->dev),sport);
if(ret)
returnret;

ret=devm_request_irq(&pdev->dev,txirq,imx_txint,0,
dev_name(&pdev->dev),sport);
if(ret)
returnret;
}else{
ret=devm_request_irq(&pdev->dev,rxirq,imx_int,0,
dev_name(&pdev->dev),sport);
if(ret)
returnret;
}

imx_ports[sport->port.line]=sport;

platform_set_drvdata(pdev,sport);

returnuart_add_one_port(&imx_reg,&sport->port);
}

在上述代碼中,與時鐘相關的操作有四個地方:wKgaoma8gzyAC9e2AAI6fT6pPh0460.png

獲取了時鐘,在這個通用uart驅動中的其他相關的回調中,則會依托于時鐘實現這些回調函數,例如imx_startup():

wKgaoma8g0qAY3-YAAFro0UVpQs184.png

綜上所述,可見時鐘框架在linux設備驅動的實現中非常重要,只要與外設相關的驅動實現幾乎都需要使用到時鐘框架。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 內核
    +關注

    關注

    3

    文章

    1372

    瀏覽量

    40294
  • Linux
    +關注

    關注

    87

    文章

    11306

    瀏覽量

    209570
  • 時鐘
    +關注

    關注

    11

    文章

    1734

    瀏覽量

    131496

原文標題:聊聊linux時鐘子系統

文章出處:【微信號:嵌入式小生,微信公眾號:嵌入式小生】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    Linux下輸入子系統上報觸摸屏坐標

    ??在 Linux 中,輸入子系統是由輸入子系統設備驅動層、輸入子系統核心層(Input Core)和輸入子系統事件處理層(Event Ha
    的頭像 發表于 09-25 08:56 ?2506次閱讀
    <b class='flag-5'>Linux</b>下輸入<b class='flag-5'>子系統</b>上報觸摸屏坐標

    Linux LED子系統詳解

    Linux LED子系統詳解
    的頭像 發表于 06-10 10:37 ?1556次閱讀
    <b class='flag-5'>Linux</b> LED<b class='flag-5'>子系統</b>詳解

    深度搜索Linux操作系統系統構建和原理解析

    深度搜索Linux操作系統系統構建和原理解析!比較好的一本Linux內核書籍,從另一個角度去解
    發表于 09-16 16:40

    如何使用Linux內核中的input子系統

    的 input 子系統下提供的 API 函數接口,完成設備的注冊即可。在本章節中我們來學習一下如何使用 Linux內核中的 input 子系統
    發表于 12-29 07:20

    基于Linux內核輸入子系統的驅動研究

    Linux因其完全開放的特性和穩定優良的性能深受歡迎,當推出了內核輸入子系統后,更方便了嵌入式領域的驅動開放。介紹了Linux的設備驅動基礎,詳細闡述了基于Linux內核輸入
    發表于 09-12 16:38 ?23次下載

    Linux內核輸入子系統的驅動研究

    Linux內核輸入子系統的驅動研究
    發表于 10-31 14:41 ?14次下載
    <b class='flag-5'>Linux</b>內核輸入<b class='flag-5'>子系統</b>的驅動研究

    詳細了解Linux設備模型中的input子系統

    linux輸入子系統linux input subsystem)從上到下由三層實現,分別為:輸入子系統事件處理層(EventHandler)、輸入
    發表于 05-12 09:04 ?1052次閱讀
    詳細了解<b class='flag-5'>Linux</b>設備模型中的input<b class='flag-5'>子系統</b>

    解析MSP430系統時鐘資源

    解析MSP430系統時鐘資源
    發表于 09-26 11:39 ?1次下載

    Windows 子系統助力 Linux 2.0

    Windows 子系統助力 Linux 2.0
    的頭像 發表于 01-04 11:17 ?656次閱讀

    Linux系統中NFC子系統架構分析

    目前在Linux系統中,每個廠家都使用不同的方式實現NFC驅動,然后自己在應用層上面做適配。但是Linux也已經推出NFC子系統,很多廠家也逐步在統一。
    發表于 01-04 14:01 ?2072次閱讀

    linux-usb子系統的核心描述

    本文將描述linux-usb子系統的核心,主要分析其核心的初始化流程,文中源碼基于內核版本:4.1.15。
    的頭像 發表于 01-14 09:37 ?2739次閱讀

    Linux內核之LED子系統(一)

    Linux內核的LED子系統是一種重要的框架,用于管理和控制設備上的LED指示燈。在嵌入式系統和物聯網設備中,LED子系統發揮著關鍵作用,為開發者提供了一種統一的方式來控制和定制LED
    發表于 10-02 16:53 ?1361次閱讀
    <b class='flag-5'>Linux</b>內核之LED<b class='flag-5'>子系統</b>(一)

    Linux reset子系統有什么功能

    Linux reset子系統 reset子系統非常簡單,與clock子系統非常類似,但在驅動實現上,reset驅動更簡單。 因為clock驅動主要是
    的頭像 發表于 09-27 14:06 ?770次閱讀
    <b class='flag-5'>Linux</b> reset<b class='flag-5'>子系統</b>有什么功能

    Linux clock子系統是什么

    clock子系統 Linux時鐘子系統由CCF(common clock framework)框架管理, CCF向上給用戶提供了通用的時鐘
    的頭像 發表于 09-27 14:25 ?849次閱讀
    <b class='flag-5'>Linux</b> clock<b class='flag-5'>子系統</b>是什么

    時鐘子系統中clock驅動實例

    clock驅動實例 clock驅動在時鐘子系統中屬于provider,provider是時鐘的提供者,即具體的clock驅動。 clock驅動在Linux剛啟動的時候就要完成,比 in
    的頭像 發表于 09-27 14:39 ?807次閱讀
    <b class='flag-5'>時鐘</b><b class='flag-5'>子系統</b>中clock驅動實例
    主站蜘蛛池模板: 爱操综合网| 亚洲香蕉久久| 超级香蕉97视频在线观看一区| 免费国产小视频| 久久久福利| 日本小视频免费| 日韩色网| 欧美成人精品| 九色精品在线| 手机在线观看你懂的| 天堂种子| 欧美色图亚洲综合| 四虎永久在线精品国产免费| 四虎网址| 欧美+日本+国产+在线观看| 四虎影院在线免费观看视频| 日韩欧美亚洲综合久久影院d3| 欧美性色生活片天天看99| 成人欧美一区二区三区的电影| 成人五级毛片免费播放| 在线天堂中文在线网| 国产精品视频永久免费播放| 六月婷婷久久| 男人的天堂免费网站| 河南毛片| 国内精品久久久久久久久蜜桃 | 欧美ggg666| 把小嫩嫩曰出白浆| 欧美色亚洲图| 亚洲一区二区三区免费视频 | 神马影视午夜| 福利99| 人人爽人人澡| 色婷婷激婷婷深爱五月小说| 久久精品亚瑟全部免费观看| 91精品国产免费久久久久久青草| 人人爽天天爽夜夜爽曰| 美国bj69| 亚洲qingse中文久久网| 亚洲www色| 美女视频黄视大全视频免费网址|