概念
Linux內(nèi)核從3.x開始引入設(shè)備樹的概念,用于實(shí)現(xiàn)驅(qū)動(dòng)代碼與設(shè)備信息相分離。在設(shè)備樹出現(xiàn)以前,所有關(guān)于設(shè)備的具體信息都要寫在驅(qū)動(dòng)里,一旦外圍設(shè)備變化,驅(qū)動(dòng)代碼就要重寫。引入了設(shè)備樹之后,驅(qū)動(dòng)代碼只負(fù)責(zé)處理驅(qū)動(dòng)的邏輯,而關(guān)于設(shè)備的具體信息存放到設(shè)備樹文件中,這樣,如果只是硬件接口信息的變化而沒有驅(qū)動(dòng)邏輯的變化,驅(qū)動(dòng)開發(fā)者只需要修改設(shè)備樹文件信息,不需要改寫驅(qū)動(dòng)代碼。比如在ARM Linux內(nèi),一個(gè).dts(device tree source)文件對應(yīng)一個(gè)ARM的machine,一般放置在內(nèi)核的"arch/arm/boot/dts/"目錄內(nèi),比如exynos4412參考板的板級(jí)設(shè)備樹文件就是"arch/arm/boot/dts/exynos4412-origen.dts"。這個(gè)文件可以通過$make dtbs命令編譯成二進(jìn)制的.dtb文件供內(nèi)核驅(qū)動(dòng)使用。
基于同樣的軟件分層設(shè)計(jì)的思想,由于一個(gè)SoC可能對應(yīng)多個(gè)machine,如果每個(gè)machine的設(shè)備樹都寫成一個(gè)完全獨(dú)立的.dts文件,那么勢必相當(dāng)一些.dts文件有重復(fù)的部分,為了解決這個(gè)問題,Linux設(shè)備樹目錄把一個(gè)SoC公用的部分或者多個(gè)machine共同的部分提煉為相應(yīng)的.dtsi文件。這樣每個(gè).dts就只有自己差異的部分,公有的部分只需要"include"相應(yīng)的.dtsi文件, 這樣就是整個(gè)設(shè)備樹的管理更加有序。我這里用`Linux4.8.5源碼自帶的dm9000網(wǎng)卡為例來分析設(shè)備樹的使用和移植。這個(gè)網(wǎng)卡的設(shè)備樹節(jié)點(diǎn)信息在"Documentation/devicetree/bindings/net/davicom-dm9000.txt"有詳細(xì)說明,其網(wǎng)卡驅(qū)動(dòng)源碼是"drivers/net/ethernet/davicom/dm9000.c"。
設(shè)備樹框架
設(shè)備樹用樹狀結(jié)構(gòu)描述設(shè)備信息,它有以下幾種特性
每個(gè)設(shè)備樹文件都有一個(gè)根節(jié)點(diǎn),每個(gè)設(shè)備都是一個(gè)節(jié)點(diǎn)。
節(jié)點(diǎn)間可以嵌套,形成父子關(guān)系,這樣就可以方便的描述設(shè)備間的關(guān)系。
每個(gè)設(shè)備的屬性都用一組key-value對(鍵值對)來描述。
每個(gè)屬性的描述用;結(jié)束
所以,一個(gè)設(shè)備樹的基本框架可以寫成下面這個(gè)樣子
/{ //根節(jié)點(diǎn) node1{ //node1是節(jié)點(diǎn)名,是/的子節(jié)點(diǎn) key=value; //node1的屬性 ... node2{ //node2是node1的子節(jié)點(diǎn) key=value; //node2的屬性 ... } } //node1的描述到此為止 node3{ key=value; ... }}
節(jié)點(diǎn)名
理論個(gè)節(jié)點(diǎn)名只要是長度不超過31個(gè)字符的ASCII字符串即可,此外
Linux內(nèi)核還約定設(shè)備名應(yīng)寫成形如[@]的形式,其中name就是設(shè)備名,unit_address就是設(shè)備地址,如果有應(yīng)該寫上,下面就是典型節(jié)點(diǎn)名的寫法
Linux中的設(shè)備樹還包括幾個(gè)特殊的節(jié)點(diǎn),比如chosen,chosen節(jié)點(diǎn)不描述一個(gè)真實(shí)設(shè)備,而是用于firmware傳遞一些數(shù)據(jù)給OS,比如bootloader傳遞內(nèi)核啟動(dòng)參數(shù)給內(nèi)核
引用
當(dāng)我們找一個(gè)節(jié)點(diǎn)的時(shí)候,我們必須書寫完整的節(jié)點(diǎn)路徑,這樣當(dāng)一個(gè)節(jié)點(diǎn)嵌套比較深的時(shí)候就不是很方便,所以,設(shè)備樹允許我們用下面的形式為節(jié)點(diǎn)標(biāo)注引用(起別名),借以省去冗長的路徑。這樣就可以實(shí)現(xiàn)類似函數(shù)調(diào)用的效果。編譯設(shè)備樹的時(shí)候,相同的節(jié)點(diǎn)的不同屬性信息都會(huì)被合并到設(shè)備節(jié)點(diǎn)中,而相同的屬性會(huì)被覆蓋,使用引用可以避免移植者四處找節(jié)點(diǎn),直接在板級(jí).dts增改即可。
下面的例子中就是直接引用了dtsi中的一個(gè)節(jié)點(diǎn),并向其中添加/修改新的屬性信息
KEY
在設(shè)備樹中,鍵值對是描述屬性的方式,比如,Linux驅(qū)動(dòng)中可以通過設(shè)備節(jié)點(diǎn)中的"compatible"這個(gè)屬性查找設(shè)備節(jié)點(diǎn)。
Linux設(shè)備樹語法中定義了一些具有規(guī)范意義的屬性,包括:compatible,?address,?interrupt等,這些信息能夠在內(nèi)核初始化找到節(jié)點(diǎn)的時(shí)候,自動(dòng)解析生成相應(yīng)的設(shè)備信息。此外,還有一些Linux內(nèi)核定義好的,一類設(shè)備通用的有默認(rèn)意義的屬性,這些屬性一般不能被內(nèi)核自動(dòng)解析生成相應(yīng)的設(shè)備信息,但是內(nèi)核已經(jīng)編寫的相應(yīng)的解析提取函數(shù),常見的有?"mac_addr","gpio","clock","power"。"regulator"?等等。
compatible
設(shè)備節(jié)點(diǎn)中對應(yīng)的節(jié)點(diǎn)信息已經(jīng)被內(nèi)核構(gòu)造成struct platform_device。驅(qū)動(dòng)可以通過相應(yīng)的函數(shù)從中提取信息。compatible屬性是用來查找節(jié)點(diǎn)的方法之一,另外還可以通過節(jié)點(diǎn)名或節(jié)點(diǎn)路徑查找指定節(jié)點(diǎn)。dm9000驅(qū)動(dòng)中就是使用下面這個(gè)函數(shù)通過設(shè)備節(jié)點(diǎn)中的"compatible"屬性提取相應(yīng)的信息,所以二者的字符串需要嚴(yán)格匹配。
address
(幾乎)所有的設(shè)備都需要與CPU的IO口相連,所以其IO端口信息就需要在設(shè)備節(jié)點(diǎn)節(jié)點(diǎn)中說明。常用的屬性有
#address-cells,用來描述子節(jié)點(diǎn)"reg"屬性的地址表中用來描述首地址的cell的數(shù)量,
#size-cells,用來描述子節(jié)點(diǎn)"reg"屬性的地址表中用來描述地址長度的cell的數(shù)量。
有了這兩個(gè)屬性,子節(jié)點(diǎn)中的"reg"就可以描述一塊連續(xù)的地址區(qū)域。下例中,父節(jié)點(diǎn)中指定了"#address-cells = <2>" "#size-cells = <1>",則子節(jié)點(diǎn)dev-bootscs0中的reg中的前兩個(gè)數(shù)表示一個(gè)地址,最后的0x4表示地址跨度是0x4
interrupts
一個(gè)計(jì)算機(jī)系統(tǒng)中大量設(shè)備都是通過中斷請求CPU服務(wù)的,所以設(shè)備節(jié)點(diǎn)中就需要在指定中斷號(hào)。常用的屬性有
interrupt-controller?一個(gè)空屬性用來聲明這個(gè)node接收中斷信號(hào)
#interrupt-cells,是中斷控制器節(jié)點(diǎn)的屬性,用來標(biāo)識(shí)這個(gè)控制器需要幾個(gè)單位做中斷描述符,用來描述子節(jié)點(diǎn)中"interrupts"屬性使用了父節(jié)點(diǎn)中的interrupts屬性的具體的哪個(gè)值。一般,如果父節(jié)點(diǎn)的該屬性的值是3,則子節(jié)點(diǎn)的interrupts一個(gè)cell的三個(gè)32bits整數(shù)值分別為:<中斷域 中斷 觸發(fā)方式>,如果父節(jié)點(diǎn)的該屬性是2,則是<中斷 觸發(fā)方式>
interrupt-parent,標(biāo)識(shí)此設(shè)備節(jié)點(diǎn)屬于哪一個(gè)中斷控制器,如果沒有設(shè)置這個(gè)屬性,會(huì)自動(dòng)依附父節(jié)點(diǎn)的
interrupts,一個(gè)中斷標(biāo)識(shí)符列表,表示每一個(gè)中斷輸出信號(hào)
這里,在我板子上的dm9000的的設(shè)備節(jié)點(diǎn)中,"interrupt-parent"使用了exynos4x12-pinctrl.dtsi(被板級(jí)設(shè)備樹的exynos4412.dtsi包含)中的gpx0節(jié)點(diǎn)的引用,而在gpx0節(jié)點(diǎn)中,指定了"#interrupt-cells = <2>;",所以在dm9000中的屬性"interrupts = <6 4>;"表示指定gpx0中的屬性"interrupts"中的"<0 22 0>",通過查閱exynos4412的手冊知道,對應(yīng)的中斷號(hào)是EINT[6]。
gpio
gpio也是最常見的IO口,常用的屬性有
"gpio-controller",用來說明該節(jié)點(diǎn)描述的是一個(gè)gpio控制器
"#gpio-cells",用來描述gpio使用節(jié)點(diǎn)的屬性一個(gè)cell的內(nèi)容,即?屬性 = <&引用GPIO節(jié)點(diǎn)別名 GPIO標(biāo)號(hào) 工作模式>
驅(qū)動(dòng)自定義key
針對具體的設(shè)備,有部分屬性很難做到通用,需要驅(qū)動(dòng)自己定義好,通過內(nèi)核的屬性提取解析函數(shù)進(jìn)行值的獲取,比如dm9000節(jié)點(diǎn)中的下面這句就是自定義的節(jié)點(diǎn)屬性,用以表示配置EEPROM不可用。
VALUE
dts描述一個(gè)鍵的值有多種方式,當(dāng)然,一個(gè)鍵也可以沒有值
字符串信息
32bit無符號(hào)整型數(shù)組信息
二進(jìn)制數(shù)數(shù)組
字符串哈希表
混合形式
上述幾種的混合形式
設(shè)備樹/驅(qū)動(dòng)移植
設(shè)備樹就是為驅(qū)動(dòng)服務(wù)的,配置好設(shè)備樹之后還需要配置相應(yīng)的驅(qū)動(dòng)才能檢測配置是否正確。比如dm9000網(wǎng)卡,就需要首先將示例信息掛接到我們的板級(jí)設(shè)備樹上,并根據(jù)芯片手冊和電路原理圖將相應(yīng)的屬性進(jìn)行配置,再配置相應(yīng)的驅(qū)動(dòng)。需要注意的是,dm9000的地址線一般是接在片選線上的,我這里用的exynos4412,接在了bank1,所以是"<0x50000000 0x2 0x50000004 0x2>"
最終的配置結(jié)果是:
勾選相應(yīng)的選項(xiàng)將dm9000的驅(qū)動(dòng)編譯進(jìn)內(nèi)核。
make menuconfig[*] Networking support ---> Networking options ---> <*> Packet socket <*>Unix domain sockets [*] TCP/IP networking [*] IP: kernel level autoconfigurationDevice Drivers ---> [*] Network device support ---> [*] Ethernet driver support (NEW) ---> <*> DM9000 supportFile systems ---> [*] Network File Systems (NEW) ---> <*> NFS client support [*] NFS client support for NFS version 3 [*] NFS client support for the NFSv3 ACL protocol extension [*] Root file system on NFS
執(zhí)行make uImage;make dtbs,tftp下載,成功加載nfs根文件系統(tǒng)并進(jìn)入系統(tǒng),表示網(wǎng)卡移植成功
?
評論
查看更多