1)摘自【正點原子】領航者 ZYNQ 之Linux驅動開發指南
2)實驗平臺:正點原子領航者ZYNQ開發板
3)平臺購買地址:https://item.taobao.com/item.htm?&id=606160108761
4)全套實驗源碼+手冊+視頻下載:http://www.openedv.com/docs/boards/fpga/zdyz_linhanz.html
5)對正點原子FPGA感興趣的同學可以加群討論:876744900
6)關注正點原子公眾號,獲取最新資料
第二十四章Linux設備樹
前面章節中我們多次提到「設備樹」這個概念,因為時機未到,所以當時並沒有詳細的講解什麼是「設備樹」,本章我們就來詳細的談一談設備樹。掌握設備樹是Linux驅動開發人員必備的技能!因為在新版本的Linux內核中,設備驅動基本全部採用了設備樹(也有支持老式驅動的,比較少)的方式,最新出的CPU其驅動開發也基本都是基於設備樹的,我們所使用的Linux版本為4.14.0,肯定是支持設備樹的,所以正點原子領航者開發板的所有Linux驅動都是基於設備樹的。本章我們就來了解一下設備樹的起源、重點學習一下設備樹語法。
24.1什麼是設備樹?
在舊版本(大概是3.x以前的版本)的linux內核當中,ARM架構的板級硬體設備信息被硬編碼在arch/arm/plat-xxx和arch/arm/mach-xxx目錄下的文件當中,例如板子上的platform設備信息、設備I/O資源resource、板子上的i2c設備的描述信息信息i2c_board_info、板子上spi設備的描述信息spi_board_info以及各種硬體設備的platform_data等,所以就導致在Linux內核源碼中大量的arch/arm/mach-xxx和arch/arm/plat-xxx文件夾,這些文件夾裡面的文件就描述了對應平臺下的板級硬體設備信息。比如在arch/arm/mach-s3c24xx/mach-smdk2440.c文件中有如下內容(有縮減):
示例代碼24.1.1 mach-smdk2440.c文件代碼片段
複製代碼
上述代碼中的結構體變量smdk2440_fb_info就是描述SMDK2440這個開發板上的LCD硬體信息的,結構體指針數組smdk2440_devices描述的是SMDK2440這個開發板上的所有硬體相關信息。這個僅僅是使用2440這個晶片的SMDK2440開發板下的LCD信息,SMDK2440開發板還有很多的其他外設硬體和平臺硬體信息。使用2440這個晶片的板子有很多,每個板子都有描述相應板級硬體信息的文件,這僅僅只是一個2440。隨著智慧型手機的發展,每年新出的ARM架構晶片少說都在數十、數百款,Linux內核下板級信息文件將會成指數級增長!這些板級信息文件都是.c或.h文件,都會被硬編碼進Linux內核中,導致Linux內核「虛胖」。就好比你喜歡吃自助餐,然後花了100多到一家宣傳看著很不錯的自助餐廳,結果你想吃的牛排、海鮮、烤肉基本沒多少,全都是一些涼菜、炒麵、西瓜、飲料等小吃,相信你此時肯定會脫口而出一句「F*k!」、「騙子!」。
這些板級硬體信息代碼對linux內核來說只不過是垃圾代碼而已,所以當Linux之父linus看到ARM社區向Linux內核添加了大量「無用」、冗餘的板級信息文件,不禁的發出了一句「This whole ARM thing is a f*cking pain in the ass」。從此以後ARM社區就開始引入設備樹DTS了。
DTS即Device Tree Source設備樹源碼, Device Tree是一種描述硬體的數據結構,它起源於OpenFirmware(OF),用於實現驅動代碼與設備信息相分離;在設備樹出現以前,所有關於板子上硬體設備的具體都要硬編碼在arch/arm/plat-xxx和arch/arm/mach-xxx目錄下的文件當中,或者直接硬編碼在驅動代碼當中,例如我們前面編寫的LED驅動就是直接將led的信息(用的哪個管腳、GPIO寄存器的基地址等)直接編碼在了驅動源碼當中,一旦外圍設備變化(例如PS_LED0換成另一個MIO引腳了),驅動代碼就要重寫。
引入了設備樹之後,驅動代碼只負責處理驅動的邏輯,而關於設備的具體信息存放到設備樹文件中,這樣,如果只是硬體接口信息的變化而沒有驅動邏輯的變化,驅動開發者只需要修改設備樹文件信息,不需要改寫驅動代碼。使用設備樹之後,許多硬體設備信息可以直接通過它傳遞給Linux,而不需要在內核中堆積大量的冗餘代碼。
設備樹,將這個詞分開就是「設備」和「樹」,描述設備樹的文件叫做DTS(Device Tree Source),這個DTS文件採用樹形結構描述板級設備,也就是開發板上的硬體設備信息,比如CPU數量、內存基地址、IIC接口上接了哪些設備、SPI接口上接了哪些設備等等,如圖 35.1.1所示:
圖 35.1.1 設備樹結構示意圖
在圖 35.1.1中,樹的主幹就是系統總線,IIC控制器、GPIO控制器、SPI控制器等都是接到系統主線上的分支。IIC控制器有分為IIC1和IIC2兩種,其中IIC1上接了FT5206和AT24C02這兩個IIC設備,IIC2上只接了MPU6050這個設備。DTS文件的主要功能就是按照圖 35.1.1所示的結構來描述板子上的設備信息,DTS文件描述設備信息是有相應的語法規則要求的,稍後我們會詳細的講解DTS語法規則。
設備樹文件的擴展名為.dts,一個.dts(device tree source)就文件對應一個開發板,一般放置在內核的&34;目錄下,比如exynos4412開發板的板級設備樹文件就是&34;,再比如I.MX6ULL-EVK開發板的板級設備樹文件就是arch/arm/boot/dts/imx6ull-14x14-evk.dts。那本篇驅動開發我們所使用的板級設備樹文件就是arch/arm/boot/dts/system-top.dts,這個文件是在第三十一章時候使用hsi命令自動生成的,前面已經跟大家講過了,除了system-top.dts文件之外,還生成了另外三個文件pl.dtsi、pcw.dtsi以及zynq-7000.dtsi(system-top.dts包含它們三個,後面會說到),並且一併把它們放入了linux內核源碼arch/arm/boot/dts目錄下了。
前面也跟大家講過,除了內核支持設備樹之外,新版的u-boot也是支持設備樹的,如果有機會也可以跟大家講一講U-Boot的設備樹。
24.2設備樹的基本知識
24.2.1dts
設備樹的源文件的後綴名就是.dts,每一款硬體平臺可以單獨寫一份xxxx.dts,所以在Linux內核源碼中存在大量.dts文件,對於arm架構可以在arch/arm/boot/dts找到相應的dts。
24.2.2dtsi
值得一提的是,對於一些相同的dts配置可以抽象到dtsi文件中,這個dtsi文件其實就類似於C語言當中的.h頭文件,可以通過C語言中使用include來包含一個.dtsi文件,例如arch/arm/boot/dts/system-top.dts文件有如下內容:
示例代碼24.2.2.1 system-top.dts內容片段
圖 35.3.1 頭文件包含
圖 35.3.2 使用宏定義
關於頭文件包含以及宏定義的使用這裡就不多說了,本身也非常簡單。
24.3.4標準屬性
節點的內容是由一堆的屬性組成,不同的設備需要的屬性不同,用戶可以自定義屬性。除了用戶自定義屬性,有很多屬性是標準屬性,Linux下的很多外設驅動都會使用這些標準屬性,本節我們就來學習一下幾個常用的標準屬性。
1、compatible屬性
compatible屬性也叫做「兼容性」屬性,這是非常重要的一個屬性!compatible屬性的值可以是一個字符串,也可以是一個字符串列表;一般該字符串使用」<製造商>,<型號>」這樣的形式進行命名,當然這不是必須要這樣,這是要求大家按照這樣的形式進行命名,目的是為了指定一個確切的設備,並且包括製造商的名字,以避免命名空間衝突,如下所示:
複製代碼
例子當中的xlnx和cdns就表示製造商,而後面的xuartps和uart-r1p8就表示具體設備的型號。compatible屬性用於將設備和驅動綁定起來,例如該設備首先使用第一個兼容值(xlnx,xuartps)在Linux內核裡面查找,看看能不能找到與之匹配的驅動文件,如果沒有找到的話就使用第二個兼容值(cdns,uart-r1p8)查找,直到找到或者查找完整個Linux內核也沒有找到對應的驅動。
一般驅動程序文件都會有一個OF匹配表,此OF匹配表保存著一些compatible值,如果設備樹中的節點的compatible屬性值和OF匹配表中的任何一個值相等,那麼就表示設備可以使用這個驅動。比如在驅動文件drivers/tty/serial/xilinx_uartps.c中有如下內容:
示例代碼24.3.4.1 drivers/tty/serial/xilinx_uartps.c內容片段
複製代碼
這個驅動文件是ZYNQ PS端的UART設備對應的驅動文件。
第1344~1350行定義的數組cdns_uart_of_match就是xilinx_uartps.c這個驅動文件的匹配表,此匹配表有4個匹配值「xlnx,xuartps」、「cdns,uart-r1p8」、「cdns,uart-r1p12」以及「xlnx,zynqmp-uart」。如果在設備樹中有哪個節點的compatible屬性值與這4個字符串中的某個相同,那麼這個節點就會與此驅動文件匹配成功。
第1704行,UART採用了platform_driver驅動模式,關於platform_driver驅動後面會講解。此行設置.of_match_table為cdns_uart_of_match,也就是設置這個platform_driver所使用的OF匹配表。
2、model屬性
model屬性值也是一個字符串描述信息,它指定製造商的設備型號,model屬性一般定義在根節點下,一般就是對板子的描述信息,沒啥實質性的作用,內核在解析設備樹的時候會把這個屬性對應的字符串信息列印出來。
示例代碼24.3.4.2 arch/arm/boot/dts/system-top.dts內容片段
複製代碼
可以看出,DT_MACHINE_START和MACHINE_START基本相同,只是.nr的設置不同,在DT_MACHINE_START裡面直接將.nr設置為~0。說明引入設備樹以後不會再根據machine id來檢查Linux內核是否支持某個硬體平臺了。
打開文件arch/arm/mach-zynq/common.c,有如下所示內容:
示例代碼24.3.5.9 arch/arm/mach-zynq/common.c
複製代碼