作者:linuxer 發(fā)布于:2014-7-26 18:24 分類:Linux內(nèi)核分析 一、前言 在linux2.6內(nèi)核上工作的嵌入式軟件工程師在pin control上都會遇到這樣的狀況: (1)啟動一個新的項目后,需要根據(jù)硬件平臺的設定進行pin control相關(guān)的編碼。例如:在bootloader中建立一個大的table,描述各個引腳的配置和缺省狀態(tài)。此外,由于SOC的引腳是可以復用的,因此在各個具體的driver中,也可能會對引腳進行的配置。這些工作都是比較繁瑣的工作,需要極大的耐心和細致度。 (2)發(fā)現(xiàn)某個driver不能正常工作,辛辛苦苦debug后發(fā)現(xiàn)僅僅是因為其他的driver在初始化的過程中修改了引腳的配置,導致自己的driver無法正常工作 (3)即便是主CPU是一樣的項目,但是由于外設的不同,我們也不能使用一個kernel image,而是必須要修改代碼(這些代碼主要是board-specific startup code) (4)代碼不是非常的整潔,cut-and-pasted代碼滿天飛,linux中的冗余代碼太多 作為一個嵌入式軟件工程師,項目做多了,接觸的CPU就多了,摔的跤就多了,之后自然會去思考,我們是否可以解決上面的問題呢?此外,對于基于ARM core那些SOC,雖然表面上看起來各個SOC各不相同,但是在pin control上還有很多相同的內(nèi)容的,是否可以把它抽取出來,進行進一步的抽象呢?新版本中的內(nèi)核(本文以3.14版本內(nèi)核為例)提出了pin control subsystem來解決這些問題。
二、pin control subsystem的文件列表 1、源文件列表 我們整理linux/drivers/pinctrl目錄下pin control subsystem的源文件列表如下:
在pin controller driver文檔中 ,我們以2416的pin controller為例,描述了一個具體的low level的driver,這個driver涉及的文件包括pinctrl-samsung.c,pinctrl-samsung.h和pinctrl-s3c24xx.c。 2、和其他內(nèi)核模塊接口頭文件 很多內(nèi)核的其他模塊需要用到pin control subsystem的服務,這些頭文件就定義了pin control subsystem的外部接口以及相關(guān)的數(shù)據(jù)結(jié)構(gòu)。我們整理linux/include/linux/pinctrl目錄下pin control subsystem的外部接口頭文件列表如下:
3、Low level pin controller driver接口 我們整理linux/include/linux/pinctrl目錄下pin control subsystem提供給底層specific pin controller driver的頭文件列表如下:
三、pin control subsystem的軟件框架圖 1、功能和接口概述 一般而言,學習復雜的軟件組件或者軟件模塊是一個痛苦的過程。我們可以把我們要學習的那個軟件block看成一個黑盒子,不論里面有多么復雜,第一步總是先了解其功能和外部接口特性。如果你愿意,你可以不去看其內(nèi)部實現(xiàn),先自己思考其內(nèi)部邏輯,并形成若干問題,然后帶著這些問題去看代碼,往往事半功倍。 (1)、功能規(guī)格。pin control subsystem的主要功能包括: (A)管理系統(tǒng)中所有可以控制的pin。在系統(tǒng)初始化的時候,枚舉所有可以控制的pin,并標識這些pin。 (B)管理這些pin的復用(Multiplexing)。對于SOC而言,其引腳除了配置成普通GPIO之外,若干個引腳還可以組成一個pin group,形成特定的功能。例如pin number是{ 0, 8, 16, 24 }這四個引腳組合形成一個pin group,提供SPI的功能。pin control subsystem要管理所有的pin group。 (C)配置這些pin的特性。例如配置該引腳上的pull-up/down電阻,配置drive strength等 (2)接口規(guī)格。linux內(nèi)核的某個軟件組件必須放回到linux系統(tǒng)中才容易探討它的接口以及在系統(tǒng)中的位置,因此,在本章的第二節(jié)會基于系統(tǒng)block上描述各個pin control subsystem和其他內(nèi)核模塊的接口。 (3)內(nèi)部邏輯。要研究一個subsystem的內(nèi)部邏輯,首先要打開黑盒子,細分模塊,然后針對每一個模塊進行功能分析、外部接口分析、內(nèi)部邏輯分析。如果模塊還是比較大,難于掌握,那么就繼續(xù)細分,拆成子模塊,重復上面的分析過程。在本章的第三節(jié)中,我們打開pin control subsystem的黑盒子進行進一步的分析。 2、pin control subsystem在和其他linux內(nèi)核模塊的接口關(guān)系圖如下圖所示: pin control subsystem會向系統(tǒng)中的其他driver提供接口以便進行該driver的pin config和pin mux的設定,這部分的接口在第四章描述。理想的狀態(tài)是GPIO controll driver也只是象UART,SPI這樣driver一樣和pin control subsystem進行交互,但是,實際上由于各種源由(后文詳述),pin control subsystem和GPIO subsystem必須有交互,這部分的接口在第五章描述。第六章描述了Driver model和pin control subsystem的接口,第七章描述了為Pin control subsystem提供database支持的Device Tree和Machine driver的接口。
3、pin control subsystem內(nèi)部block diagram 起始理解了接口部分內(nèi)容,閱讀和解析pin control subsystem的內(nèi)部邏輯已經(jīng)很簡單,本文就不再分析了。
四、pin control subsystem向其他driver提供的接口 當你準備撰寫一個普通的linux driver(例如串口驅(qū)動)的時候,你期望pin control subsystem提供的接口是什么樣子的?簡單,當然最好是簡單的,最最好是沒有接口,當然這是可能的,具體請參考第六章的接口。 1、概述 普通driver調(diào)用pin control subsystem的主要目標是: (1)設定該設備的功能復用。設定設備的功能復用需要了解兩個概念,一個是function,另外一個pin group。function是功能抽象,對應一個HW邏輯block,例如SPI0。雖然給定了具體的gunction name,我們并不能確定其使用的pins的情況。例如:為了設計靈活,芯片內(nèi)部的SPI0的功能可能引出到pin group { A8, A7, A6, A5 },也可能引出到另外一個pin group{ G4, G3, G2, G1 },但毫無疑問,這兩個pin group不能同時active,畢竟芯片內(nèi)部的SPI0的邏輯功能電路只有一個。 因此,只有給出function selector(所謂selector就是一個ID或者index)以及function的pin group selector才能進行function mux的設定。 (2)設定該device對應的那些pin的電氣特性。 此外,由于電源管理的要求,某個device可能處于某個電源管理狀態(tài),例如idle或者sleep,這時候,屬于該device的所有的pin就會需要處于另外的狀態(tài)。綜合上述的需求,我們把定義了pin control state的概念,也就是說設備可能處于非常多的狀態(tài)中的一個,device driver可以切換設備處于的狀態(tài)。為了方便管理pin control state,我們又提出了一個pin control state holder的概念,用來管理一個設備的所有的pin control狀態(tài)。因此普通driver調(diào)用pin control subsystem的接口從邏輯上將主要是: (1)獲取pin control state holder的句柄 (2)設定pin control狀態(tài) (3)釋放pin control state holder的句柄 pin control state holder的定義如下:
系統(tǒng)中的每一個需要和pin control subsystem進行交互的設備在進行設定之前都需要首先獲取這個句柄。而屬于該設備的所有的狀態(tài)都是掛入到一個鏈表中,鏈表頭就是pin control state holder的states成員,一個state的定義如下:
一個pin state包含若干個setting,所有的settings被掛入一個鏈表中,鏈表頭就是pin state中的settings成員,定義如下:
當driver設定一個pin state的時候,pin control subsystem內(nèi)部會遍歷該state的settings鏈表,將一個一個的setting進行設定。這些settings有各種類型,定義如下:
有pin mux相關(guān)的設定(PIN_MAP_TYPE_MUX_GROUP),定義如下:
有了function selector以及屬于該functiong的roup selector就可以進行該device和pin mux相關(guān)的設定了。設定電氣特性的settings定義如下:
2、具體的接口 (1)devm_pinctrl_get和pinctrl_get。devm_pinctrl_get是Resource managed版本的pinctrl_get,核心還是pinctrl_get函數(shù)。這兩個接口都是獲取設備(設備模型中的struct device)的pin control state holder(struct pinctrl)。pin control state holder不是靜態(tài)定義的,一般在第一次調(diào)用該函數(shù)的時候會動態(tài)創(chuàng)建。創(chuàng)建一個pin control state holder是一個大工程,我們分析一下這段代碼:
(2)devm_pinctrl_put和pinctrl_put。是(1)接口中的逆函數(shù)。devm_pinctrl_get和pinctrl_get獲取句柄的時候申請了很多資源,在devm_pinctrl_put和pinctrl_put可以釋放。需要注意的是多次調(diào)用get函數(shù)不會重復分配資源,只會reference count加一,在put中referrenct count減一,當count==0的時候才釋放該device的pin control state holder持有的所有資源。 (3)pinctrl_lookup_state。根據(jù)state name在pin control state holder找到對應的pin control state。具體的state是各個device自己定義的,不過pin control subsystem自己定義了一些標準的pin control state,定義在pinctrl-state.h文件中:
(4)pinctrl_select_state。設定一個具體的pin control state接口。
五、和GPIO subsystem交互 1、為何pin control subsystem要和GPIO subsystem交互? GPIO的HW block應該和其他功能復用的block是對等關(guān)系的,它們共同輸入到一個復用器block,這個block的寄存器控制哪一個功能電路目前是active的。pin configuration是全局的,不論哪種功能是active的,都可以針對pin進行電氣特性的設定。這樣的架構(gòu)下,上圖中紅色邊框的三個block是完全獨立的HW block,其控制寄存器在SOC datasheet中應該是分成三個章節(jié)描述,同時,這些block的寄存器應該分別處于不同的地址區(qū)間。 對于軟件工程師,我們可以讓pin control subsystem和GPIO subsystem完全獨立,各自進行初始化,各自映射自己的寄存器地址空間,對于pin control subsystem而言,GPIO和其他的HW block沒有什么不同,都是使用自己提供服務的一個軟件模塊而已。然而實際上SOC的設計并非總是向軟件工程師期望的那樣,有的SOC的設計框架圖如下: 這時候,GPIO block是alway active的,而紅色邊框的三個block是緊密的捆綁在一起,它們的寄存器占據(jù)了一個memory range(datasheet中用一個章節(jié)描述這三個block)。這時候,對于軟件工程師來說就有些糾結(jié)了,本來不屬于我的GPIO控制也被迫要參與進來。這時候,硬件寄存器的控制都是pin controller來處理,GPIO相關(guān)的操作都要經(jīng)過pin controller driver,這時候,pin controller driver要作為GPIO driver的back-end出現(xiàn)。 2、具體的接口形態(tài) (1)pinctrl_request_gpio。該接口主要用來申請GPIO。GPIO也是一種資源,使用前應該request,使用完畢后釋放。具體的代碼如下:
毫無疑問,申請GPIO資源本應該是GPIO subsystem的責任,但是由于上一節(jié)描述的源由,pin control subsystem提供了這樣一個接口函數(shù)供GPIO driver使用(其他的內(nèi)核driver不應該調(diào)用,它們應該使用GPIO subsystem提供的接口)。多么丑陋的代碼,作為pin control subsystem,除了維護pin space中的ID,還要維護GPIO 的ID以及pin ID和GPIO ID的關(guān)系。 A:根據(jù)GPIO ID找到該ID對應的pin control device(struct pinctrl_dev)和GPIO rang(pinctrl_gpio_range)。在core driver中,每個low level的pin controller device都被映射成一個struct pinctrl_dev,并形成鏈表,鏈表頭就是pinctrldev_list。由于實際的硬件設計(例如GPIO block被分成若干個GPIO 的bank,每個bank就對應一個HW GPIO Controller Block),一個pin control device要管理的GPIO ID是分成區(qū)域的,每個區(qū)域用struct pinctrl_gpio_range來抽象,在low level 的pin controller初始化的時候(具體參考samsung_pinctrl_register的代碼),會調(diào)用pinctrl_add_gpio_range將每個GPIO bank表示的gpio range掛入到pin control device的range list中(gpio_ranges成員)。pinctrl_gpio_range 的定義如下:
pin ID和GPIO ID有兩種映射關(guān)系,一種是線性映射(這時候pin_base有效),也就是說,對于這個GPIO range,GPIO base ID是a,pin ID base是b,那么a<--->b,a+1<--->b+1,a+2<--->b+2,以此類推。對于非線性映射(pin_base無效,pins是有效的),我們需要建立一個lookup table,以GPIO ID為索引,可以找到對于的pin ID。 B:這里主要是進行復用功能的設定,畢竟GPIO也是引腳的一個特定的功能。pinmux_request_gpio函數(shù)的作用主要有兩個,一個是在core driver中標記該pin已經(jīng)用作GPIO了,這樣,如果有模塊后續(xù)request該資源,那么core driver可以拒絕不合理的要求。第二步就是調(diào)用底層pin controller driver的callback函數(shù),進行底層寄存器相關(guān)的操作。 (2)pinctrl_free_gpio。有申請就有釋放,這是pinctrl_request_gpio的逆函數(shù) (3)pinctrl_gpio_direction_input和pinctrl_gpio_direction_output。為已經(jīng)指定為GPIO功能的引腳設定方向,輸入或者輸出。代碼很簡單,不再贅述。
六、和驅(qū)動模型的接口 前文已經(jīng)表述過,最好是讓統(tǒng)一設備驅(qū)動模型(Driver model)來處理pin 的各種設定。與其自己寫代碼調(diào)用devm_pinctrl_get、pinctrl_lookup_state、pinctrl_select_state等pin control subsystem的接口函數(shù),為了不讓linux內(nèi)核自己的框架處理呢。本章將分析具體的代碼,這些代碼實例對自己driver調(diào)用pin control subsystem的接口函數(shù)來設定本device的pin control的相關(guān)設定也是有指導意義的。 linux kernel中的驅(qū)動模型提供了driver和device的綁定機制,一旦匹配會調(diào)用probe函數(shù)如下:
pinctrl_bind_pins的代碼如下:
(1)struct device數(shù)據(jù)結(jié)構(gòu)有一個pins的成員,它描述了和該設備相關(guān)的pin control的信息,定義如下:
(2)調(diào)用devm_pinctrl_get獲取該device對應的 pin control state holder句柄。 (3)搜索default state,sleep state,idle state并記錄在本device中 (3)將該設備設定為pin default state
七、和device tree或者machine driver相關(guān)的接口 1、概述 device tree或者machine driver這兩個模塊主要是為 pin control subsystem提供pin mapping database的支持。這個database的每個entry用下面的數(shù)據(jù)結(jié)構(gòu)表示:
2、通過machine driver靜態(tài)定義的數(shù)據(jù)來建立pin mapping database machine driver定義一個巨大的mapping table,描述,然后在machine初始化的時候,調(diào)用pinctrl_register_mappings將該table注冊到pin control subsystem中。 3、通過device tree來建立pin mapping database pin mapping信息定義在dts中,主要包括兩個部分,一個是定義在各個具體的device node中,另外一處是定義在pin controller的device node中。 一個典型的device tree中的外設node定義如下(建議先看看pin controller driver的第二章關(guān)于dts的描述):
對普通device的dts分析在函數(shù)pinctrl_dt_to_map中,代碼如下:
(1)pinctrl-0 pinctrl-1 pinctrl-2……表示了該設備的一個個的狀態(tài),這里我們定義了兩個pinctrl-0和pinctrl-1分別對應sleep和default狀態(tài)。這里每次循環(huán)分析一個pin state。 (2)代碼執(zhí)行到這里,size和list分別保存了該pin state中所涉及pin configuration phandle的數(shù)目以及phandle的列表 (3)讀取從pinctrl-names屬性中獲取state name (4)如果沒有定義pinctrl-names屬性,那么我們將pinctrl-0 pinctrl-1 pinctrl-2……中的那個ID取出來作為state name (5)遍歷一個pin state中的pin configuration list,這里的pin configuration實際應該是pin controler device node中的sub node,用phandle標識。 (6)用phandle作為索引,在device tree中找他該phandle表示的那個pin configuration (7)分析一個pin configuration,具體下面會仔細分析 (8)如果該設備沒有定義pin configuration,那么也要創(chuàng)建一個dummy的pin state。 這里我們已經(jīng)進入對pin controller node下面的子節(jié)點的分析過程了。分析一個pin configuration的代碼如下:
(1)首先找到該pin configuration node對應的parent node(也就是pin controler對應的node),如果找不到或者是root node,則進入出錯處理。 (2)獲取pin control class device (3)一旦找到pin control class device則跳出for循環(huán) (4)調(diào)用底層的callback函數(shù)處理pin configuration node。這也是合理的,畢竟很多的pin controller bindings是需要自己解析的。 (5)將該pin configuration node的mapping entry信息注冊到系統(tǒng)中
八、core driver和low level pin controller driver的接口規(guī)格 pin controller描述符。每一個特定的pin controller都用一個struct pinctrl_desc來抽象,具體如下:
pin controller描述符需要描述它可以控制多少個pin(成員npins),每一個pin的信息為何?(成員pins)。這兩個成員就確定了一個pin controller所能控制的引腳的信息。 pin controller描述符中包括了三類操作函數(shù):pctlops是一些全局的控制函數(shù),pmxops是復用引腳相關(guān)的操作函數(shù),confops操作函數(shù)是用來配置引腳的特性(例如:pull-up/down)。struct pinctrl_ops中各個callback函數(shù)的具體的解釋如下:
復用引腳相關(guān)的操作函數(shù)的具體解釋如下:
配置引腳的特性的struct pinconf_ops數(shù)據(jù)結(jié)構(gòu)的各個成員定義如下:
|
|