日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

Linux设备树

發(fā)布時(shí)間:2023/12/10 linux 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux设备树 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

  • 什么是設(shè)備樹(shù)
  • DTS、DTB 和DTC
  • DTS語(yǔ)法
    • .dtsi 頭文件
    • 設(shè)備節(jié)點(diǎn)(節(jié)點(diǎn)名字、寄存器外設(shè)首地址或設(shè)備地址、子節(jié)點(diǎn))
    • 設(shè)備標(biāo)準(zhǔn)屬性(compatible綁定、status狀態(tài)、reg設(shè)備寄存器地址...)
    • 根節(jié)點(diǎn)compatible屬性(設(shè)備怎么匹配到對(duì)應(yīng)的內(nèi)核驅(qū)動(dòng)?了解)
    • 向設(shè)備節(jié)點(diǎn)添加或修改內(nèi)容(不要直接添加在.dtsi頭文件,通過(guò)&在各自的.dts里添加)
  • 創(chuàng)建小型模板設(shè)備樹(shù)(了解)
  • 設(shè)備樹(shù)在系統(tǒng)中的體現(xiàn)(/proc/device-tree目錄下列出所有根節(jié)點(diǎn)“/”及其屬性,了解)
  • 特殊節(jié)點(diǎn)(aliases和chosen,了解)
    • aliases 子節(jié)點(diǎn)
    • chosen 子節(jié)點(diǎn)
  • Linux 內(nèi)核解析DTB文件(了解)
  • 綁定信息文檔(Linux內(nèi)核源碼中.txt 文檔描述如何添加設(shè)備節(jié)點(diǎn)規(guī)則,了解)
  • 設(shè)備樹(shù)常用OF操作函數(shù)(獲取reg'寄存器地址'屬性,操作外設(shè))
    • 查找節(jié)點(diǎn)的OF函數(shù)
    • 查找父/子節(jié)點(diǎn)的OF函數(shù)
    • 提取屬性值的OF函數(shù)
    • 其他常用的OF函數(shù)
    • OF函數(shù)使用案例
  • 總結(jié)

在3.x 版本(具體哪個(gè)版本筆者也無(wú)從考證)以前的Linux 內(nèi)核中ARM 架構(gòu)并沒(méi)有采用設(shè)備樹(shù)。前面章節(jié)中我們多次提到“設(shè)備樹(shù)”這個(gè)概念,在新版本的Linux 中,ARM相關(guān)的驅(qū)動(dòng)全部采用了設(shè)備樹(shù)(也有支持老式驅(qū)動(dòng)的,比較少),最新出的CPU 其驅(qū)動(dòng)開(kāi)發(fā)也基本都是基于設(shè)備樹(shù)的。本章我們就來(lái)了解一下設(shè)備樹(shù)的起源、重點(diǎn)學(xué)習(xí)一下設(shè)備樹(shù)語(yǔ)法。

什么是設(shè)備樹(shù)

設(shè)備樹(shù)(Device Tree),將這個(gè)詞分開(kāi)就是“設(shè)備”和“樹(shù)”,描述設(shè)備樹(shù)的文件叫做DTS(Device Tree Source),這個(gè)DTS 文件采用樹(shù)形結(jié)構(gòu)描述板級(jí)設(shè)備,也就是開(kāi)發(fā)板上的設(shè)備信息,比如CPU 數(shù)量、內(nèi)存基地址、IIC 接口上接了哪些設(shè)備、SPI 接口上接了哪些設(shè)備等等,如圖43.1.1所示:

在圖43.1.1 中,樹(shù)的主干就是系統(tǒng)總線(xiàn),IIC 控制器、GPIO 控制器、SPI 控制器等都是接到系統(tǒng)主線(xiàn)上的分支。IIC 控制器有分為IIC1 和IIC2 兩種,其中IIC1 上接了FT5206 和AT24C02這兩個(gè)IIC 設(shè)備,IIC2 上只接了MPU6050 這個(gè)設(shè)備。DTS 文件描述設(shè)備信息是有相應(yīng)的語(yǔ)法規(guī)則要求的,稍后會(huì)詳細(xì)。

在沒(méi)有設(shè)備樹(shù)的時(shí)候Linux 是如何描述ARM 架構(gòu)中的板級(jí)信息呢?在Linux 內(nèi)核源碼中大量的arch/arm/mach-xxx 和arch/arm/plat-xxx 文件夾,這些文件夾里面的文件就是對(duì)應(yīng)平臺(tái)下的板級(jí)信息。比如在三星arch/arm/mach-smdk2440.c 中有如下內(nèi)容(結(jié)構(gòu)體方式):

90 static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = { 91 92 .lcdcon5 = S3C2410_LCDCON5_FRM565 | 93 S3C2410_LCDCON5_INVVLINE | 94 S3C2410_LCDCON5_INVVFRAME | 95 S3C2410_LCDCON5_PWREN | 96 S3C2410_LCDCON5_HWSWP, ...... 113 }; 114 115 static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = { 116 .displays = &smdk2440_lcd_cfg, 117 .num_displays = 1, 118 .default_display = 0, ...... 133 }; 134 135 static struct platform_device *smdk2440_devices[] __initdata = { 136 &s3c_device_ohci, 137 &s3c_device_lcd, 138 &s3c_device_wdt, 139 &s3c_device_i2c0, 140 &s3c_device_iis, 141 };

上述代碼中的結(jié)構(gòu)體變量smdk2440_fb_info 就是描述SMDK2440 這個(gè)開(kāi)發(fā)板上的LCD 信息的,結(jié)構(gòu)體指針數(shù)組smdk2440_devices 描述的SMDK2440 這個(gè)開(kāi)發(fā)板上的所有平臺(tái)相關(guān)信息。這個(gè)僅僅是使用2440 這個(gè)芯片的SMDK2440 開(kāi)發(fā)板下的LCD 信息,SMDK2440 開(kāi)發(fā)板還有很多的其他外設(shè)硬件和平臺(tái)硬件信息。隨著智能手機(jī)的發(fā)展,每年新出的ARM 架構(gòu)芯片至少都在數(shù)十、數(shù)百款,這些板級(jí)信息文件都是.c 或.h 文件,都會(huì)被硬編碼進(jìn)Linux 內(nèi)核中,導(dǎo)致Linux 內(nèi)核“虛胖”,Linux應(yīng)主要聚焦內(nèi)核調(diào)度算法。

從此以后ARM 社區(qū)就引入了PowerPC 等架構(gòu)已經(jīng)采用的設(shè)備樹(shù)(Flattened Device Tree),將這些描述板級(jí)硬件信息的內(nèi)容都從Linux 內(nèi)中分離開(kāi)來(lái),放到一個(gè)單獨(dú)的文件夾內(nèi),這就叫設(shè)備樹(shù),文件擴(kuò)展名為.dts。

一個(gè)SOC 可以作出很多不同的板子,這些不同的板子肯定是有共同的信息,將這些共同的信息提取出來(lái)作為一個(gè)通用的.dtsi 文件,其他的 .dts 文件直接引用這個(gè)通用文件,類(lèi)似于C 語(yǔ)言中的頭文件。

  • 一般.dts 描述板級(jí)信息(也就是開(kāi)發(fā)板上有哪些IIC 設(shè)備、SPI 設(shè)備等)
  • .dtsi 描述SOC級(jí)信息(也就是SOC 有幾個(gè)CPU、主頻是多少、各個(gè)外設(shè)控制器信息等)。

DTS、DTB 和DTC

DTS 和DTB 這兩個(gè)文件是什么關(guān)系呢?

  • DTS 是設(shè)備樹(shù)源碼文件
  • DTB 是將DTS 編譯以后得到的可執(zhí)行二進(jìn)制文件
  • DTC 是將.dts 編譯為.dtb的編譯工具

DTC 工具源碼在Linux 內(nèi)核scripts/dtc/Makefile 文件內(nèi)容如下:

1 hostprogs-y := dtc 2 always := $(hostprogs-y) 3 4 dtc-objs:= dtc.o flattree.o fstree.o data.o livetree.o treesource.o \ 5 srcpos.o checks.o util.o 6 dtc-objs += dtc-lexer.lex.o dtc-parser.tab.o ......

可以看出,DTC 工具依賴(lài)于dtc.c、flattree.c、fstree.c 等文件,最終編譯并鏈接出DTC 這個(gè)主機(jī)文件。如果要編譯DTS 文件的話(huà)只需要進(jìn)入到Linux 源碼根目錄下,然后執(zhí)行如下命令:

make all

或者:

make dtbs

“make all”命令是編譯Linux 源碼中的所有東西,包括zImage,.ko 驅(qū)動(dòng)模塊以及設(shè)備樹(shù),如果只是編譯設(shè)備樹(shù)的話(huà)使用“make dtbs”命令。

基于ARM 架構(gòu)的SOC 有很多種,一種SOC 又可以制作出很多款板子,每個(gè)板子都有一個(gè)對(duì)應(yīng)的DTS 文件,那么如何確定編譯哪一個(gè)DTS 文件呢?我們就以I.MX6ULL 這款芯片對(duì)應(yīng)的板子為例來(lái)看一下,打開(kāi)arch/arm/boot/dts/Makefile,有如下內(nèi)容:

381 dtb-$(CONFIG_SOC_IMX6UL) += \ 382 imx6ul-14x14-ddr3-arm2.dtb \ 383 imx6ul-14x14-ddr3-arm2-emmc.dtb \ ...... 400 dtb-$(CONFIG_SOC_IMX6ULL) += \ 401 imx6ull-14x14-ddr3-arm2.dtb \ 402 imx6ull-14x14-ddr3-arm2-adc.dtb \ 403 imx6ull-14x14-ddr3-arm2-cs42888.dtb \ 404 imx6ull-14x14-ddr3-arm2-ecspi.dtb \ 405 imx6ull-14x14-ddr3-arm2-emmc.dtb \ 406 imx6ull-14x14-ddr3-arm2-epdc.dtb \ 407 imx6ull-14x14-ddr3-arm2-flexcan2.dtb \ 408 imx6ull-14x14-ddr3-arm2-gpmi-weim.dtb \ 409 imx6ull-14x14-ddr3-arm2-lcdif.dtb \ 410 imx6ull-14x14-ddr3-arm2-ldo.dtb \ 411 imx6ull-14x14-ddr3-arm2-qspi.dtb \ 412 imx6ull-14x14-ddr3-arm2-qspi-all.dtb \ 413 imx6ull-14x14-ddr3-arm2-tsc.dtb \ 414 imx6ull-14x14-ddr3-arm2-uart2.dtb \ 415 imx6ull-14x14-ddr3-arm2-usb.dtb \ 416 imx6ull-14x14-ddr3-arm2-wm8958.dtb \ 417 imx6ull-14x14-evk.dtb \ 418 imx6ull-14x14-evk-btwifi.dtb \ 419 imx6ull-14x14-evk-emmc.dtb \ 420 imx6ull-14x14-evk-gpmi-weim.dtb \ 421 imx6ull-14x14-evk-usb-certi.dtb \ 422 imx6ull-alientek-emmc.dtb \ 423 imx6ull-alientek-nand.dtb \ 424 imx6ull-9x9-evk.dtb \ 425 imx6ull-9x9-evk-btwifi.dtb \ 426 imx6ull-9x9-evk-ldo.dtb 427 dtb-$(CONFIG_SOC_IMX6SLL) += \ 428 imx6sll-lpddr2-arm2.dtb \ 429 imx6sll-lpddr3-arm2.dtb \ ......

可以看出,當(dāng)選中I.MX6ULL 這個(gè)SOC 以后(CONFIG_SOC_IMX6ULL=y),所有使用到I.MX6ULL 這個(gè)SOC 的板子對(duì)應(yīng)的.dts 文件都會(huì)被編譯為.dtb。如果我們使用I.MX6ULL 新做了一個(gè)板子,只需要新建一個(gè)此板子對(duì)應(yīng)的.dts 文件,然后將對(duì)應(yīng)的.dtb 文件名添加到dtb-$(CONFIG_SOC_IMX6ULL)下,這樣在編譯設(shè)備樹(shù)的時(shí)候就會(huì)將對(duì)應(yīng)的.dts 編譯為二進(jìn)制的.dtb文件。

第422 和423 行就是我們?cè)诮o正點(diǎn)原子的I.MX6U-ALPHA 開(kāi)發(fā)板移植Linux 系統(tǒng)的時(shí)候添加的設(shè)備樹(shù)。

.dtb 文件怎么使用?前面講解Uboot 移植、Linux 內(nèi)核移植的時(shí)候已經(jīng)提到了,uboot 中使用bootz 或bootm命令向Linux 內(nèi)核傳遞二進(jìn)制設(shè)備樹(shù)文件(.dtb)。

DTS語(yǔ)法

雖然我們基本上不會(huì)從頭到尾重寫(xiě)一個(gè).dts 文件,大多時(shí)候是直接在SOC 廠(chǎng)商提供的.dts文件上進(jìn)行修改。但是DTS 文件語(yǔ)法我們還是需要詳細(xì)的學(xué)習(xí)一遍,因?yàn)槲覀兛隙ㄐ枰薷?dts文件。DTS 語(yǔ)法非常的人性化,是一種ASCII文本文件,不管是閱讀還是修改都很方便。

本節(jié)我們就以imx6ull-alientek-emmc.dts 這個(gè)文件為例來(lái)講解一下DTS 語(yǔ)法。關(guān)于設(shè)備樹(shù)詳細(xì)的語(yǔ)法規(guī)則請(qǐng)參考《Devicetree SpecificationV0.2.pdf 》和《Power_ePAPR_APPROVED_v1.12.pdf》這兩份文檔,此兩份文檔已經(jīng)放到了開(kāi)發(fā)板光盤(pán)中,路徑為:4 、參考資料->Devicetree SpecificationV0.2.pdf 、4 、參考資料-> Power_ePAPR_APPROVED_v1.12.pdf

.dtsi 頭文件

和C 語(yǔ)言一樣,設(shè)備樹(shù)也支持頭文件,設(shè)備樹(shù)的頭文件擴(kuò)展名為.dtsi。在imx6ull-alientek-emmc.dts 中有如下所示內(nèi)容:

12 #include <dt-bindings/input/input.h> 13 #include "imx6ull.dtsi"

第12 行,使用“#include”來(lái)引用“input.h”這個(gè).h 頭文件。
第13 行,使用“#include”來(lái)引用“imx6ull.dtsi”這個(gè).dtsi 頭文件。

看到這里,大家可能會(huì)疑惑,不是說(shuō)設(shè)備樹(shù)的擴(kuò)展名是.dtsi 嗎?為什么也可以直接引用C語(yǔ)言中的.h 頭文件呢?這里并沒(méi)有錯(cuò),.dts文件引用C 語(yǔ)言中的.h 文件,甚至也可以引用.dts 文件,打開(kāi)imx6ull-14x14-evk-gpmi-eim.dts
這個(gè)文件,此文件中有如下內(nèi)容:

9 #include "imx6ull-14x14-evk.dts"

可以看出,示例代碼43.3.1.2 中直接引用了.dts 文件,因此在.dts> 設(shè)備樹(shù)文件中,可以通過(guò)“#include”來(lái)引用.h、.dtsi 和.dts 文件。只是,我們?cè)诰帉?xiě)設(shè)備樹(shù)頭文件的時(shí)候最好選擇.dtsi> 后綴。

一般.dtsi 文件用于描述SOC 的內(nèi)部外設(shè)信息,比如CPU 架構(gòu)、主頻等;外設(shè)寄存器地址范圍(比如UART、IIC 等)。imx6ull.dtsi 就是描述I.MX6ULL 這顆SOC 內(nèi)部外設(shè)情況信息的,內(nèi)容如下:

10 #include <dt-bindings/clock/imx6ul-clock.h> 11 #include <dt-bindings/gpio/gpio.h> 12 #include <dt-bindings/interrupt-controller/arm-gic.h> 13 #include "imx6ull-pinfunc.h" 14 #include "imx6ull-pinfunc-snvs.h" 15 #include "skeleton.dtsi" 16 17 / { 18 aliases { 19 can0 = &flexcan1; ...... 48 }; 49 50 cpus { 51 #address-cells = <1>; 52 #size-cells = <0>; 53 54 cpu0: cpu@0 { 55 compatible = "arm,cortex-a7"; 56 device_type = "cpu"; ...... 89 }; 90 }; 91 92 intc: interrupt-controller@00a01000 { 93 compatible = "arm,cortex-a7-gic"; 94 #interrupt-cells = <3>; 95 interrupt-controller; 96 reg = <0x00a01000 0x1000>, 97 <0x00a02000 0x100>; 98 }; 99 100 clocks { 101 #address-cells = <1>; 102 #size-cells = <0>; 103 104 ckil: clock@0 { 105 compatible = "fixed-clock"; 106 reg = <0>; 107 #clock-cells = <0>; 108 clock-frequency = <32768>; 109 clock-output-names = "ckil"; 110 }; ...... 135 }; 136 137 soc { 138 #address-cells = <1>; 139 #size-cells = <1>; 140 compatible = "simple-bus"; 141 interrupt-parent = <&gpc>; 142 ranges; 143 144 busfreq { 145 compatible = "fsl,imx_busfreq"; ...... 162 }; 197 198 gpmi: gpmi-nand@01806000{ 199 compatible = "fsl,imx6ull-gpmi-nand", "fsl, imx6ul-gpmi-nand"; 200 #address-cells = <1>; 201 #size-cells = <1>; 202 reg = <0x01806000 0x2000>, <0x01808000 0x4000>; ...... 216 }; ...... 1177 }; 1178 };

示例代碼43.3.1.3 中第54~89 行就是cpu0這個(gè)設(shè)備節(jié)點(diǎn)信息,這個(gè)節(jié)點(diǎn)信息描述了I.MX6ULL 這顆SOC 所使用的CPU 信息,比如架構(gòu)是cortex-A7,頻率支持996MHz、792MHz、528MHz、396MHz 和198MHz 等等。

在imx6ull.dtsi 文件中不僅僅描述了cpu0 這一個(gè)節(jié)點(diǎn)信息,
I.MX6ULL 這顆SOC 所有的外設(shè)都描述的清清楚楚,比如ecspi1~ 4、uart1~ 8、usbphy1~ 2、i2c1~4等等,關(guān)于這些設(shè)備節(jié)點(diǎn)信息的具體內(nèi)容我們稍后在詳細(xì)的講解。

設(shè)備節(jié)點(diǎn)(節(jié)點(diǎn)名字、寄存器外設(shè)首地址或設(shè)備地址、子節(jié)點(diǎn))

設(shè)備樹(shù)是采用樹(shù)形結(jié)構(gòu)來(lái)描述板子上的設(shè)備信息的文件,每個(gè)設(shè)備都是一個(gè)節(jié)點(diǎn),叫做設(shè)備節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)都通過(guò)一些屬性信息來(lái)描述節(jié)點(diǎn)信息。以下是從imx6ull.dtsi 文件中縮減出來(lái)的設(shè)備樹(shù)文件內(nèi)容:

1 / { 2 aliases { 3 can0 = &flexcan1; 4 }; 5 6 cpus { 7 #address-cells = <1>; 8 #size-cells = <0>; 9 10 cpu0: cpu@0 { 11 compatible = "arm,cortex-a7"; 12 device_type = "cpu"; 13 reg = <0>; 14 }; 15 }; 16 17 intc: interrupt-controller@00a01000 { 18 compatible = "arm,cortex-a7-gic"; 19 #interrupt-cells = <3>; 20 interrupt-controller; 21 reg = <0x00a01000 0x1000>, 22 <0x00a02000 0x100>; 23 }; 24 }

第1 行,“/”是根節(jié)點(diǎn),每個(gè)設(shè)備樹(shù)文件只有一個(gè)根節(jié)點(diǎn)。細(xì)心的同學(xué)應(yīng)該會(huì)發(fā)現(xiàn),imx6ull.dtsi和imx6ull-alientek-emmc.dts 這兩個(gè)文件都有一個(gè)“/”根節(jié)點(diǎn),這樣不會(huì)出錯(cuò)嗎?不會(huì)的,因?yàn)檫@兩個(gè)“/”根節(jié)點(diǎn)的內(nèi)容會(huì)合并成一個(gè)根節(jié)點(diǎn)。

第2、6 和17 行,aliases、cpus 和intc 是三個(gè)子節(jié)點(diǎn),在設(shè)備樹(shù)中節(jié)點(diǎn)命名格式如下:

node-name@unit-address
  • “node-name”是節(jié)點(diǎn)名字,為ASCII 字符串,節(jié)點(diǎn)名字應(yīng)該能夠清晰的描述出節(jié)點(diǎn)的功能,比如“uart1”就表示這個(gè)節(jié)點(diǎn)是UART1 外設(shè)
  • “unit-address”一般表示設(shè)備的地址或寄存器首地址(數(shù)據(jù)手冊(cè)上查),如果某個(gè)節(jié)點(diǎn)沒(méi)有地址或者寄存器的話(huà)“unit-address”可以不要,比如“cpu@0”、“interrupt-controller@00a01000”。

引入標(biāo)簽:
我們?cè)谑纠a43.3.2.1 中我們看到的節(jié)點(diǎn)命名卻如下所示:

c cpu0:cpu@0

上述命令并不是“node-name@unit-address”這樣的格式,而是用“:”隔開(kāi)成了兩部分,“:”前面的是節(jié)點(diǎn)標(biāo)簽(label),“:”后面的才是節(jié)點(diǎn)名字,格式如下所示:

c label: node-name@unit-address

引入label 的目的就是為了方便訪(fǎng)問(wèn)節(jié)點(diǎn),可以直接通過(guò)&label 來(lái)訪(fǎng)問(wèn)這個(gè)節(jié)點(diǎn),比如通過(guò)&cpu0就可以訪(fǎng)問(wèn)“cpu@0”這個(gè)節(jié)點(diǎn),而不需要輸入完整的節(jié)點(diǎn)名字。再比如節(jié)點(diǎn)“intc:
interrupt-controller@00a01000”,節(jié)點(diǎn)label是intc,而節(jié)點(diǎn)名字就很長(zhǎng)了,為“interrupt-controller@00a01000”。很明顯通過(guò)&intc來(lái)訪(fǎng)問(wèn)“interrupt-controller@00a01000”這個(gè)節(jié)點(diǎn)要方便很多!

第10 行,cpu0 也是一個(gè)節(jié)點(diǎn),只是cpu0 是cpus 的子節(jié)點(diǎn)
每個(gè)節(jié)點(diǎn)都有不同屬性,不同的屬性又有不同的內(nèi)容,屬性都是鍵值對(duì),值可以為空或任意的字節(jié)流。設(shè)備樹(shù)源碼中常用的幾種數(shù)據(jù)形式如下所示:
①、字符串

compatible = "arm,cortex-a7";

上述代碼設(shè)置compatible 屬性的值為字符串“arm,cortex-a7”。
②、32位無(wú)符號(hào)整數(shù)

reg = <0>;

上述代碼設(shè)置reg 屬性的值為0,reg 的值也可以設(shè)置為一組值,比如:

reg = <0 0x123456 100>;

③、字符串列表
屬性值也可以為字符串列表,字符串和字符串之間采用“,”隔開(kāi),如下所示:

compatible = "fsl,imx6ull-gpmi-nand", "fsl, imx6ul-gpmi-nand";

上述代碼設(shè)置屬性compatible 的值為“fsl,imx6ull-gpmi-nand”和“fsl, imx6ul-gpmi-nand”。

設(shè)備標(biāo)準(zhǔn)屬性(compatible綁定、status狀態(tài)、reg設(shè)備寄存器地址…)

節(jié)點(diǎn)是由一堆的屬性組成,節(jié)點(diǎn)都是具體的設(shè)備,不同的設(shè)備需要的屬性不同,用戶(hù)可以自定義屬性,也可以使用標(biāo)準(zhǔn)屬性,Linux 下的很多外設(shè)驅(qū)動(dòng)都會(huì)使用這些標(biāo)準(zhǔn)屬性,本節(jié)我們就來(lái)學(xué)習(xí)一下幾個(gè)常用的標(biāo)準(zhǔn)屬性。

1、compatible 屬性

compatible 屬性也叫做“兼容性”屬性,這是非常重要的一個(gè)屬性!compatible 屬性的值是一個(gè)字符串列表,用于將設(shè)備和驅(qū)動(dòng)綁定起來(lái)。字符串列表用于選擇設(shè)備所要使用的驅(qū)動(dòng)程序,compatible 屬性的值格式如下所示:

"manufacturer,model"
  • manufacturer 表示廠(chǎng)商
  • model 一般是模塊對(duì)應(yīng)的驅(qū)動(dòng)名字。

比如imx6ull-alientek-emmc.dts 中sound 節(jié)點(diǎn)是I.MX6U-ALPHA 開(kāi)發(fā)板的音頻設(shè)備節(jié)點(diǎn),I.MX6U-ALPHA 開(kāi)發(fā)板上的音頻芯片采用的歐勝(WOLFSON)出品的WM8960,sound 節(jié)點(diǎn)的compatible 屬性值如下:

compatible = "fsl,imx6ul-evk-wm8960","fsl,imx-audio-wm8960";

屬性值有兩個(gè),分別為“fsl,imx6ul-evk-wm8960”和“fsl,imx-audio-wm8960”,其中“fsl”表示廠(chǎng)商是飛思卡爾,“imx6ul-evk-wm8960”和“imx-audio-wm8960”表示驅(qū)動(dòng)模塊名字。sound這個(gè)設(shè)備首先使用第一個(gè)兼容值在Linux 內(nèi)核里面查找,看看能不能找到與之匹配的驅(qū)動(dòng)文件,如果沒(méi)有找到的話(huà)就使用第二個(gè)兼容值查。

一般驅(qū)動(dòng)程序文件都會(huì)有一個(gè)OF 匹配表,此OF 匹配表保存著一些compatible 值,如果設(shè)備節(jié)點(diǎn)的compatible 屬性值和OF 匹配表中的任何一個(gè)值相等,那么就表示設(shè)備可以使用這個(gè)驅(qū)動(dòng)。比如在文件imx-wm8960.c 中有如下內(nèi)容:

632 static const struct of_device_id imx_wm8960_dt_ids[] = { 633 { .compatible = "fsl,imx-audio-wm8960", }, 634 { /* sentinel */ } 635 }; 636 MODULE_DEVICE_TABLE(of, imx_wm8960_dt_ids); 637 638 static struct platform_driver imx_wm8960_driver = { 639 .driver = { 640 .name = "imx-wm8960", 641 .pm = &snd_soc_pm_ops, 642 .of_match_table = imx_wm8960_dt_ids, 643 }, 644 .probe = imx_wm8960_probe, 645 .remove = imx_wm8960_remove, 646 };

第632~635 行的數(shù)組imx_wm8960_dt_ids 就是imx-wm8960.c 這個(gè)驅(qū)動(dòng)文件的匹配表,此匹配表只有一個(gè)匹配值“fsl,imx-audio-wm8960”。如果在設(shè)備樹(shù)中有哪個(gè)節(jié)點(diǎn)的compatible 屬 性值與此相等,那么這個(gè)節(jié)點(diǎn)就會(huì)使用此驅(qū)動(dòng)文件。

第642 行,wm8960 采用了platform_driver 驅(qū)動(dòng)模式,關(guān)于platform_driver 驅(qū)動(dòng)后面會(huì)講解。此行設(shè)置.of_match_table 為imx_wm8960_dt_ids,也就是設(shè)置這個(gè)platform_driver 所使用的OF 匹配表。

2、model 屬性

model 屬性值也是一個(gè)字符串,一般model 屬性描述設(shè)備模塊信息,比如名字什么的,比如:

model = "wm8960-audio";

3、status 屬性

status 屬性看名字就知道是和設(shè)備狀態(tài)有關(guān)的,status 屬性值也是字符串,字符串是設(shè)備的狀態(tài)信息,可選的狀態(tài)如表43.3.3.1 所示:

值描述
“okay”表明設(shè)備是可操作的。
“disabled”表明設(shè)備當(dāng)前是不可操作的,但是在未來(lái)可以變?yōu)榭刹僮鞯?#xff0c;比如熱插拔設(shè)備插入以后。至于disabled 的具體含義還要看設(shè)備的綁定文檔。
“fail”表明設(shè)備不可操作,設(shè)備檢測(cè)到了一系列的錯(cuò)誤,而且設(shè)備也不大可能變得可操作。
“fail-sss”含義和“fail”相同,后面的sss 部分是檢測(cè)到的錯(cuò)誤內(nèi)容。

4、#address-cells 和#size-cells 屬性(決定如何編寫(xiě)reg屬性值)

這兩個(gè)屬性的值都是無(wú)符號(hào)32 位整形,用于描述子節(jié)點(diǎn)的地址信息

  • #address-cells 屬性值決定了子節(jié)點(diǎn)reg屬性中地址信息所占用的字長(zhǎng)(32 位)
  • #size-cells 屬性值決定了子節(jié)點(diǎn)reg 屬性中長(zhǎng)度信息所占的字長(zhǎng)(32 位)。

#address-cells 和#size-cells 表明了子節(jié)點(diǎn)應(yīng)該如何編寫(xiě)reg屬性值,一般reg 屬性都是和地址有關(guān)的內(nèi)容,和地址相關(guān)的信息有兩種:起始地址和地址長(zhǎng)度,reg 屬性的格式一為:

reg = <address1 length1 address2 length2 address3 length3……>

每個(gè)“address length”組合表示一個(gè)地址范圍

  • address 是起始地址,length 是地址長(zhǎng)度
  • #address-cells 表明address 這個(gè)數(shù)據(jù)所占用的字長(zhǎng),#size-cells 表明length 這個(gè)數(shù)據(jù)所占用的字長(zhǎng),比如下面的兩種情況:
1 spi4 { 2 compatible = "spi-gpio"; 3 #address-cells = <1>; 4 #size-cells = <0>; 5 6 gpio_spi: gpio_spi@0 { 7 compatible = "fairchild,74hc595"; 8 reg = <0>; 9 }; 10 }; 11 12 aips3: aips-bus@02200000 { 13 compatible = "fsl,aips-bus", "simple-bus"; 14 #address-cells = <1>; 15 #size-cells = <1>; 16 17 dcp: dcp@02280000 { 18 compatible = "fsl,imx6sl-dcp"; 19 reg = <0x02280000 0x4000>; 20 }; 21 };

第3,4 行,節(jié)點(diǎn)spi4 的#address-cells = <1>,#size-cells = <0>,說(shuō)明spi4 的子節(jié)點(diǎn)reg 屬性中起始地址所占用的字長(zhǎng)為1,地址長(zhǎng)度所占用的字長(zhǎng)為0。

第8 行,子節(jié)點(diǎn)gpio_spi: gpio_spi@0 的reg 屬性值為<0>,因?yàn)楦腹?jié)點(diǎn)設(shè)置了#address-cells = <1>,#size-cells = <0>,因此addres=0,沒(méi)有l(wèi)ength 的值,相當(dāng)于設(shè)置了起始地址,而沒(méi)有設(shè)置地址長(zhǎng)度。


第14,15 行,設(shè)置aips3: aips-bus@02200000 節(jié)點(diǎn)#address-cells = <1>,#size-cells = <1>,說(shuō)明aips3: aips-bus@02200000 節(jié)點(diǎn)起始地址長(zhǎng)度所占用的字長(zhǎng)為1,地址長(zhǎng)度所占用的字長(zhǎng)也為1。

第19 行,子節(jié)點(diǎn)dcp: dcp@02280000 的reg 屬性值為<0x02280000 0x4000>,因?yàn)楦腹?jié)點(diǎn)設(shè)置了#address-cells = <1>,#size-cells = <1>,address= 0x02280000,length= 0x4000,相當(dāng)于設(shè)置了起始地址為0x02280000,地址長(zhǎng)度為0x40000。

5、reg 屬性

reg 屬性前面已經(jīng)提到過(guò)了,reg 屬性的值一般是(address,length)對(duì)。reg 屬性一般用于描述設(shè)備地址空間資源信息,一般都是某個(gè)外設(shè)的寄存器地址范圍信息,比如在imx6ull.dtsi 中有如下內(nèi)容:

323 uart1: serial@02020000 { 324 compatible = "fsl,imx6ul-uart", 325 "fsl,imx6q-uart", "fsl,imx21-uart"; 326 reg = <0x02020000 0x4000>; 327 interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>; 328 clocks = <&clks IMX6UL_CLK_UART1_IPG>, 329 <&clks IMX6UL_CLK_UART1_SERIAL>; 330 clock-names = "ipg", "per"; 331 status = "disabled"; 332 };

上述代碼是節(jié)點(diǎn)uart1,uart1 節(jié)點(diǎn)描述了I.MX6ULL 的UART1 相關(guān)信息,重點(diǎn)是第326 行的reg 屬性。其中uart1 的父節(jié)點(diǎn)aips1: aips-bus@02000000 設(shè)置了#address-cells = <1>、#size-cells = <1>,因此reg 屬性中address=0x02020000,length=0x4000。

查閱《I.MX6ULL 參考手冊(cè)》可知,I.MX6ULL 的UART1 寄存器首地址為0x02020000,但是UART1 的地址長(zhǎng)度(范圍)并沒(méi)有0x4000 這么多,這里我們重點(diǎn)是獲取UART1 寄存器首地址。

6、ranges 屬性

ranges 屬性值可以為空或者按照(child-bus-address,parent-bus-address,length)格式編寫(xiě)的數(shù)字矩陣,ranges 是一個(gè)地址映射/轉(zhuǎn)換表,ranges 屬性每個(gè)項(xiàng)目由子地址、父地址和地址空間長(zhǎng)度這三部分組成:

  • child-bus-address:子總線(xiàn)地址空間的物理地址,由父節(jié)點(diǎn)的#address-cells 確定此物理地址所占用的字長(zhǎng)。
  • parent-bus-address:父總線(xiàn)地址空間的物理地址,同樣由父節(jié)點(diǎn)的#address-cells 確定此物理地址所占用的字長(zhǎng)。
  • length:子地址空間的長(zhǎng)度,由父節(jié)點(diǎn)的#size-cells 確定此地址長(zhǎng)度所占用的字長(zhǎng)。

如果ranges 屬性值為空值,說(shuō)明子地址空間和父地址空間完全相同,不需要進(jìn)行地址轉(zhuǎn)換,對(duì)于我們所使用的I.MX6ULL 來(lái)說(shuō),子地址空間和父地址空間完全相同,因此會(huì)在imx6ull.dtsi中找到大量的值為空的ranges 屬性,如下所示:

137 soc { 138 #address-cells = <1>; 139 #size-cells = <1>; 140 compatible = "simple-bus"; 141 interrupt-parent = <&gpc>; 142 ranges; ...... 1177 }

第142 行定義了ranges 屬性,但是ranges 屬性值為空。
ranges 屬性不為空的示例代碼如下所示:

1 soc { 2 compatible = "simple-bus"; 3 #address-cells = <1>; 4 #size-cells = <1>; 5 ranges = <0x0 0xe0000000 0x00100000>; 6 7 serial { 8 device_type = "serial"; 9 compatible = "ns16550"; 10 reg = <0x4600 0x100>; 11 clock-frequency = <0>; 12 interrupts = <0xA 0x8>; 13 interrupt-parent = <&ipic>; 14 }; 15 };

第5 行,節(jié)點(diǎn)soc 定義的ranges 屬性,值為<0x0 0xe0000000 0x00100000>,此屬性值指定了一個(gè)1024KB(0x00100000)的地址范圍,子地址空間的物理起始地址為0x0,父地址空間的物理起始地址為0xe0000000。

第10 行,serial 是串口設(shè)備節(jié)點(diǎn),reg 屬性定義了serial 設(shè)備寄存器的起始地址為0x4600,寄存器長(zhǎng)度為0x100。經(jīng)過(guò)地址轉(zhuǎn)換,serial 設(shè)備可以從0xe0004600 開(kāi)始進(jìn)行讀寫(xiě)操作,0xe0004600=0x4600+0xe0000000。

7、name 屬性(棄用)

name 屬性值為字符串,name 屬性用于記錄節(jié)點(diǎn)名字,name 屬性已經(jīng)被棄用,不推薦使用name 屬性,一些老的設(shè)備樹(shù)文件可能會(huì)使用此屬性。

8、device_type 屬性(棄用)

device_type 屬性值為字符串,IEEE 1275 會(huì)用到此屬性,用于描述設(shè)備的FCode,但是設(shè)備樹(shù)沒(méi)有FCode,所以此屬性也被拋棄了。此屬性只能用于cpu 節(jié)點(diǎn)或者memory 節(jié)點(diǎn)。
imx6ull.dtsi 的cpu0 節(jié)點(diǎn)用到了此屬性,內(nèi)容如下所示:

54 cpu0: cpu@0 { 55 compatible = "arm,cortex-a7"; 56 device_type = "cpu"; 57 reg = <0>; ...... 89 };

關(guān)于標(biāo)準(zhǔn)屬性就講解這么多,其他的比如中斷、IIC、SPI 等使用的標(biāo)準(zhǔn)屬性等到具體的例程再講解。

根節(jié)點(diǎn)compatible屬性(設(shè)備怎么匹配到對(duì)應(yīng)的內(nèi)核驅(qū)動(dòng)?了解)

每個(gè)節(jié)點(diǎn)都有compatible 屬性,根節(jié)點(diǎn)“/”也不例外,imx6ull-alientek-emmc.dts 文件中根節(jié)點(diǎn)的compatible 屬性?xún)?nèi)容如下所示:

14 / { 15 model = "Freescale i.MX6 ULL 14x14 EVK Board"; 16 compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull"; ...... 148 }

可以看出,compatible 有兩個(gè)值:“fsl,imx6ull-14x14-evk”和“fsl,imx6ull”。

  • 設(shè)備節(jié)點(diǎn)的compatible 屬性值是為了匹配Linux 內(nèi)核中的驅(qū)動(dòng)程序
  • 根節(jié)點(diǎn)的compatible 屬性可以知道我們所使用的設(shè)備,一般第一個(gè)值描述了所使用的硬件設(shè)備名字,比如這里使用的是“imx6ull-14x14-evk”這個(gè)設(shè)備,第二個(gè)值描述了設(shè)備所使用的SOC,比如這里使用的是“imx6ull”這顆SOC。

Linux 內(nèi)核會(huì)通過(guò)根節(jié)點(diǎn)的compoatible 屬性查看是否支持此設(shè)備,如果支持的話(huà)設(shè)備就會(huì)啟動(dòng)Linux 內(nèi)核。接下來(lái)我們就來(lái)學(xué)習(xí)一下Linux 內(nèi)核在使用設(shè)備樹(shù)前后是如何判斷是否支持某款設(shè)備的。

1、使用設(shè)備樹(shù)之前設(shè)備匹配方法(machine id)

在沒(méi)有使用設(shè)備樹(shù)以前,uboot 會(huì)向Linux 內(nèi)核傳遞一個(gè)叫做machine id(設(shè)備ID)的值,告訴Linux 內(nèi)核自己是個(gè)什么設(shè)備,看看Linux 內(nèi)核是否支持。Linux 內(nèi)核是支持很多設(shè)備的,針對(duì)每一個(gè)設(shè)備(板子),Linux內(nèi)核都用MACHINE_START 和MACHINE_END來(lái)定義一個(gè)machine_desc 結(jié)構(gòu)體來(lái)描述這個(gè)設(shè)備,比如在文件arch/arm/mach-imx/mach-mx35_3ds.c 中有如下定義:

613 MACHINE_START(MX35_3DS, "Freescale MX35PDK") 614 /* Maintainer: Freescale Semiconductor, Inc */ 615 .atag_offset = 0x100, 616 .map_io = mx35_map_io, 617 .init_early = imx35_init_early, 618 .init_irq = mx35_init_irq, 619 .init_time = mx35pdk_timer_init, 620 .init_machine = mx35_3ds_init, 621 .reserve = mx35_3ds_reserve, 622 .restart = mxc_restart, 623 MACHINE_END

上述代碼就是定義了“Freescale MX35PDK”這個(gè)設(shè)備,其中MACHINE_START 和MACHINE_END 定義在文件arch/arm/include/asm/mach/arch.h 中,內(nèi)容如下:

#define MACHINE_START(_type,_name) \ static const struct machine_desc __mach_desc_##_type \ __used \ __attribute__((__section__(".arch.info.init"))) = { \ .nr = MACH_TYPE_##_type, \ .name = _name, #define MACHINE_END \ };

根據(jù)MACHINE_START 和MACHINE_END 的宏定義,將示例代碼43.3.4.2 展開(kāi)后如下所示:

1 static const struct machine_desc __mach_desc_MX35_3DS \ 2 __used \ 3 __attribute__((__section__(".arch.info.init"))) = { 4 .nr = MACH_TYPE_MX35_3DS, 5 .name = "Freescale MX35PDK", 6 /* Maintainer: Freescale Semiconductor, Inc */ 7 .atag_offset = 0x100, 8 .map_io = mx35_map_io, 9 .init_early = imx35_init_early, 10 .init_irq = mx35_init_irq, 11 .init_time = mx35pdk_timer_init, 12 .init_machine = mx35_3ds_init, 13 .reserve = mx35_3ds_reserve, 14 .restart = mxc_restart, 15 };

從示例代碼43.3.4.3 中可以看出,這里定義了一個(gè)machine_desc 類(lèi)型的結(jié)構(gòu)體變量__mach_desc_MX35_3DS ,這個(gè)變量存儲(chǔ)在“.arch.info.init ”段中。第4 行的MACH_TYPE_MX35_3DS 就是“Freescale MX35PDK ”這個(gè)板子的machine id 。

MACH_TYPE_MX35_3DS 定義在文件include/generated/mach-types.h 中,此文件定義了大量的machine id,內(nèi)容如下所示:

15 #define MACH_TYPE_EBSA110 0 16 #define MACH_TYPE_RISCPC 1 17 #define MACH_TYPE_EBSA285 4 18 #define MACH_TYPE_NETWINDER 5 19 #define MACH_TYPE_CATS 6 20 #define MACH_TYPE_SHARK 15 21 #define MACH_TYPE_BRUTUS 16 22 #define MACH_TYPE_PERSONAL_SERVER 17 ...... 287 #define MACH_TYPE_MX35_3DS 1645 ...... 1000 #define MACH_TYPE_PFLA03 4575

第287 行就是MACH_TYPE_MX35_3DS 的值,為1645。
前面說(shuō)了,uboot 會(huì)給Linux 內(nèi)核傳遞machine id 這個(gè)參數(shù),Linux 內(nèi)核將machine id 與這些MACH_TYPE_XXX 宏進(jìn)行對(duì)比,如果相等的話(huà)就表示Linux 內(nèi)核支持這個(gè)設(shè)備,如果不支持的話(huà)那么這個(gè)設(shè)備就沒(méi)法啟動(dòng)Linux 內(nèi)核。

2、使用設(shè)備樹(shù)以后的設(shè)備匹配方法(設(shè)備根節(jié)點(diǎn)“/”的compatible 屬性值匹配)

當(dāng)Linux 內(nèi)核引入設(shè)備樹(shù)以后就不再使用MACHINE_START 了,而是換為了
DT_MACHINE_START。DT_MACHINE_START 也定義在文件arch/arm/include/asm/mach/arch.h里面,定義如下:

#define DT_MACHINE_START(_name, _namestr) \ static const struct machine_desc __mach_desc_##_name \ __used \ __attribute__((__section__(".arch.info.init"))) = { \ .nr = ~0, \ .name = _namestr,

可以看出,DT_MACHINE_START 和MACHINE_START 基本相同,只是.nr 的設(shè)置不同,在DT_MACHINE_START 里面將.nr 設(shè)置為~0,說(shuō)明引入設(shè)備樹(shù)以后不再根據(jù)machine id 來(lái)檢查L(zhǎng)inux內(nèi)核是否支持某個(gè)設(shè)備。
打開(kāi)文件arch/arm/mach-imx/mach-imx6ul.c,有如下所示內(nèi)容:

208 static const char *imx6ul_dt_compat[] __initconst = { 209 "fsl,imx6ul", 210 "fsl,imx6ull", 211 NULL, 212 }; 213 214 DT_MACHINE_START(IMX6UL, "Freescale i.MX6 Ultralite (Device Tree)") 215 .map_io = imx6ul_map_io, 216 .init_irq = imx6ul_init_irq, 217 .init_machine = imx6ul_init_machine, 218 .init_late = imx6ul_init_late, 219 .dt_compat = imx6ul_dt_compat, 220 MACHINE_END

machine_desc 結(jié)構(gòu)體中有個(gè) .dt_compat 成員變量(保存著本設(shè)備兼容屬性),示例代碼43.3.4.5 中設(shè)置.dt_compat = imx6ul_dt_compat,imx6ul_dt_compat 表里面有"fsl,imx6ul"和"fsl,imx6ull"這兩個(gè)兼容值。只要某個(gè)設(shè)備(板子)根節(jié)點(diǎn)“/”的compatible 屬性值與imx6ul_dt_compat 表中的任何一個(gè)值相等,那么就表示Linux 內(nèi)核支持此設(shè)備。imx6ull-alientek-emmc.dts 中根節(jié)點(diǎn)的compatible 屬性值如下:

compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";

其中“fsl,imx6ull”與imx6ul_dt_compat 中的“fsl,imx6ull”匹配,因此I.MX6U-ALPHA 開(kāi)發(fā)板可以正常啟動(dòng)Linux 內(nèi)核。如果將imx6ull-alientek-emmc.dts 根節(jié)點(diǎn)的compatible 屬性改為其他的值,比如:

compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ullll"

重新編譯DTS,并用新的DTS 啟動(dòng)Linux 內(nèi)核,結(jié)果如圖43.3.4.1 所示的錯(cuò)誤提示:

當(dāng)我們修改了根節(jié)點(diǎn)compatible 屬性?xún)?nèi)容以后,因?yàn)長(zhǎng)inux 內(nèi)核找不到對(duì)應(yīng)的設(shè)備,因此Linux 內(nèi)核無(wú)法啟動(dòng)。在uboot 輸出Starting kernel…以后就再也沒(méi)有其他信息輸出了。

接下來(lái)我們簡(jiǎn)單看一下Linux 內(nèi)核是如何根據(jù)設(shè)備樹(shù)根節(jié)點(diǎn)的compatible 屬性來(lái)匹配出對(duì)應(yīng)的machine_desc,Linux 內(nèi)核調(diào)用start_kernel 函數(shù)來(lái)啟動(dòng)內(nèi)核,start_kernel 函數(shù)會(huì)調(diào)用setup_arch 函數(shù)來(lái)匹配machine_desc,setup_arch 函數(shù)定義在文件arch/arm/kernel/setup.c 中,函數(shù)內(nèi)容如下(有縮減):

913 void __init setup_arch(char **cmdline_p) 914 { 915 const struct machine_desc *mdesc; 916 917 setup_processor(); 918 mdesc = setup_machine_fdt(__atags_pointer); 919 if (!mdesc) 920 mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type); 921 machine_desc = mdesc; 922 machine_name = mdesc->name; ...... 986 }

第918 行,調(diào)用setup_machine_fdt 函數(shù)來(lái)獲取匹配的machine_desc,參數(shù)就是atags 的首地址,也就是uboot 傳遞給Linux 內(nèi)核的dtb 文件首地址,setup_machine_fdt 函數(shù)的返回值就是找到的最匹配的machine_desc。

函數(shù)setup_machine_fdt 定義在文件arch/arm/kernel/devtree.c 中,內(nèi)容如下(有縮減):

204 const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys) 205 { 206 const struct machine_desc *mdesc, *mdesc_best = NULL; ...... 214 215 if (!dt_phys || !early_init_dt_verify(phys_to_virt(dt_phys))) 216 return NULL; 217 218 mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach); 219 ...... 247 __machine_arch_type = mdesc->nr; 248 249 return mdesc; 250 }

第218 行,調(diào)用函數(shù)of_flat_dt_match_machine 來(lái)獲取匹配的machine_desc,參數(shù)mdesc_best是默認(rèn)的machine_desc ,參數(shù)arch_get_next_mach 是個(gè)函數(shù),此函數(shù)定義在定義在
arch/arm/kernel/devtree.c 文件中。找到匹配的machine_desc 的過(guò)程就是用設(shè)備樹(shù)根節(jié)點(diǎn)的compatible 屬性值和Linux 內(nèi)核中machine_desc 下.dt_compat 的值比較,看看那個(gè)相等,如果相等的話(huà)就表示找到匹配的machine_desc,arch_get_next_mach 函數(shù)的工作就是獲取Linux 內(nèi)核中
下一個(gè)machine_desc 結(jié)構(gòu)體。

最后再來(lái)看一下of_flat_dt_match_machine 函數(shù),此函數(shù)定義在文件drivers/of/fdt.c 中,內(nèi)容如下(有縮減):

705 const void * __init of_flat_dt_match_machine(const void *default_match, 706 const void * (*get_next_compat)(const char * const**)) 707 { 708 const void *data = NULL; 709 const void *best_data = default_match; 710 const char *const *compat; 711 unsigned long dt_root; 712 unsigned int best_score = ~1, score = 0; 713 714 dt_root = of_get_flat_dt_root(); 715 while ((data = get_next_compat(&compat))) { 716 score = of_flat_dt_match(dt_root, compat); 717 if (score > 0 && score < best_score) { 718 best_data = data; 719 best_score = score; 720 } 721 } ...... 739 740 pr_info("Machine model: %s\n", of_flat_dt_get_machine_name()); 741 742 return best_data; 743 }

第714 行,通過(guò)函數(shù)of_get_flat_dt_root 獲取設(shè)備樹(shù)根節(jié)點(diǎn)。
第715~720 行,此循環(huán)就是查找匹配的machine_desc 過(guò)程,第716 行的of_flat_dt_match 函數(shù)會(huì)將根節(jié)點(diǎn)compatible 屬性的值和每個(gè)machine_desc 結(jié)構(gòu)體中. dt_compat 的值進(jìn)行比較,直至找到匹配的那個(gè)machine_desc
總結(jié)一下,Linux 內(nèi)核通過(guò)根節(jié)點(diǎn)compatible 屬性找到對(duì)應(yīng)的設(shè)備的函數(shù)調(diào)用過(guò)程,如圖43.3.4.2 所示:

向設(shè)備節(jié)點(diǎn)添加或修改內(nèi)容(不要直接添加在.dtsi頭文件,通過(guò)&在各自的.dts里添加)

產(chǎn)品開(kāi)發(fā)過(guò)程中可能面臨著頻繁的需求更改,比如第一版硬件上有一個(gè)IIC 接口的六軸芯片MPU6050,第二版硬件又要把這個(gè)IIC六軸芯片MPU6050 更換為MPU9250 等。一旦硬件修改了,我們就要同步的修改設(shè)備樹(shù)文件,畢竟設(shè)備樹(shù)是描述板子硬件信息的文件。假設(shè)現(xiàn)在有個(gè)六軸芯片fxls8471,fxls8471 要接到I.MX6U-ALPHA 開(kāi)發(fā)板的I2C1 接口上,那么相當(dāng)于需要在i2c1 這個(gè)節(jié)點(diǎn)上添加一個(gè)fxls8471 子節(jié)點(diǎn)。先看一下I2C1 接口對(duì)應(yīng)的節(jié)點(diǎn),打開(kāi)文件imx6ull.dtsi 文件,找到如下所示內(nèi)容:

937 i2c1: i2c@021a0000 { 938 #address-cells = <1>; 939 #size-cells = <0>; 940 compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c"; 941 reg = <0x021a0000 0x4000>; 942 interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>; 943 clocks = <&clks IMX6UL_CLK_I2C1>; 944 status = "disabled"; 945 };

示例代碼43.3.5.1 就是I.MX6ULL 的I2C1 節(jié)點(diǎn),現(xiàn)在要在i2c1 節(jié)點(diǎn)下創(chuàng)建一個(gè)子節(jié)點(diǎn),這個(gè)子節(jié)點(diǎn)就是fxls8471,最簡(jiǎn)單的方法就是在i2c1 下直接添加一個(gè)名為fxls8471 的子節(jié)點(diǎn),如下所示:

937 i2c1: i2c@021a0000 { 938 #address-cells = <1>; 939 #size-cells = <0>; 940 compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c"; 941 reg = <0x021a0000 0x4000>; 942 interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>; 943 clocks = <&clks IMX6UL_CLK_I2C1>; 944 status = "disabled"; 945 946 //fxls8471子節(jié)點(diǎn) 947 fxls8471@1e { 948 compatible = "fsl,fxls8471"; 949 reg = <0x1e>; 950 }; 951 };

第947~950 行就是添加的fxls8471 這個(gè)芯片對(duì)應(yīng)的子節(jié)點(diǎn)。但是這樣會(huì)有個(gè)問(wèn)題!i2c1 節(jié)點(diǎn)是定義在imx6ull.dtsi 文件中的,而imx6ull.dtsi 是設(shè)備樹(shù)頭文件,其他所有使用到I.MX6ULL這顆SOC 的板子都會(huì)引用imx6ull.dtsi 這個(gè)文件。直接在i2c1 節(jié)點(diǎn)中添加fxls8471 就相當(dāng)于在其他的所有板子上都添加了fxls8471 這個(gè)設(shè)備,但是其他的板子并沒(méi)有這個(gè)設(shè)備啊!因此,按照示例代碼43.3.5.2 這樣寫(xiě)肯定是不行的。

這里就要引入另外一個(gè)內(nèi)容,那就是如何向節(jié)點(diǎn)追加數(shù)據(jù),我們現(xiàn)在要解決的就是如何向i2c1 節(jié)點(diǎn)追加一個(gè)名為fxls8471 的子節(jié)點(diǎn),而且不能影響到其他使用到I.MX6ULL 的板子。
I.MX6U-ALPHA 開(kāi)發(fā)板使用的設(shè)備樹(shù)文件為imx6ull-alientek-emmc.dts,因此我們需要在imx6ull-alientek-emmc.dts 文件中完成數(shù)據(jù)追加的內(nèi)容,方式如下:

1 &i2c1 { 2 /* 要追加或修改的內(nèi)容*/ 3 };

第1 行,&i2c1 表示要訪(fǎng)問(wèn)i2c1 這個(gè)label 所對(duì)應(yīng)的節(jié)點(diǎn),也就是imx6ull.dtsi 中的“i2c1: i2c@021a0000”。
第2 行,花括號(hào)內(nèi)就是要向i2c1 這個(gè)節(jié)點(diǎn)添加的內(nèi)容,包括修改某些屬性的值。
打開(kāi)imx6ull-alientek-emmc.dts,找到如下所示內(nèi)容:

224 &i2c1 { 225 clock-frequency = <100000>; 226 pinctrl-names = "default"; 227 pinctrl-0 = <&pinctrl_i2c1>; 228 status = "okay"; 229 230 mag3110@0e { 231 compatible = "fsl,mag3110"; 232 reg = <0x0e>; 233 position = <2>; 234 }; 235 236 fxls8471@1e { 237 compatible = "fsl,fxls8471"; 238 reg = <0x1e>; 239 position = <0>; 240 interrupt-parent = <&gpio5>; 241 interrupts = <0 8>; 242 }; 243 };

示例代碼43.3.5.4 就是向i2c1 節(jié)點(diǎn)添加/修改數(shù)據(jù),比如第225 行的屬性“clock-frequency”就表示i2c1 時(shí)鐘為100KHz。“clock-frequency”就是新添加的屬性。
第228 行,將status 屬性的值由原來(lái)的disabled 改為okay。
第230~234 行,i2c1 子節(jié)點(diǎn)mag3110,因?yàn)镹XP 官方開(kāi)發(fā)板在I2C1 上接了一個(gè)磁力計(jì)芯片mag3110,正點(diǎn)原子的I.MX6U-ALPHA 開(kāi)發(fā)板并沒(méi)有使用mag3110。
第236~242 行,i2c1 子節(jié)點(diǎn)fxls8471,同樣是因?yàn)镹XP 官方開(kāi)發(fā)板在I2C1 上接了fxls8471這顆六軸芯片。
因?yàn)槭纠a43.3.5.4 中的內(nèi)容是imx6ull-alientek-emmc.dts 這個(gè)文件內(nèi)的,所以不會(huì)對(duì)使用I.MX6ULL 這顆SOC 的其他板子造成任何影響。這個(gè)就是向節(jié)點(diǎn)追加或修改內(nèi)容,重點(diǎn)就是通過(guò)&label 來(lái)訪(fǎng)問(wèn)節(jié)點(diǎn),然后直接在里面編寫(xiě)要追加或者修改的內(nèi)容。

創(chuàng)建小型模板設(shè)備樹(shù)(了解)

上一節(jié)已經(jīng)對(duì)DTS 的語(yǔ)法做了比較詳細(xì)的講解,本節(jié)我們就根據(jù)前面講解的語(yǔ)法,從頭到尾編寫(xiě)一個(gè)小型的設(shè)備樹(shù)文件。當(dāng)然了,這個(gè)小型設(shè)備樹(shù)沒(méi)有實(shí)際的意義,做這個(gè)的目的是為了掌握設(shè)備樹(shù)的語(yǔ)法。在實(shí)際產(chǎn)品開(kāi)發(fā)中,我們是不需要完完全全的重寫(xiě)一個(gè).dts 設(shè)備樹(shù)文件,一般都是使用SOC 廠(chǎng)商提供好的.dts 文件,我們只需要在上面根據(jù)自己的實(shí)際情況做相應(yīng)的修
改即可。在編寫(xiě)設(shè)備樹(shù)之前要先定義一個(gè)設(shè)備,我們就以I.MX6ULL 這個(gè)SOC 為例,我們需要在設(shè)備樹(shù)里面描述的內(nèi)容如下:

①、I.MX6ULL 這個(gè)Cortex-A7 架構(gòu)的32 位CPU。
②、I.MX6ULL 內(nèi)部ocram,起始地址0x00900000,大小為128KB(0x20000)。
③、I.MX6ULL 內(nèi)部aips1 域下的ecspi1 外設(shè)控制器,寄存器起始地址為0x02008000,大小為0x4000。
④、I.MX6ULL 內(nèi)部aips2 域下的usbotg1 外設(shè)控制器,寄存器起始地址為0x02184000,大小為0x4000。
⑤、I.MX6ULL 內(nèi)部aips3 域下的rngb 外設(shè)控制器,寄存器起始地址為0x02284000,大小為0x4000。

為了簡(jiǎn)單起見(jiàn),我們就在設(shè)備樹(shù)里面就實(shí)現(xiàn)這些內(nèi)容即可,首先,搭建一個(gè)僅含有根節(jié)點(diǎn)“/”的基礎(chǔ)的框架,新建一個(gè)名為myfirst.dts 文件,在里面輸入如下所示內(nèi)容:

1 / { 2 compatible = "fsl,imx6ull-alientek-evk", "fsl,imx6ull"; 3 }

設(shè)備樹(shù)框架很簡(jiǎn)單,就一個(gè)根節(jié)點(diǎn)“/”,根節(jié)點(diǎn)里面只有一個(gè)compatible 屬性。我們就在這個(gè)基礎(chǔ)框架上面將上面列出的內(nèi)容一點(diǎn)點(diǎn)添加進(jìn)來(lái)。

1、添加cpus 節(jié)點(diǎn)
首先添加CPU 節(jié)點(diǎn),I.MX6ULL 采用Cortex-A7 架構(gòu),而且只有一個(gè)CPU,因此只有一個(gè)cpu0 節(jié)點(diǎn),完成以后如下所示:

1 / { 2 compatible = "fsl,imx6ull-alientek-evk", "fsl,imx6ull"; 3 4 cpus { 5 #address-cells = <1>; 6 #size-cells = <0>; 7 8 //CPU0節(jié)點(diǎn) 9 cpu0: cpu@0 { 10 compatible = "arm,cortex-a7"; 11 device_type = "cpu"; 12 reg = <0>; 13 }; 14 }; 15 }

第4~14 行,cpus 節(jié)點(diǎn),此節(jié)點(diǎn)用于描述SOC 內(nèi)部的所有CPU,因?yàn)镮.MX6ULL 只有一個(gè)CPU,因此只有一個(gè)cpu0 子節(jié)點(diǎn)。

2、添加soc 節(jié)點(diǎn)
像uart,iic 控制器等等這些都屬于SOC 內(nèi)部外設(shè),因此一般會(huì)創(chuàng)建一個(gè)叫做soc 的父節(jié)點(diǎn)來(lái)管理這些SOC 內(nèi)部外設(shè)的子節(jié)點(diǎn),添加soc 節(jié)點(diǎn)以后的myfirst.dts 文件內(nèi)容如下所示:

1 / { 2 compatible = "fsl,imx6ull-alientek-evk", "fsl,imx6ull"; 3 4 cpus { 5 #address-cells = <1>; 6 #size-cells = <0>; 7 8 //CPU0節(jié)點(diǎn) 9 cpu0: cpu@0 { 10 compatible = "arm,cortex-a7"; 11 device_type = "cpu"; 12 reg = <0>; 13 }; 14 }; 15 16 //soc節(jié)點(diǎn) 17 soc { 18 #address-cells = <1>; 19 #size-cells = <1>; 20 compatible = "simple-bus"; 21 ranges; 22 } 23 }

第17~22 行,soc 節(jié)點(diǎn),soc 節(jié)點(diǎn)設(shè)置#address-cells = <1>,#size-cells = <1>,這樣soc 子節(jié)點(diǎn)的reg 屬性中起始地占用一個(gè)字長(zhǎng),地址空間長(zhǎng)度也占用一個(gè)字長(zhǎng)。
第21 行,ranges 屬性,ranges 屬性為空,說(shuō)明子空間和父空間地址范圍相同。

3、添加ocram 節(jié)點(diǎn)

根據(jù)第②點(diǎn)的要求,添加ocram 節(jié)點(diǎn),ocram 是I.MX6ULL 內(nèi)部RAM,因此ocram 節(jié)點(diǎn)應(yīng)該是soc 節(jié)點(diǎn)的子節(jié)點(diǎn)。ocram 起始地址為0x00900000,大小為128KB(0x20000),添加ocram節(jié)點(diǎn)以后myfirst.dts 文件內(nèi)容如下所示:

1 / { 2 compatible = "fsl,imx6ull-alientek-evk", "fsl,imx6ull"; 3 4 cpus { 5 #address-cells = <1>; 6 #size-cells = <0>; 7 8 //CPU0節(jié)點(diǎn) 9 cpu0: cpu@0 { 10 compatible = "arm,cortex-a7"; 11 device_type = "cpu"; 12 reg = <0>; 13 }; 14 }; 15 16 //soc節(jié)點(diǎn) 17 soc { 18 #address-cells = <1>; 19 #size-cells = <1>; 20 compatible = "simple-bus"; 21 ranges; 22 23 //ocram節(jié)點(diǎn) 24 ocram: sram@00900000 { 25 compatible = "fsl,lpm-sram"; 26 reg = <0x00900000 0x20000>; 27 }; 28 } 29 }

第24~27 行,ocram 節(jié)點(diǎn),第24 行節(jié)點(diǎn)名字@后面的0x00900000 就是ocram 的起始地址。
第26 行的reg 屬性也指明了ocram 內(nèi)存的起始地址為0x00900000,大小為0x20000。

4、添加aips1、aips2 和aips3 這三個(gè)子節(jié)點(diǎn)
I.MX6ULL 內(nèi)部分為三個(gè)域:aips1~ 3,這三個(gè)域分管不同的外設(shè)控制器,aips1~3 這三個(gè)域?qū)?yīng)的內(nèi)存范圍如表43.4.1 所示:

域起始地址大小(十六進(jìn)制)
AIPS10X020000000X100000
AIPS20X021000000X100000
AIPS30X022000000X100000

我們先在設(shè)備樹(shù)中添加這三個(gè)域?qū)?yīng)的子節(jié)點(diǎn)。aips1~3 這三個(gè)域都屬于soc 節(jié)點(diǎn)的子節(jié)點(diǎn),完成以后的myfirst.dts 文件內(nèi)容如下所示:

1 / { 2 compatible = "fsl,imx6ull-alientek-evk", "fsl,imx6ull"; 3 4 cpus { 5 #address-cells = <1>; 6 #size-cells = <0>; 7 8 //CPU0節(jié)點(diǎn) 9 cpu0: cpu@0 { 10 compatible = "arm,cortex-a7"; 11 device_type = "cpu"; 12 reg = <0>; 13 }; 14 }; 15 16 //soc節(jié)點(diǎn) 17 soc { 18 #address-cells = <1>; 19 #size-cells = <1>; 20 compatible = "simple-bus"; 21 ranges; 22 23 //ocram節(jié)點(diǎn) 24 ocram: sram@00900000 { 25 compatible = "fsl,lpm-sram"; 26 reg = <0x00900000 0x20000>; 27 }; 28 29 //aips1節(jié)點(diǎn) 30 aips1: aips-bus@02000000 { 31 compatible = "fsl,aips-bus", "simple-bus"; 32 #address-cells = <1>; 33 #size-cells = <1>; 34 reg = <0x02000000 0x100000>; 35 ranges; 36 } 37 38 //aips2節(jié)點(diǎn) 39 aips2: aips-bus@02100000 { 40 compatible = "fsl,aips-bus", "simple-bus"; 41 #address-cells = <1>; 42 #size-cells = <1>; 43 reg = <0x02100000 0x100000>; 44 ranges; 45 } 46 47 //aips3節(jié)點(diǎn) 48 aips3: aips-bus@02200000 { 49 compatible = "fsl,aips-bus", "simple-bus"; 50 #address-cells = <1>; 51 #size-cells = <1>; 52 reg = <0x02200000 0x100000>; 53 ranges; 54 } 55 } 56 }

第30~36 行,aips1 節(jié)點(diǎn)。
第39~45 行,aips2 節(jié)點(diǎn)。
第48~54 行,aips3 節(jié)點(diǎn)。

5、添加ecspi1、usbotg1 和rngb 這三個(gè)外設(shè)控制器節(jié)點(diǎn)

最后我們?cè)趍yfirst.dts 文件中加入ecspi1,usbotg1 和rngb 這三個(gè)外設(shè)控制器對(duì)應(yīng)的節(jié)點(diǎn),其中ecspi1 屬于aips1 的子節(jié)點(diǎn),usbotg1 屬于aips2 的子節(jié)點(diǎn),rngb 屬于aips3 的子節(jié)點(diǎn)。最終的myfirst.dts 文件內(nèi)容如下:

1 / { 2 compatible = "fsl,imx6ull-alientek-evk", "fsl,imx6ull"; 3 4 cpus { 5 #address-cells = <1>; 6 #size-cells = <0>; 7 8 //CPU0節(jié)點(diǎn) 9 cpu0: cpu@0 { 10 compatible = "arm,cortex-a7"; 11 device_type = "cpu"; 12 reg = <0>; 13 }; 14 }; 15 16 //soc節(jié)點(diǎn) 17 soc { 18 #address-cells = <1>; 19 #size-cells = <1>; 20 compatible = "simple-bus"; 21 ranges; 22 23 //ocram節(jié)點(diǎn) 24 ocram: sram@00900000 { 25 compatible = "fsl,lpm-sram"; 26 reg = <0x00900000 0x20000>; 27 }; 28 29 //aips1節(jié)點(diǎn) 30 aips1: aips-bus@02000000 { 31 compatible = "fsl,aips-bus", "simple-bus"; 32 #address-cells = <1>; 33 #size-cells = <1>; 34 reg = <0x02000000 0x100000>; 35 ranges; 36 37 //ecspi1節(jié)點(diǎn) 38 ecspi1: ecspi@02008000 { 39 #address-cells = <1>; 40 #size-cells = <0>; 41 compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi"; 42 reg = <0x02008000 0x4000>; 43 status = "disabled"; 44 }; 45 } 46 47 //aips2節(jié)點(diǎn) 48 aips2: aips-bus@02100000 { 49 compatible = "fsl,aips-bus", "simple-bus"; 50 #address-cells = <1>; 51 #size-cells = <1>; 52 reg = <0x02100000 0x100000>; 53 ranges; 54 55 //usbotg1節(jié)點(diǎn) 56 usbotg1: usb@02184000 { 57 compatible = "fsl,imx6ul-usb", "fsl,imx27-usb"; 58 reg = <0x02184000 0x4000>; 59 status = "disabled"; 60 }; 61 } 62 63 //aips3節(jié)點(diǎn) 64 aips3: aips-bus@02200000 { 65 compatible = "fsl,aips-bus", "simple-bus"; 66 #address-cells = <1>; 67 #size-cells = <1>; 68 reg = <0x02200000 0x100000>; 69 ranges; 70 71 //rngb節(jié)點(diǎn) 72 rngb: rngb@02284000 { 73 compatible = "fsl,imx6sl-rng", "fsl,imx-rng", "imx-rng"; 74 reg = <0x02284000 0x4000>; 75 }; 76 } 77 } 78 }

第38~44 行,ecspi1 外設(shè)控制器節(jié)點(diǎn)。
第56~60 行,usbotg1 外設(shè)控制器節(jié)點(diǎn)。
第72~75 行,rngb 外設(shè)控制器節(jié)點(diǎn)。

至此,myfirst.dts 這個(gè)小型的模板設(shè)備樹(shù)就編寫(xiě)好了,基本和imx6ull.dtsi 很像,可以看做是imx6ull.dtsi 的縮小版。在myfirst.dts 里面我們僅僅是編寫(xiě)了I.MX6ULL 的外設(shè)控制器節(jié)點(diǎn),像IIC 接口,SPI 接口下所連接的具體設(shè)備我們并沒(méi)有寫(xiě),因?yàn)榫唧w的設(shè)備其設(shè)備樹(shù)屬性?xún)?nèi)容不同,這個(gè)等到具體的實(shí)驗(yàn)在詳細(xì)講解。

設(shè)備樹(shù)在系統(tǒng)中的體現(xiàn)(/proc/device-tree目錄下列出所有根節(jié)點(diǎn)“/”及其屬性,了解)

Linux 內(nèi)核啟動(dòng)的時(shí)候會(huì)解析設(shè)備樹(shù)中各個(gè)節(jié)點(diǎn)的信息,并且在根文件系統(tǒng)的/proc/device-tree 目錄下根據(jù)節(jié)點(diǎn)名字創(chuàng)建不同文件夾,如圖43.5.1 所示:

圖43.5.1 就是目錄/proc/device-tree 目錄下的內(nèi)容,/proc/device-tree 目錄下列出根節(jié)點(diǎn)“/”的所有屬性和子節(jié)點(diǎn),我們依次來(lái)看一下這些屬性和子節(jié)點(diǎn)。

1、根節(jié)點(diǎn)“/”各個(gè)屬性

在圖43.5.1 中,根節(jié)點(diǎn)屬性屬性表現(xiàn)為一個(gè)個(gè)的文件(圖中細(xì)字體文件),比如圖43.5.1 中的“#address-cells”、“#size-cells”、“compatible”、“model”和“name”這5 個(gè)文件,它們?cè)谠O(shè)備樹(shù)中就是根節(jié)點(diǎn)的5 個(gè)屬性。既然是文件那么肯定可以查看其內(nèi)容,輸入cat 命令來(lái)查看model和compatible 這兩個(gè)文件的內(nèi)容,結(jié)果如圖43.5.2 所示:

從圖43.5.2 可以看出,文件model 的內(nèi)容是“Freescale i.MX6 ULL 14x14 EVK Board”,文件compatible 的內(nèi)容為“fsl,imx6ull-14x14-evkfsl,imx6ull”。打開(kāi)文件imx6ull-alientek-emmc.dts查看一下,這不正是根節(jié)點(diǎn)“/”的model 和compatible 屬性值嗎!

2、根節(jié)點(diǎn)“/”各子節(jié)點(diǎn)

圖43.5.1 中各個(gè)文件夾(圖中粗字體文件夾)就是根節(jié)點(diǎn)“/”的各個(gè)子節(jié)點(diǎn),比如“aliases”、“backlight”、“chosen”和“clocks”等等。大家可以查看一下imx6ull-alientek-emmc.dts 和imx6ull.dtsi 這兩個(gè)文件,看看根節(jié)點(diǎn)的子節(jié)點(diǎn)都有哪些,看看是否和圖43.5.1 中的一致。

/proc/device-tree 目錄就是設(shè)備樹(shù)在根文件系統(tǒng)中的體現(xiàn),同樣是按照樹(shù)形結(jié)構(gòu)組織的,進(jìn)入/proc/device-tree/soc 目錄中就可以看到soc 節(jié)點(diǎn)的所有子節(jié)點(diǎn),如圖43.5.3 所示:

和根節(jié)點(diǎn)“/”一樣,圖43.5.3 中的所有文件分別為soc 節(jié)點(diǎn)的屬性文件和子節(jié)點(diǎn)文件夾。

大家可以自行查看一下這些屬性文件的內(nèi)容是否和imx6ull.dtsi 中soc 節(jié)點(diǎn)的屬性值相同,也可以進(jìn)入“busfreq”這樣的文件夾里面查看soc 節(jié)點(diǎn)的子節(jié)點(diǎn)信息。

特殊節(jié)點(diǎn)(aliases和chosen,了解)

在根節(jié)點(diǎn)“/”中有兩個(gè)特殊的子節(jié)點(diǎn):aliases 和chosen,我們接下來(lái)看一下這兩個(gè)特殊的子節(jié)點(diǎn)。

aliases 子節(jié)點(diǎn)

打開(kāi)imx6ull.dtsi 文件,aliases 節(jié)點(diǎn)內(nèi)容如下所示:

18 aliases { 19 can0 = &flexcan1; 20 can1 = &flexcan2; 21 ethernet0 = &fec1; 22 ethernet1 = &fec2; 23 gpio0 = &gpio1; 24 gpio1 = &gpio2; ...... 42 spi0 = &ecspi1; 43 spi1 = &ecspi2; 44 spi2 = &ecspi3; 45 spi3 = &ecspi4; 46 usbphy0 = &usbphy1; 47 usbphy1 = &usbphy2; 48 };

單詞aliases 的意思是“別名”,因此aliases 節(jié)點(diǎn)的主要功能就是定義別名,定義別名的目的就是為了方便訪(fǎng)問(wèn)節(jié)點(diǎn)。不過(guò)我們一般會(huì)在節(jié)點(diǎn)命名的時(shí)候會(huì)加上label,然后通過(guò)&label來(lái)訪(fǎng)問(wèn)節(jié)點(diǎn),這樣也很方便,而且設(shè)備樹(shù)里面大量的使用&label 的形式來(lái)訪(fǎng)問(wèn)節(jié)點(diǎn)。

chosen 子節(jié)點(diǎn)

chosen 并不是一個(gè)真實(shí)的設(shè)備,chosen 節(jié)點(diǎn)主要是為了uboot 向Linux 內(nèi)核傳遞數(shù)據(jù),重點(diǎn)是bootargs 參數(shù)。一般.dts 文件中chosen 節(jié)點(diǎn)通常為空或者內(nèi)容很少,imx6ull-alientek-emmc.dts 中chosen 節(jié)點(diǎn)內(nèi)容如下所示:

18 chosen { 19 stdout-path = &uart1; 20 };

從示例代碼43.6.2.1 中可以看出,chosen 節(jié)點(diǎn)僅僅設(shè)置了屬性“stdout-path”,表示標(biāo)準(zhǔn)輸出使用uart1。但是當(dāng)我們進(jìn)入到/proc/device-tree/chosen 目錄里面,會(huì)發(fā)現(xiàn)多了bootargs 這個(gè)屬性,如圖43.6.2.1 所示:

輸入cat 命令查看bootargs 這個(gè)文件的內(nèi)容,結(jié)果如圖43.6.2.2 所示:

從圖43.6.2.2 可以看出,bootargs 這個(gè)文件的內(nèi)容為“console=ttymxc0,115200……”,這個(gè)不就是我們?cè)趗boot 中設(shè)置的bootargs 環(huán)境變量的值嗎?現(xiàn)在有兩個(gè)疑點(diǎn):
①、我們并沒(méi)有在設(shè)備樹(shù)中設(shè)置chosen 節(jié)點(diǎn)的bootargs 屬性,那么圖43.6.2.1 中bootargs這個(gè)屬性是怎么產(chǎn)生的?
②、為何bootargs 文件的內(nèi)容和uboot 中bootargs 環(huán)境變量的值一樣?它們之間有什么關(guān)系?

前面講解uboot 的時(shí)候說(shuō)過(guò),uboot 在啟動(dòng)Linux 內(nèi)核的時(shí)候會(huì)將bootargs 的值傳遞給Linux內(nèi)核,bootargs 會(huì)作為L(zhǎng)inux 內(nèi)核的命令行參數(shù),Linux 內(nèi)核啟動(dòng)的時(shí)候會(huì)打印出命令行參數(shù)(也就是uboot 傳遞進(jìn)來(lái)的bootargs 的值),如圖43.6.2.3 所示:

既然chosen 節(jié)點(diǎn)的bootargs 屬性不是我們?cè)谠O(shè)備樹(shù)里面設(shè)置的,那么只有一種可能,那就是uboot 自己在chosen 節(jié)點(diǎn)里面添加了bootargs 屬性,并且設(shè)置bootargs 屬性的值為bootargs環(huán)境變量的值。因?yàn)?strong>在啟動(dòng)Linux 內(nèi)核之前,只有uboot 知道bootargs 環(huán)境變量的值,并且uboot也知道.dtb 設(shè)備樹(shù)文件在DRAM 中的位置。

在uboot 源碼中全局搜索“chosen”這個(gè)字符串,發(fā)現(xiàn)在common/fdt_support.c 文件中發(fā)現(xiàn)了“chosen”,fdt_support.c 文件中有個(gè)fdt_chosen 函數(shù),此函數(shù)內(nèi)容如下所示:

275 int fdt_chosen(void *fdt) 276 { 277 int nodeoffset; 278 int err; 279 char *str; /* used to set string properties */ 280 281 err = fdt_check_header(fdt); 282 if (err < 0) { 283 printf("fdt_chosen: %s\n", fdt_strerror(err)); 284 return err; 285 } 286 287 /* find or create "/chosen" node. */ 288 nodeoffset = fdt_find_or_add_subnode(fdt, 0, "chosen"); 289 if (nodeoffset < 0) 290 return nodeoffset; 291 292 str = getenv("bootargs"); 293 if (str) { 294 err = fdt_setprop(fdt, nodeoffset, "bootargs", str, 295 strlen(str) + 1); 296 if (err < 0) { 297 printf("WARNING: could not set bootargs %s.\n", 298 fdt_strerror(err)); 299 return err; 300 } 301 } 302 303 return fdt_fixup_stdout(fdt, nodeoffset); 304 }

第288 行,調(diào)用函數(shù)fdt_find_or_add_subnode 從設(shè)備樹(shù)(.dtb)中找到chosen 節(jié)點(diǎn),如果沒(méi)有找到的話(huà)就會(huì)自己創(chuàng)建一個(gè)chosen 節(jié)點(diǎn)。
第292 行,讀取uboot 中bootargs 環(huán)境變量的內(nèi)容。
第294 行,調(diào)用函數(shù)fdt_setprop 向chosen 節(jié)點(diǎn)添加bootargs 屬性,并且bootargs 屬性的值就是環(huán)境變量bootargs 的內(nèi)容。

證據(jù)“實(shí)錘”了,就是uboot 中的fdt_chosen 函數(shù)在設(shè)備樹(shù)的chosen 節(jié)點(diǎn)中加入了bootargs屬性,并且還設(shè)置了bootargs 屬性值。接下來(lái)我們順著fdt_chosen 函數(shù)一點(diǎn)點(diǎn)的抽絲剝繭,看看都有哪些函數(shù)調(diào)用了fdt_chosen,一直找到最終的源頭。最終發(fā)現(xiàn)整個(gè)流程見(jiàn)圖43.6.2.4:

圖43.6.2.4 中框起來(lái)的部分就是函數(shù)do_bootm_linux 函數(shù)的執(zhí)行流程,也就是說(shuō)do_bootm_linux 函數(shù)會(huì)通過(guò)一系列復(fù)雜的調(diào)用,最終通過(guò)fdt_chosen 函數(shù)在chosen 節(jié)點(diǎn)中加入了bootargs 屬性。而我們通過(guò)bootz 命令啟動(dòng)Linux 內(nèi)核的時(shí)候會(huì)運(yùn)行do_bootm_linux 函數(shù),至此,真相大白,一切事情的源頭都源于如下命令:

bootz 8080000083000000

當(dāng)我們輸入上述命令并執(zhí)行以后,do_bootz 函數(shù)就會(huì)執(zhí)行,然后一切就按照?qǐng)D43.6.2.4 中所示的流程開(kāi)始運(yùn)行。

Linux 內(nèi)核解析DTB文件(了解)

Linux 內(nèi)核在啟動(dòng)的時(shí)候會(huì)解析DTB 文件,然后在/proc/device-tree 目錄下生成相應(yīng)的設(shè)備樹(shù)節(jié)點(diǎn)文件。接下來(lái)我們簡(jiǎn)單分析一下Linux 內(nèi)核是如何解析DTB 文件的,流程如圖43.7.1 所示:

從圖43.7.1 中可以看出,在start_kernel 函數(shù)中完成了設(shè)備樹(shù)節(jié)點(diǎn)解析的工作,最終實(shí)際工
作的函數(shù)為unflatten_dt_node。

綁定信息文檔(Linux內(nèi)核源碼中.txt 文檔描述如何添加設(shè)備節(jié)點(diǎn)規(guī)則,了解)

設(shè)備樹(shù)是用來(lái)描述板子上的設(shè)備信息的,不同的設(shè)備其信息不同,反映到設(shè)備樹(shù)中就是屬性不同。那么我們?cè)谠O(shè)備樹(shù)中添加一個(gè)硬件對(duì)應(yīng)的節(jié)點(diǎn)的時(shí)候從哪里查閱相關(guān)的說(shuō)明呢?在Linux 內(nèi)核源碼中有詳細(xì)的.txt 文檔描述了如何添加節(jié)點(diǎn),這些.txt 文檔叫做綁定文檔,路徑為:Linux 源碼目錄/Documentation/devicetree/bindings,如圖43.8.1 所示:

比如我們現(xiàn)在要想在I.MX6ULL 這顆SOC 的I2C 下添加一個(gè)節(jié)點(diǎn),那么就可以查看Documentation/devicetree/bindings/i2c/i2c-imx.txt,此文檔詳細(xì)的描述了I.MX 系列的SOC 如何在設(shè)備樹(shù)中添加I2C 設(shè)備節(jié)點(diǎn),文檔內(nèi)容如下所示:

* Freescale Inter IC (I2C) and High Speed Inter IC (HS-I2C) for i.MX Required properties: - compatible : - "fsl,imx1-i2c" for I2C compatible with the one integrated on i.MX1 SoC - "fsl,imx21-i2c" for I2C compatible with the one integrated on i.MX21 SoC - "fsl,vf610-i2c" for I2C compatible with the one integrated on Vybrid vf610 SoC - reg : Should contain I2C/HS-I2C registers location and length - interrupts : Should contain I2C/HS-I2C interrupt - clocks : Should contain the I2C/HS-I2C clock specifier Optional properties: - clock-frequency : Constains desired I2C/HS-I2C bus clock frequency in Hz. The absence of the propoerty indicates the default frequency 100 kHz. - dmas: A list of two dma specifiers, one for each entry in dma-names. - dma-names: should contain "tx" and "rx". Examples: i2c@83fc4000 { /* I2C2 on i.MX51 */ compatible = "fsl,imx51-i2c", "fsl,imx21-i2c"; reg = <0x83fc4000 0x4000>; interrupts = <63>; }; i2c@70038000 { /* HS-I2C on i.MX51 */ compatible = "fsl,imx51-i2c", "fsl,imx21-i2c"; reg = <0x70038000 0x4000>; interrupts = <64>; clock-frequency = <400000>; }; i2c0: i2c@40066000 { /* i2c0 on vf610 */ compatible = "fsl,vf610-i2c"; reg = <0x40066000 0x1000>; interrupts =<0 71 0x04>; dmas = <&edma0 0 50>, <&edma0 0 51>; dma-names = "rx","tx"; };

有時(shí)候使用的一些芯片在Documentation/devicetree/bindings 目錄下找不到對(duì)應(yīng)的文檔,這個(gè)時(shí)候就要咨詢(xún)芯片的提供商,讓他們給你提供參考的設(shè)備樹(shù)文件。

設(shè)備樹(shù)常用OF操作函數(shù)(獲取reg’寄存器地址’屬性,操作外設(shè))

設(shè)備樹(shù)描述了設(shè)備的詳細(xì)信息,這些信息包括數(shù)字類(lèi)型的、字符串類(lèi)型的、數(shù)組類(lèi)型的,我們?cè)诰帉?xiě)驅(qū)動(dòng)的時(shí)候需要獲取到這些信息。比如設(shè)備樹(shù)使用reg 屬性描述了某個(gè)外設(shè)的寄存器地址為0X02005482,長(zhǎng)度為0X400,我們?cè)诰帉?xiě)驅(qū)動(dòng)的時(shí)候需要獲取到reg屬性的0X02005482 和0X400 這兩個(gè)值,然后初始化外設(shè)。Linux 內(nèi)核給我們提供了一系列的函數(shù)來(lái)獲取設(shè)備樹(shù)中的節(jié)點(diǎn)或者屬性信息,這一系列的函數(shù)都有一個(gè)統(tǒng)一的前綴“of_”,所以在很多資料里面也被叫做OF 函數(shù)。這些OF 函數(shù)原型都定義在include/linux/of.h 文件中。

查找節(jié)點(diǎn)的OF函數(shù)

設(shè)備都是以節(jié)點(diǎn)的形式 “掛”到設(shè)備樹(shù)上的,因此要想獲取這個(gè)設(shè)備的其他屬性信息,必須先獲取到這個(gè)設(shè)備的節(jié)點(diǎn)。Linux 內(nèi)核使用device_node 結(jié)構(gòu)體來(lái)描述一個(gè)節(jié)點(diǎn),此結(jié)構(gòu)體定義在文件include/linux/of.h 中,定義如下:

49 struct device_node { 50 const char *name; /* 節(jié)點(diǎn)名字*/ 51 const char *type; /* 設(shè)備類(lèi)型*/ 52 phandle phandle; 53 const char *full_name; /* 節(jié)點(diǎn)全名*/ 54 struct fwnode_handle fwnode; 55 56 struct property *properties; /* 屬性*/ 57 struct property *deadprops; /* removed屬性*/ 58 struct device_node *parent; /* 父節(jié)點(diǎn)*/ 59 struct device_node *child; /* 子節(jié)點(diǎn)*/ 60 struct device_node *sibling; 61 struct kobject kobj; 62 unsigned long _flags; 63 void *data; 64 #if defined(CONFIG_SPARC) 65 const char *path_component_name; 66 unsigned int unique_id; 67 struct of_irq_controller *irq_trans; 68 #endif 69 };

與查找節(jié)點(diǎn)有關(guān)的OF 函數(shù)有5 個(gè),我們依次來(lái)看一下。

1、of_find_node_by_name 函數(shù)

of_find_node_by_name 函數(shù)通過(guò)節(jié)點(diǎn)名字查找指定的節(jié)點(diǎn),函數(shù)原型如下:

struct device_node *of_find_node_by_name(struct device_node *from, const char *name);

函數(shù)參數(shù)和返回值含義如下:

  • from:開(kāi)始查找的節(jié)點(diǎn),如果為NULL 表示從根節(jié)點(diǎn)開(kāi)始查找整個(gè)設(shè)備樹(shù)(像鏈表)。
  • name:要查找的節(jié)點(diǎn)名字。
  • 返回值:找到的節(jié)點(diǎn),如果為NULL 表示查找失敗。

2、of_find_node_by_type 函數(shù)

of_find_node_by_type 函數(shù)通過(guò)device_type 屬性查找指定的節(jié)點(diǎn),函數(shù)原型如下:

struct device_node *of_find_node_by_type(struct device_node *from, const char *type)

函數(shù)參數(shù)和返回值含義如下:

  • from:開(kāi)始查找的節(jié)點(diǎn),如果為NULL 表示從根節(jié)點(diǎn)開(kāi)始查找整個(gè)設(shè)備樹(shù)。
  • type:要查找的節(jié)點(diǎn)對(duì)應(yīng)的type 字符串,也就是device_type 屬性值。
  • 返回值:找到的節(jié)點(diǎn),如果為NULL 表示查找失敗。

3、of_find_compatible_node 函數(shù)

of_find_compatible_node 函數(shù)根據(jù)device_type 和compatible 這兩個(gè)屬性查找指定的節(jié)點(diǎn),
函數(shù)原型如下:

struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compatible)

函數(shù)參數(shù)和返回值含義如下:

  • from:開(kāi)始查找的節(jié)點(diǎn),如果為NULL 表示從根節(jié)點(diǎn)開(kāi)始查找整個(gè)設(shè)備樹(shù)。
  • type:要查找的節(jié)點(diǎn)對(duì)應(yīng)的type 字符串,也就是device_type 屬性值,可以為NULL,表示忽略掉device_type 屬性。
  • compatible:要查找的節(jié)點(diǎn)所對(duì)應(yīng)的compatible 屬性列表。
  • 返回值:找到的節(jié)點(diǎn),如果為NULL 表示查找失敗

4、of_find_matching_node_and_match 函數(shù)

of_find_matching_node_and_match 函數(shù)通過(guò)of_device_id 匹配表來(lái)查找指定的節(jié)點(diǎn),函數(shù)原型如下:

struct device_node *of_find_matching_node_and_match(struct device_node *from, const struct of_device_id *matches, const struct of_device_id **match)

函數(shù)參數(shù)和返回值含義如下:

  • from:開(kāi)始查找的節(jié)點(diǎn),如果為NULL 表示從根節(jié)點(diǎn)開(kāi)始查找整個(gè)設(shè)備樹(shù)。
  • matches:of_device_id 匹配表,也就是在此匹配表里面查找節(jié)點(diǎn)。
  • match:找到的匹配的of_device_id。
  • 返回值:找到的節(jié)點(diǎn),如果為NULL 表示查找失敗

5、of_find_node_by_path 函數(shù)

of_find_node_by_path 函數(shù)通過(guò)路徑來(lái)查找指定的節(jié)點(diǎn),函數(shù)原型如下:

inline struct device_node *of_find_node_by_path(const char *path)

函數(shù)參數(shù)和返回值含義如下:

  • path:帶有全路徑的節(jié)點(diǎn)名,可以使用節(jié)點(diǎn)的別名,比如“/backlight”就是backlight 這個(gè)節(jié)點(diǎn)的全路徑。
  • 返回值:找到的節(jié)點(diǎn),如果為NULL 表示查找失敗

查找父/子節(jié)點(diǎn)的OF函數(shù)

Linux 內(nèi)核提供了幾個(gè)查找節(jié)點(diǎn)對(duì)應(yīng)的父節(jié)點(diǎn)或子節(jié)點(diǎn)的OF 函數(shù),我們依次來(lái)看一下。

1、of_get_parent 函數(shù)

of_get_parent 函數(shù)用于獲取指定節(jié)點(diǎn)的父節(jié)點(diǎn)(如果有父節(jié)點(diǎn)的話(huà)),函數(shù)原型如下:

struct device_node *of_get_parent(const struct device_node *node)

函數(shù)參數(shù)和返回值含義如下:

  • node:要查找的父節(jié)點(diǎn)的節(jié)點(diǎn)。
  • 返回值:找到的父節(jié)點(diǎn)。

2、of_get_next_child 函數(shù)

of_get_next_child 函數(shù)用迭代的方式查找子節(jié)點(diǎn),函數(shù)原型如下:

struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev)

函數(shù)參數(shù)和返回值含義如下:

  • node:父節(jié)點(diǎn)。
  • prev:前一個(gè)子節(jié)點(diǎn),也就是從哪一個(gè)子節(jié)點(diǎn)開(kāi)始迭代的查找下一個(gè)子節(jié)點(diǎn)。可以設(shè)置為NULL,表示從第一個(gè)子節(jié)點(diǎn)開(kāi)始。
  • 返回值:找到的下一個(gè)子節(jié)點(diǎn)。

提取屬性值的OF函數(shù)

節(jié)點(diǎn)的屬性信息里面保存了驅(qū)動(dòng)所需要的內(nèi)容,因此對(duì)于屬性值的提取非常重要,Linux 內(nèi)核中使用結(jié)構(gòu)體property 表示屬性,此結(jié)構(gòu)體同樣定義在文件include/linux/of.h 中,內(nèi)容如下:

35 struct property { 36 char *name; /* 屬性名字*/ 37 int length; /* 屬性長(zhǎng)度*/ 38 void *value; /* 屬性值*/ 39 struct property *next; /* 下一個(gè)屬性*/ 40 unsigned long _flags; 41 unsigned int unique_id; 42 struct bin_attribute attr; 43 };

Linux 內(nèi)核也提供了提取屬性值的OF 函數(shù),我們依次來(lái)看一下。

1、of_find_property 函數(shù)

of_find_property 函數(shù)用于查找指定的屬性,函數(shù)原型如下:

property *of_find_property(const struct device_node *np,const char *name,int *lenp)

函數(shù)參數(shù)和返回值含義如下:

  • np:設(shè)備節(jié)點(diǎn)。
  • name:屬性名字。
  • lenp:屬性值的字節(jié)數(shù)
  • 返回值:找到的屬性。

2、of_property_count_elems_of_size 函數(shù)

of_property_count_elems_of_size 函數(shù)用于獲取屬性中元素的數(shù)量,比如reg 屬性值是一個(gè)數(shù)組,那么使用此函數(shù)可以獲取到這個(gè)數(shù)組的大小,此函數(shù)原型如下:

int of_property_count_elems_of_size(const struct device_node *np,const char *propname,int elem_size)

函數(shù)參數(shù)和返回值含義如下:

  • np:設(shè)備節(jié)點(diǎn)。
  • proname:需要統(tǒng)計(jì)元素?cái)?shù)量的屬性名字。
  • elem_size:元素長(zhǎng)度。
  • 返回值:得到的屬性元素?cái)?shù)量。

3、of_property_read_u32_index 函數(shù)

of_property_read_u32_index 函數(shù)用于從屬性中獲取指定標(biāo)號(hào)的u32 類(lèi)型數(shù)據(jù)值(無(wú)符號(hào)32位),比如某個(gè)屬性有多個(gè)u32 類(lèi)型的值,那么就可以使用此函數(shù)來(lái)獲取指定標(biāo)號(hào)的數(shù)據(jù)值,此函數(shù)原型如下:

int of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value)

函數(shù)參數(shù)和返回值含義如下:

  • np:設(shè)備節(jié)點(diǎn)。
  • proname:要讀取的屬性名字。
  • index:要讀取的值標(biāo)號(hào)。
  • out_value:讀取到的值
  • 返回值:0 讀取成功,負(fù)值,讀取失敗,-EINVAL 表示屬性不存在,-ENODATA 表示沒(méi)有要讀取的數(shù)據(jù),-EOVERFLOW 表示屬性值列表太小。

4、of_property_read_u8_array 函數(shù)

of_property_read_u16_array 函數(shù)
of_property_read_u32_array 函數(shù)
of_property_read_u64_array 函數(shù)

這4 個(gè)函數(shù)分別是讀取屬性中u8、u16、u32 和u64 類(lèi)型的數(shù)組數(shù)據(jù),比如大多數(shù)的reg 屬性都是數(shù)組數(shù)據(jù),可以使用這4 個(gè)函數(shù)一次讀取出reg 屬性中的所有數(shù)據(jù)。這四個(gè)函數(shù)的原型如下:

int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz) int of_property_read_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz) int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz) int of_property_read_u64_array(const struct device_node *np, const char *propname, u64 *out_values, size_t sz)

函數(shù)參數(shù)和返回值含義如下:

  • np:設(shè)備節(jié)點(diǎn)。
  • proname:要讀取的屬性名字。
  • out_value:讀取到的數(shù)組值,分別為u8、u16、u32 和u64。
  • sz:要讀取的數(shù)組元素?cái)?shù)量。
  • 返回值:0,讀取成功,負(fù)值,讀取失敗,-EINVAL 表示屬性不存在,ENODATA 表示沒(méi)有要讀取的數(shù)據(jù),-EOVERFLOW 表示屬性值列表太小。

5、of_property_read_u8 函數(shù)

of_property_read_u16 函數(shù)
of_property_read_u32 函數(shù)
of_property_read_u64 函數(shù)

有些屬性只有一個(gè)整形值,這四個(gè)函數(shù)就是用于讀取這種只有一個(gè)整形值的屬性,分別用于讀取u8、u16、u32 和u64 類(lèi)型屬性值,函數(shù)原型如下:

int of_property_read_u8(const struct device_node *np, const char *propname, u8 *out_value) int of_property_read_u16(const struct device_node *np, const char *propname, u16 *out_value) int of_property_read_u32(const struct device_node *np, const char *propname, u32 *out_value) int of_property_read_u64(const struct device_node *np, const char *propname, u64 *out_value)

函數(shù)參數(shù)和返回值含義如下:

  • np:設(shè)備節(jié)點(diǎn)。
  • proname:要讀取的屬性名字。
  • out_value:讀取到的數(shù)組值。
  • 返回值:0,讀取成功,負(fù)值,讀取失敗,-EINVAL 表示屬性不存在,-ENODATA 表示沒(méi)有要讀取的數(shù)據(jù),-EOVERFLOW 表示屬性值列表太小。

6、of_property_read_string 函數(shù)

of_property_read_string 函數(shù)用于讀取屬性中字符串值,函數(shù)原型如下:

int of_property_read_string(struct device_node *np, const char *propname, const char **out_string)

函數(shù)參數(shù)和返回值含義如下:

  • np:設(shè)備節(jié)點(diǎn)。
  • proname:要讀取的屬性名字。
  • out_string:讀取到的字符串值。
  • 返回值:0,讀取成功,負(fù)值,讀取失敗。

7、of_n_addr_cells 函數(shù)

of_n_addr_cells 函數(shù)用于獲取#address-cells 屬性值,函數(shù)原型如下:

int of_n_addr_cells(struct device_node *np)

函數(shù)參數(shù)和返回值含義如下:

  • np:設(shè)備節(jié)點(diǎn)。
  • 返回值:獲取到的#address-cells 屬性值。

8、of_n_size_cells 函數(shù)

of_size_cells 函數(shù)用于獲取#size-cells 屬性值,函數(shù)原型如下:

int of_n_size_cells(struct device_node *np)

函數(shù)參數(shù)和返回值含義如下:

  • np:設(shè)備節(jié)點(diǎn)。
  • 返回值:獲取到的#size-cells 屬性值。

其他常用的OF函數(shù)

1、of_device_is_compatible 函數(shù)

of_device_is_compatible 函數(shù)用于查看節(jié)點(diǎn)的compatible 屬性是否有包含compat 指定的字符串,也就是檢查設(shè)備節(jié)點(diǎn)的兼容性,函數(shù)原型如下:

int of_device_is_compatible(const struct device_node *device, const char *compat)

函數(shù)參數(shù)和返回值含義如下:

  • device:設(shè)備節(jié)點(diǎn)。
  • compat:要查看的字符串。
  • 返回值:0,節(jié)點(diǎn)的compatible 屬性中不包含compat 指定的字符串;正數(shù),節(jié)點(diǎn)的compatible屬性中包含compat 指定的字符串。

2、of_get_address 函數(shù)

of_get_address 函數(shù)用于獲取地址相關(guān)屬性,主要是“reg”或者“assigned-addresses”屬性值,函數(shù)原型如下:

const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, unsigned int *flags)

函數(shù)參數(shù)和返回值含義如下:

  • dev:設(shè)備節(jié)點(diǎn)。
  • index:要讀取的地址標(biāo)號(hào)。
  • size:地址長(zhǎng)度。
  • flags:參數(shù),比如IORESOURCE_IO、IORESOURCE_MEM 等
  • 返回值:讀取到的地址數(shù)據(jù)首地址,為NULL 的話(huà)表示讀取失敗。

3、of_translate_address 函數(shù)

of_translate_address 函數(shù)負(fù)責(zé)將從設(shè)備樹(shù)讀取到的地址轉(zhuǎn)換為物理地址,函數(shù)原型如下:

u64 of_translate_address(struct device_node *dev, const __be32 *in_addr)

函數(shù)參數(shù)和返回值含義如下:

  • dev:設(shè)備節(jié)點(diǎn)。
  • in_addr:要轉(zhuǎn)換的地址。
  • 返回值:得到的物理地址,如果為OF_BAD_ADDR 的話(huà)表示轉(zhuǎn)換失敗。

4、of_address_to_resource 函數(shù)

IIC、SPI、GPIO 等這些外設(shè)都有對(duì)應(yīng)的寄存器,這些寄存器其實(shí)就是一組內(nèi)存空間,Linux內(nèi)核使用resource 結(jié)構(gòu)體來(lái)描述一段內(nèi)存空間,“resource”翻譯出來(lái)就是“資源”,因此用resource結(jié)構(gòu)體描述的都是設(shè)備資源信息,resource 結(jié)構(gòu)體定義在文件include/linux/ioport.h 中,定義如下:

18 struct resource { 19 resource_size_t start; 20 resource_size_t end; 21 const char *name; 22 unsigned long flags; 23 struct resource *parent, *sibling, *child; 24 };

對(duì)于32 位的SOC 來(lái)說(shuō),resource_size_t 是u32 類(lèi)型的。其中start 表示開(kāi)始地址,end 表示結(jié)束地址,name 是這個(gè)資源的名字,flags 是資源標(biāo)志位,一般表示資源類(lèi)型,可選的資源標(biāo)志定義在文件include/linux/ioport.h 中,如下所示:

1 #define IORESOURCE_BITS 0x000000ff 2 #define IORESOURCE_TYPE_BITS 0x00001f00 3 #define IORESOURCE_IO 0x00000100 4 #define IORESOURCE_MEM 0x00000200 5 #define IORESOURCE_REG 0x00000300 6 #define IORESOURCE_IRQ 0x00000400 7 #define IORESOURCE_DMA 0x00000800 8 #define IORESOURCE_BUS 0x00001000 9 #define IORESOURCE_PREFETCH 0x00002000 10 #define IORESOURCE_READONLY 0x00004000 11 #define IORESOURCE_CACHEABLE 0x00008000 12 #define IORESOURCE_RANGELENGTH 0x00010000 13 #define IORESOURCE_SHADOWABLE 0x00020000 14 #define IORESOURCE_SIZEALIGN 0x00040000 15 #define IORESOURCE_STARTALIGN 0x00080000 16 #define IORESOURCE_MEM_64 0x00100000 17 #define IORESOURCE_WINDOW 0x00200000 18 #define IORESOURCE_MUXED 0x00400000 19 #define IORESOURCE_EXCLUSIVE 0x08000000 20 #define IORESOURCE_DISABLED 0x10000000 21 #define IORESOURCE_UNSET 0x20000000 22 #define IORESOURCE_AUTO 0x40000000 23 #define IORESOURCE_BUSY 0x80000000

大家一般最常見(jiàn)的資源標(biāo)志就是IORESOURCE_MEM 、IORESOURCE_REG 和IORESOURCE_IRQ 等。接下來(lái)我們回到of_address_to_resource 函數(shù),此函數(shù)看名字像是從設(shè)備樹(shù)里面提取資源值,但是本質(zhì)上就是將reg 屬性值,然后將其轉(zhuǎn)換為resource 結(jié)構(gòu)體類(lèi)型,
函數(shù)原型如下所示

int of_address_to_resource(struct device_node *dev, int index, struct resource *r)

函數(shù)參數(shù)和返回值含義如下:

  • dev:設(shè)備節(jié)點(diǎn)。
  • index:地址資源標(biāo)號(hào)。
  • r:得到的resource 類(lèi)型的資源值。
  • 返回值:0,成功;負(fù)值,失敗。

5、of_iomap 函數(shù)

of_iomap 函數(shù)用于直接內(nèi)存映射,以前我們會(huì)通過(guò)ioremap 函數(shù)來(lái)完成物理地址到虛擬地址的映射,采用設(shè)備樹(shù)以后就可以直接通過(guò)of_iomap 函數(shù)來(lái)獲取內(nèi)存地址所對(duì)應(yīng)的虛擬地址,不需要使用ioremap 函數(shù)了。當(dāng)然了,你也可以使用ioremap 函數(shù)來(lái)完成物理地址到虛擬地址的內(nèi)存映射,只是在采用設(shè)備樹(shù)以后,大部分的驅(qū)動(dòng)都使用of_iomap 函數(shù)了。of_iomap 函數(shù)本
質(zhì)上也是將reg 屬性中地址信息轉(zhuǎn)換為虛擬地址,如果reg 屬性有多段的話(huà),可以通過(guò)index 參數(shù)指定要完成內(nèi)存映射的是哪一段,of_iomap 函數(shù)原型如下:

void __iomem *of_iomap(struct device_node *np, int index)

函數(shù)參數(shù)和返回值含義如下:

  • np:設(shè)備節(jié)點(diǎn)。
  • index:reg 屬性中要完成內(nèi)存映射的段,如果reg 屬性只有一段的話(huà)index 就設(shè)置為0。
  • 返回值:經(jīng)過(guò)內(nèi)存映射后的虛擬內(nèi)存首地址,如果為NULL 的話(huà)表示內(nèi)存映射失敗。

OF函數(shù)使用案例


在驅(qū)動(dòng)入口函數(shù)里面填寫(xiě)編寫(xiě)代碼獲取設(shè)備屬性(多去內(nèi)核里看看別人是怎么寫(xiě)的):


關(guān)于設(shè)備樹(shù)常用的OF 函數(shù)就先講解到這里,Linux 內(nèi)核中關(guān)于設(shè)備樹(shù)的OF 函數(shù)不僅僅只有前面講的這幾個(gè),還有很多OF 函數(shù)我們并沒(méi)有講解,這些沒(méi)有講解的OF 函數(shù)要結(jié)合具體的驅(qū)動(dòng),比如獲取中斷號(hào)的OF 函數(shù)、獲取GPIO 的OF 函數(shù)等等,這些OF 函數(shù)我們?cè)诤竺娴尿?qū)動(dòng)實(shí)驗(yàn)中再詳細(xì)的講解。

總結(jié)

關(guān)于設(shè)備樹(shù)就講解到這里,關(guān)于設(shè)備樹(shù)我們重點(diǎn)要了解一下幾點(diǎn)內(nèi)容:
①、DTS、DTB 和DTC 之間的區(qū)別,如何將.dts 文件編譯為.dtb 文件。
②、設(shè)備樹(shù)語(yǔ)法,這個(gè)是重點(diǎn),因?yàn)樵趯?shí)際工作中我們是需要修改設(shè)備樹(shù)的。
③、設(shè)備樹(shù)的幾個(gè)特殊子節(jié)點(diǎn)。
④、關(guān)于設(shè)備樹(shù)的OF 操作函數(shù),也是重點(diǎn),因?yàn)樵O(shè)備樹(shù)最終是被驅(qū)動(dòng)文件所使用的,而驅(qū)動(dòng)文件必須要讀取設(shè)備樹(shù)中的屬性信息,比如內(nèi)存信息、GPIO 信息、中斷信息等等。要想在驅(qū)動(dòng)中讀取設(shè)備樹(shù)的屬性值,那么就必須使用Linux 內(nèi)核提供的眾多的OF 函數(shù)。

從下一章開(kāi)始所以的Linux 驅(qū)動(dòng)實(shí)驗(yàn)都將采用設(shè)備樹(shù),從最基本的點(diǎn)燈,到復(fù)雜的音頻、網(wǎng)絡(luò)或塊設(shè)備等驅(qū)動(dòng)。將會(huì)帶領(lǐng)大家由簡(jiǎn)入深,深度剖析設(shè)備樹(shù),最終掌握基于設(shè)備樹(shù)的驅(qū)動(dòng)開(kāi)發(fā)技能。

總結(jié)

以上是生活随笔為你收集整理的Linux设备树的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。