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