日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

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

linux

Linux设备树

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

目錄

  • 什么是設(shè)備樹
  • DTS、DTB 和DTC
  • DTS語法
    • .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è)備怎么匹配到對應(yīng)的內(nèi)核驅(qū)動?了解)
    • 向設(shè)備節(jié)點(diǎn)添加或修改內(nèi)容(不要直接添加在.dtsi頭文件,通過&在各自的.dts里添加)
  • 創(chuàng)建小型模板設(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è)備樹常用OF操作函數(shù)(獲取reg'寄存器地址'屬性,操作外設(shè))
    • 查找節(jié)點(diǎn)的OF函數(shù)
    • 查找父/子節(jié)點(diǎn)的OF函數(shù)
    • 提取屬性值的OF函數(shù)
    • 其他常用的OF函數(shù)
    • OF函數(shù)使用案例
  • 總結(jié)

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

什么是設(shè)備樹

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

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

在沒有設(shè)備樹的時(shí)候Linux 是如何描述ARM 架構(gòu)中的板級信息呢?在Linux 內(nèi)核源碼中大量的arch/arm/mach-xxx 和arch/arm/plat-xxx 文件夾,這些文件夾里面的文件就是對應(yīng)平臺下的板級信息。比如在三星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è)開發(fā)板上的LCD 信息的,結(jié)構(gòu)體指針數(shù)組smdk2440_devices 描述的SMDK2440 這個(gè)開發(fā)板上的所有平臺相關(guān)信息。這個(gè)僅僅是使用2440 這個(gè)芯片的SMDK2440 開發(fā)板下的LCD 信息,SMDK2440 開發(fā)板還有很多的其他外設(shè)硬件和平臺硬件信息。隨著智能手機(jī)的發(fā)展,每年新出的ARM 架構(gòu)芯片至少都在數(shù)十、數(shù)百款,這些板級信息文件都是.c 或.h 文件,都會被硬編碼進(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è)備樹(Flattened Device Tree),將這些描述板級硬件信息的內(nèi)容都從Linux 內(nèi)中分離開來,放到一個(gè)單獨(dú)的文件夾內(nèi),這就叫設(shè)備樹,文件擴(kuò)展名為.dts。

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

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

DTS、DTB 和DTC

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

  • DTS 是設(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 工具依賴于dtc.c、flattree.c、fstree.c 等文件,最終編譯并鏈接出DTC 這個(gè)主機(jī)文件。如果要編譯DTS 文件的話只需要進(jìn)入到Linux 源碼根目錄下,然后執(zhí)行如下命令:

make all

或者:

make dtbs

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

基于ARM 架構(gòu)的SOC 有很多種,一種SOC 又可以制作出很多款板子,每個(gè)板子都有一個(gè)對應(yīng)的DTS 文件,那么如何確定編譯哪一個(gè)DTS 文件呢?我們就以I.MX6ULL 這款芯片對應(yīng)的板子為例來看一下,打開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 的板子對應(yīng)的.dts 文件都會被編譯為.dtb。如果我們使用I.MX6ULL 新做了一個(gè)板子,只需要新建一個(gè)此板子對應(yīng)的.dts 文件,然后將對應(yīng)的.dtb 文件名添加到dtb-$(CONFIG_SOC_IMX6ULL)下,這樣在編譯設(shè)備樹的時(shí)候就會將對應(yīng)的.dts 編譯為二進(jìn)制的.dtb文件。

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

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

DTS語法

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

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

.dtsi 頭文件

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

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

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

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

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

可以看出,示例代碼43.3.1.2 中直接引用了.dts 文件,因此在.dts> 設(shè)備樹文件中,可以通過“#include”來引用.h、.dtsi 和.dts 文件。只是,我們在編寫設(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è)備樹是采用樹形結(jié)構(gòu)來描述板子上的設(shè)備信息的文件,每個(gè)設(shè)備都是一個(gè)節(jié)點(diǎn),叫做設(shè)備節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)都通過一些屬性信息來描述節(jié)點(diǎn)信息。以下是從imx6ull.dtsi 文件中縮減出來的設(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è)備樹文件只有一個(gè)根節(jié)點(diǎn)。細(xì)心的同學(xué)應(yīng)該會發(fā)現(xiàn),imx6ull.dtsi和imx6ull-alientek-emmc.dts 這兩個(gè)文件都有一個(gè)“/”根節(jié)點(diǎn),這樣不會出錯嗎?不會的,因?yàn)檫@兩個(gè)“/”根節(jié)點(diǎn)的內(nèi)容會合并成一個(gè)根節(jié)點(diǎn)。

第2、6 和17 行,aliases、cpus 和intc 是三個(gè)子節(jié)點(diǎn),在設(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ù)手冊上查),如果某個(gè)節(jié)點(diǎn)沒有地址或者寄存器的話“unit-address”可以不要,比如“cpu@0”、“interrupt-controller@00a01000”。

引入標(biāo)簽:
我們在示例代碼43.3.2.1 中我們看到的節(jié)點(diǎn)命名卻如下所示:

c cpu0:cpu@0

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

c label: node-name@unit-address

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

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

compatible = "arm,cortex-a7";

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

reg = <0>;

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

reg = <0 0x123456 100>;

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

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è)備需要的屬性不同,用戶可以自定義屬性,也可以使用標(biāo)準(zhǔn)屬性,Linux 下的很多外設(shè)驅(qū)動都會使用這些標(biāo)準(zhǔn)屬性,本節(jié)我們就來學(xué)習(xí)一下幾個(gè)常用的標(biāo)準(zhǔn)屬性。

1、compatible 屬性

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

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

比如imx6ull-alientek-emmc.dts 中sound 節(jié)點(diǎn)是I.MX6U-ALPHA 開發(fā)板的音頻設(shè)備節(jié)點(diǎn),I.MX6U-ALPHA 開發(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”表示廠商是飛思卡爾,“imx6ul-evk-wm8960”和“imx-audio-wm8960”表示驅(qū)動模塊名字。sound這個(gè)設(shè)備首先使用第一個(gè)兼容值在Linux 內(nèi)核里面查找,看看能不能找到與之匹配的驅(qū)動文件,如果沒有找到的話就使用第二個(gè)兼容值查。

一般驅(qū)動程序文件都會有一個(gè)OF 匹配表,此OF 匹配表保存著一些compatible 值,如果設(shè)備節(jié)點(diǎn)的compatible 屬性值和OF 匹配表中的任何一個(gè)值相等,那么就表示設(shè)備可以使用這個(gè)驅(qū)動。比如在文件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ū)動文件的匹配表,此匹配表只有一個(gè)匹配值“fsl,imx-audio-wm8960”。如果在設(shè)備樹中有哪個(gè)節(jié)點(diǎn)的compatible 屬 性值與此相等,那么這個(gè)節(jié)點(diǎn)就會使用此驅(qū)動文件。

第642 行,wm8960 采用了platform_driver 驅(qū)動模式,關(guān)于platform_driver 驅(qū)動后面會講解。此行設(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)前是不可操作的,但是在未來可以變?yōu)榭刹僮鞯?#xff0c;比如熱插拔設(shè)備插入以后。至于disabled 的具體含義還要看設(shè)備的綁定文檔。
“fail”表明設(shè)備不可操作,設(shè)備檢測到了一系列的錯誤,而且設(shè)備也不大可能變得可操作。
“fail-sss”含義和“fail”相同,后面的sss 部分是檢測到的錯誤內(nèi)容。

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

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

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

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

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

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

  • address 是起始地址,length 是地址長度
  • #address-cells 表明address 這個(gè)數(shù)據(jù)所占用的字長,#size-cells 表明length 這個(gè)數(shù)據(jù)所占用的字長,比如下面的兩種情況:
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>,說明spi4 的子節(jié)點(diǎn)reg 屬性中起始地址所占用的字長為1,地址長度所占用的字長為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,沒有l(wèi)ength 的值,相當(dāng)于設(shè)置了起始地址,而沒有設(shè)置地址長度。


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

5、reg 屬性

reg 屬性前面已經(jīng)提到過了,reg 屬性的值一般是(address,length)對。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 參考手冊》可知,I.MX6ULL 的UART1 寄存器首地址為0x02020000,但是UART1 的地址長度(范圍)并沒有0x4000 這么多,這里我們重點(diǎn)是獲取UART1 寄存器首地址。

6、ranges 屬性

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

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

如果ranges 屬性值為空值,說明子地址空間和父地址空間完全相同,不需要進(jìn)行地址轉(zhuǎn)換,對于我們所使用的I.MX6ULL 來說,子地址空間和父地址空間完全相同,因此會在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,寄存器長度為0x100。經(jīng)過地址轉(zhuǎn)換,serial 設(shè)備可以從0xe0004600 開始進(jìn)行讀寫操作,0xe0004600=0x4600+0xe0000000。

7、name 屬性(棄用)

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

8、device_type 屬性(棄用)

device_type 屬性值為字符串,IEEE 1275 會用到此屬性,用于描述設(shè)備的FCode,但是設(shè)備樹沒有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è)備怎么匹配到對應(yīng)的內(nèi)核驅(qū)動?了解)

每個(gè)節(jié)點(diǎn)都有compatible 屬性,根節(jié)點(diǎn)“/”也不例外,imx6ull-alientek-emmc.dts 文件中根節(jié)點(diǎn)的compatible 屬性內(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ū)動程序
  • 根節(jié)點(diǎn)的compatible 屬性可以知道我們所使用的設(shè)備,一般第一個(gè)值描述了所使用的硬件設(shè)備名字,比如這里使用的是“imx6ull-14x14-evk”這個(gè)設(shè)備,第二個(gè)值描述了設(shè)備所使用的SOC,比如這里使用的是“imx6ull”這顆SOC。

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

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

在沒有使用設(shè)備樹以前,uboot 會向Linux 內(nèi)核傳遞一個(gè)叫做machine id(設(shè)備ID)的值,告訴Linux 內(nèi)核自己是個(gè)什么設(shè)備,看看Linux 內(nèi)核是否支持。Linux 內(nèi)核是支持很多設(shè)備的,針對每一個(gè)設(shè)備(板子),Linux內(nèi)核都用MACHINE_START 和MACHINE_END來定義一個(gè)machine_desc 結(jié)構(gòu)體來描述這個(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 展開后如下所示:

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 類型的結(jié)構(gòu)體變量__mach_desc_MX35_3DS ,這個(gè)變量存儲在“.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。
前面說了,uboot 會給Linux 內(nèi)核傳遞machine id 這個(gè)參數(shù),Linux 內(nèi)核將machine id 與這些MACH_TYPE_XXX 宏進(jìn)行對比,如果相等的話就表示Linux 內(nèi)核支持這個(gè)設(shè)備,如果不支持的話那么這個(gè)設(shè)備就沒法啟動Linux 內(nèi)核。

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

當(dāng)Linux 內(nèi)核引入設(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,說明引入設(shè)備樹以后不再根據(jù)machine id 來檢查Linux內(nèi)核是否支持某個(gè)設(shè)備。
打開文件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 開發(fā)板可以正常啟動Linux 內(nèi)核。如果將imx6ull-alientek-emmc.dts 根節(jié)點(diǎn)的compatible 屬性改為其他的值,比如:

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

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

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

接下來我們簡單看一下Linux 內(nèi)核是如何根據(jù)設(shè)備樹根節(jié)點(diǎn)的compatible 屬性來匹配出對應(yīng)的machine_desc,Linux 內(nèi)核調(diào)用start_kernel 函數(shù)來啟動內(nèi)核,start_kernel 函數(shù)會調(diào)用setup_arch 函數(shù)來匹配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ù)來獲取匹配的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 來獲取匹配的machine_desc,參數(shù)mdesc_best是默認(rèn)的machine_desc ,參數(shù)arch_get_next_mach 是個(gè)函數(shù),此函數(shù)定義在定義在
arch/arm/kernel/devtree.c 文件中。找到匹配的machine_desc 的過程就是用設(shè)備樹根節(jié)點(diǎn)的compatible 屬性值和Linux 內(nèi)核中machine_desc 下.dt_compat 的值比較,看看那個(gè)相等,如果相等的話就表示找到匹配的machine_desc,arch_get_next_mach 函數(shù)的工作就是獲取Linux 內(nèi)核中
下一個(gè)machine_desc 結(jié)構(gòu)體。

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

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

產(chǎn)品開發(fā)過程中可能面臨著頻繁的需求更改,比如第一版硬件上有一個(gè)IIC 接口的六軸芯片MPU6050,第二版硬件又要把這個(gè)IIC六軸芯片MPU6050 更換為MPU9250 等。一旦硬件修改了,我們就要同步的修改設(shè)備樹文件,畢竟設(shè)備樹是描述板子硬件信息的文件。假設(shè)現(xiàn)在有個(gè)六軸芯片fxls8471,fxls8471 要接到I.MX6U-ALPHA 開發(fā)板的I2C1 接口上,那么相當(dāng)于需要在i2c1 這個(gè)節(jié)點(diǎn)上添加一個(gè)fxls8471 子節(jié)點(diǎn)。先看一下I2C1 接口對應(yīng)的節(jié)點(diǎn),打開文件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,最簡單的方法就是在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è)芯片對應(yīng)的子節(jié)點(diǎn)。但是這樣會有個(gè)問題!i2c1 節(jié)點(diǎn)是定義在imx6ull.dtsi 文件中的,而imx6ull.dtsi 是設(shè)備樹頭文件,其他所有使用到I.MX6ULL這顆SOC 的板子都會引用imx6ull.dtsi 這個(gè)文件。直接在i2c1 節(jié)點(diǎn)中添加fxls8471 就相當(dāng)于在其他的所有板子上都添加了fxls8471 這個(gè)設(shè)備,但是其他的板子并沒有這個(gè)設(shè)備啊!因此,按照示例代碼43.3.5.2 這樣寫肯定是不行的。

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

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

第1 行,&i2c1 表示要訪問i2c1 這個(gè)label 所對應(yīng)的節(jié)點(diǎn),也就是imx6ull.dtsi 中的“i2c1: i2c@021a0000”。
第2 行,花括號內(nèi)就是要向i2c1 這個(gè)節(jié)點(diǎn)添加的內(nè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?!癱lock-frequency”就是新添加的屬性。
第228 行,將status 屬性的值由原來的disabled 改為okay。
第230~234 行,i2c1 子節(jié)點(diǎn)mag3110,因?yàn)镹XP 官方開發(fā)板在I2C1 上接了一個(gè)磁力計(jì)芯片mag3110,正點(diǎn)原子的I.MX6U-ALPHA 開發(fā)板并沒有使用mag3110。
第236~242 行,i2c1 子節(jié)點(diǎn)fxls8471,同樣是因?yàn)镹XP 官方開發(fā)板在I2C1 上接了fxls8471這顆六軸芯片。
因?yàn)槭纠a43.3.5.4 中的內(nèi)容是imx6ull-alientek-emmc.dts 這個(gè)文件內(nèi)的,所以不會對使用I.MX6ULL 這顆SOC 的其他板子造成任何影響。這個(gè)就是向節(jié)點(diǎn)追加或修改內(nèi)容,重點(diǎn)就是通過&label 來訪問節(jié)點(diǎn),然后直接在里面編寫要追加或者修改的內(nèi)容。

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

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

為了簡單起見,我們就在設(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è)備樹框架很簡單,就一個(gè)根節(jié)點(diǎn)“/”,根節(jié)點(diǎn)里面只有一個(gè)compatible 屬性。我們就在這個(gè)基礎(chǔ)框架上面將上面列出的內(nèi)容一點(diǎn)點(diǎn)添加進(jìn)來。

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è),因此一般會創(chuàng)建一個(gè)叫做soc 的父節(jié)點(diǎn)來管理這些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è)字長,地址空間長度也占用一個(gè)字長。
第21 行,ranges 屬性,ranges 屬性為空,說明子空間和父空間地址范圍相同。

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è)備樹中添加這三個(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)

最后我們在myfirst.dts 文件中加入ecspi1,usbotg1 和rngb 這三個(gè)外設(shè)控制器對應(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è)備樹就編寫好了,基本和imx6ull.dtsi 很像,可以看做是imx6ull.dtsi 的縮小版。在myfirst.dts 里面我們僅僅是編寫了I.MX6ULL 的外設(shè)控制器節(jié)點(diǎn),像IIC 接口,SPI 接口下所連接的具體設(shè)備我們并沒有寫,因?yàn)榫唧w的設(shè)備其設(shè)備樹屬性內(nèi)容不同,這個(gè)等到具體的實(shí)驗(yàn)在詳細(xì)講解。

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

Linux 內(nèi)核啟動的時(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),我們依次來看一下這些屬性和子節(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è)文件,它們在設(shè)備樹中就是根節(jié)點(diǎn)的5 個(gè)屬性。既然是文件那么肯定可以查看其內(nèi)容,輸入cat 命令來查看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”。打開文件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è)備樹在根文件系統(tǒng)中的體現(xiàn),同樣是按照樹形結(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,我們接下來看一下這兩個(gè)特殊的子節(jié)點(diǎn)。

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

打開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)的主要功能就是定義別名,定義別名的目的就是為了方便訪問節(jié)點(diǎn)。不過我們一般會在節(jié)點(diǎn)命名的時(shí)候會加上label,然后通過&label來訪問節(jié)點(diǎn),這樣也很方便,而且設(shè)備樹里面大量的使用&label 的形式來訪問節(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 目錄里面,會發(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è)不就是我們在uboot 中設(shè)置的bootargs 環(huán)境變量的值嗎?現(xiàn)在有兩個(gè)疑點(diǎn):
①、我們并沒有在設(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í)候說過,uboot 在啟動Linux 內(nèi)核的時(shí)候會將bootargs 的值傳遞給Linux內(nèi)核,bootargs 會作為Linux 內(nèi)核的命令行參數(shù),Linux 內(nèi)核啟動的時(shí)候會打印出命令行參數(shù)(也就是uboot 傳遞進(jìn)來的bootargs 的值),如圖43.6.2.3 所示:

既然chosen 節(jié)點(diǎn)的bootargs 屬性不是我們在設(shè)備樹里面設(shè)置的,那么只有一種可能,那就是uboot 自己在chosen 節(jié)點(diǎn)里面添加了bootargs 屬性,并且設(shè)置bootargs 屬性的值為bootargs環(huán)境變量的值。因?yàn)?strong>在啟動Linux 內(nèi)核之前,只有uboot 知道bootargs 環(huán)境變量的值,并且uboot也知道.dtb 設(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è)備樹(.dtb)中找到chosen 節(jié)點(diǎn),如果沒有找到的話就會自己創(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è)備樹的chosen 節(jié)點(diǎn)中加入了bootargs屬性,并且還設(shè)置了bootargs 屬性值。接下來我們順著fdt_chosen 函數(shù)一點(diǎn)點(diǎn)的抽絲剝繭,看看都有哪些函數(shù)調(diào)用了fdt_chosen,一直找到最終的源頭。最終發(fā)現(xiàn)整個(gè)流程見圖43.6.2.4:

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

bootz 8080000083000000

當(dāng)我們輸入上述命令并執(zhí)行以后,do_bootz 函數(shù)就會執(zhí)行,然后一切就按照圖43.6.2.4 中所示的流程開始運(yùn)行。

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

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

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

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

設(shè)備樹是用來描述板子上的設(shè)備信息的,不同的設(shè)備其信息不同,反映到設(shè)備樹中就是屬性不同。那么我們在設(shè)備樹中添加一個(gè)硬件對應(yīng)的節(jié)點(diǎn)的時(shí)候從哪里查閱相關(guān)的說明呢?在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è)備樹中添加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 目錄下找不到對應(yīng)的文檔,這個(gè)時(shí)候就要咨詢芯片的提供商,讓他們給你提供參考的設(shè)備樹文件。

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

設(shè)備樹描述了設(shè)備的詳細(xì)信息,這些信息包括數(shù)字類型的、字符串類型的、數(shù)組類型的,我們在編寫驅(qū)動的時(shí)候需要獲取到這些信息。比如設(shè)備樹使用reg 屬性描述了某個(gè)外設(shè)的寄存器地址為0X02005482,長度為0X400,我們在編寫驅(qū)動的時(shí)候需要獲取到reg屬性的0X02005482 和0X400 這兩個(gè)值,然后初始化外設(shè)。Linux 內(nè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è)備樹上的,因此要想獲取這個(gè)設(shè)備的其他屬性信息,必須先獲取到這個(gè)設(shè)備的節(jié)點(diǎn)。Linux 內(nèi)核使用device_node 結(jié)構(gòu)體來描述一個(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è)備類型*/ 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è),我們依次來看一下。

1、of_find_node_by_name 函數(shù)

of_find_node_by_name 函數(shù)通過節(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:開始查找的節(jié)點(diǎn),如果為NULL 表示從根節(jié)點(diǎn)開始查找整個(gè)設(shè)備樹(像鏈表)。
  • name:要查找的節(jié)點(diǎn)名字。
  • 返回值:找到的節(jié)點(diǎn),如果為NULL 表示查找失敗。

2、of_find_node_by_type 函數(shù)

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

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

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

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

4、of_find_matching_node_and_match 函數(shù)

of_find_matching_node_and_match 函數(shù)通過of_device_id 匹配表來查找指定的節(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:開始查找的節(jié)點(diǎn),如果為NULL 表示從根節(jié)點(diǎn)開始查找整個(gè)設(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ù)通過路徑來查找指定的節(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)對應(yīng)的父節(jié)點(diǎn)或子節(jié)點(diǎn)的OF 函數(shù),我們依次來看一下。

1、of_get_parent 函數(shù)

of_get_parent 函數(shù)用于獲取指定節(jié)點(diǎn)的父節(jié)點(diǎn)(如果有父節(jié)點(diǎn)的話),函數(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)開始迭代的查找下一個(gè)子節(jié)點(diǎn)。可以設(shè)置為NULL,表示從第一個(gè)子節(jié)點(diǎn)開始。
  • 返回值:找到的下一個(gè)子節(jié)點(diǎn)。

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

節(jié)點(diǎn)的屬性信息里面保存了驅(qū)動所需要的內(nèi)容,因此對于屬性值的提取非常重要,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; /* 屬性長度*/ 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ù),我們依次來看一下。

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:元素長度。
  • 返回值:得到的屬性元素?cái)?shù)量。

3、of_property_read_u32_index 函數(shù)

of_property_read_u32_index 函數(shù)用于從屬性中獲取指定標(biāo)號的u32 類型數(shù)據(jù)值(無符號32位),比如某個(gè)屬性有多個(gè)u32 類型的值,那么就可以使用此函數(shù)來獲取指定標(biā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)號。
  • out_value:讀取到的值
  • 返回值:0 讀取成功,負(fù)值,讀取失敗,-EINVAL 表示屬性不存在,-ENODATA 表示沒有要讀取的數(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 類型的數(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 表示沒有要讀取的數(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 類型屬性值,函數(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 表示沒有要讀取的數(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)號。
  • size:地址長度。
  • flags:參數(shù),比如IORESOURCE_IO、IORESOURCE_MEM 等
  • 返回值:讀取到的地址數(shù)據(jù)首地址,為NULL 的話表示讀取失敗。

3、of_translate_address 函數(shù)

of_translate_address 函數(shù)負(fù)責(zé)將從設(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 的話表示轉(zhuǎn)換失敗。

4、of_address_to_resource 函數(shù)

IIC、SPI、GPIO 等這些外設(shè)都有對應(yīng)的寄存器,這些寄存器其實(shí)就是一組內(nèi)存空間,Linux內(nèi)核使用resource 結(jié)構(gòu)體來描述一段內(nèi)存空間,“resource”翻譯出來就是“資源”,因此用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 };

對于32 位的SOC 來說,resource_size_t 是u32 類型的。其中start 表示開始地址,end 表示結(jié)束地址,name 是這個(gè)資源的名字,flags 是資源標(biāo)志位,一般表示資源類型,可選的資源標(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

大家一般最常見的資源標(biāo)志就是IORESOURCE_MEM 、IORESOURCE_REG 和IORESOURCE_IRQ 等。接下來我們回到of_address_to_resource 函數(shù),此函數(shù)看名字像是從設(shè)備樹里面提取資源值,但是本質(zhì)上就是將reg 屬性值,然后將其轉(zhuǎn)換為resource 結(jié)構(gòu)體類型,
函數(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)號。
  • r:得到的resource 類型的資源值。
  • 返回值:0,成功;負(fù)值,失敗。

5、of_iomap 函數(shù)

of_iomap 函數(shù)用于直接內(nèi)存映射,以前我們會通過ioremap 函數(shù)來完成物理地址到虛擬地址的映射,采用設(shè)備樹以后就可以直接通過of_iomap 函數(shù)來獲取內(nèi)存地址所對應(yīng)的虛擬地址,不需要使用ioremap 函數(shù)了。當(dāng)然了,你也可以使用ioremap 函數(shù)來完成物理地址到虛擬地址的內(nèi)存映射,只是在采用設(shè)備樹以后,大部分的驅(qū)動都使用of_iomap 函數(shù)了。of_iomap 函數(shù)本
質(zhì)上也是將reg 屬性中地址信息轉(zhuǎn)換為虛擬地址,如果reg 屬性有多段的話,可以通過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 屬性只有一段的話index 就設(shè)置為0。
  • 返回值:經(jīng)過內(nèi)存映射后的虛擬內(nèi)存首地址,如果為NULL 的話表示內(nèi)存映射失敗。

OF函數(shù)使用案例


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


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

總結(jié)

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

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

總結(jié)

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

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

成人黄大片视频在线观看 | 五月婷婷深开心 | 缴情综合网五月天 | 欧洲一区二区在线观看 | 欧洲成人av | 天天拍夜夜拍 | 麻豆视频在线看 | 久久精品一| 日韩欧美精品在线观看 | 亚洲精品中文在线资源 | 91重口视频 | 伊人久久在线观看 | 久久激情五月丁香伊人 | 国产69精品久久久久99尤 | 久久免费精品一区二区三区 | 天天干天天摸 | 91九色精品| 最新国产精品久久精品 | 日韩免费电影网站 | 中文字幕在线观看免费高清电影 | 精品一区二区三区香蕉蜜桃 | 中文字幕色站 | 一区中文字幕在线观看 | 欧美日韩视频在线观看免费 | www日日夜夜 | 五月天狠狠操 | 97色涩 | 成人午夜精品福利免费 | 麻豆成人在线观看 | 国产裸体视频网站 | 成人在线观看资源 | 狠狠躁夜夜躁人人爽超碰91 | 成人黄色视 | 国产日韩欧美在线观看 | 99久久这里有精品 | 欧美一级性视频 | 中文字幕乱码电影 | 国内精品福利视频 | 亚洲精品成人在线 | 亚洲激情小视频 | 中文字幕一区二区三区四区久久 | 99欧美精品 | 亚洲国产成人在线观看 | 日韩精品视频免费专区在线播放 | 亚洲一级电影 | 99久久精品久久亚洲精品 | 国产永久免费观看 | 国产一级视屏 | 中文在线字幕观看电影 | 久久精品国产v日韩v亚洲 | 免费在线观看av的网站 | 视频在线观看国产 | 婷婷五天天在线视频 | 欧美电影黄色 | 免费看的黄色 | 国产精品免费一区二区三区在线观看 | 99视频在线精品国自产拍免费观看 | 亚洲一区二区三区毛片 | 欧美一区二区精美视频 | 在线观看国产www | 99久久久国产精品美女 | 亚洲婷婷丁香 | 免费看的黄网站软件 | 4438全国亚洲精品观看视频 | 激情自拍av | 日韩精品一区二区三区视频播放 | 91成人短视频在线观看 | 日韩艹 | 天天综合天天综合 | 亚洲精品麻豆视频 | 日本中文字幕视频 | adc在线观看 | 天天摸夜夜操 | 伊人中文网 | 97在线精品国自产拍中文 | 色先锋av资源中文字幕 | 在线免费观看黄色 | 久久久91精品国产一区二区精品 | 色婷婷综合视频在线观看 | 久久久精品免费看 | 插久久 | 成人h电影 | 日韩有色 | 国产精品精品国产 | 黄污视频网站大全 | 岛国精品一区二区 | 亚洲黄色免费观看 | 国产美女被啪进深处喷白浆视频 | 久草在线视频中文 | 九九热只有精品 | 精品九九九九 | 亚州精品一二三区 | 成片人卡1卡2卡3手机免费看 | 中文字幕在线观看视频一区二区三区 | 成年人在线电影 | 久久久久久久久毛片精品 | 天天摸夜夜操 | 精品国产免费一区二区三区五区 | 精品免费视频123区 午夜久久成人 | 天天操夜夜拍 | 日日综合网 | 免费合欢视频成人app | 韩国三级一区 | 久久最新网址 | 中文字幕二区三区 | 亚洲精品一区二区网址 | 99久久精品国产欧美主题曲 | 国产成人一区二区三区久久精品 | 欧美日韩视频在线一区 | 91av在线播放视频 | 最近中文字幕高清字幕免费mv | 友田真希x88av| 99九九热只有国产精品 | 国产中文在线字幕 | 天天操天天射天天爽 | 狠狠干狠狠久久 | 99精品视频精品精品视频 | 国产精品免费大片视频 | av一区二区三区在线播放 | 久操中文字幕在线观看 | 808电影免费观看三年 | 久草在线视频免费资源观看 | 日韩视频在线一区 | 国产精品99久久免费观看 | 91桃色在线播放 | 久久久国产精品视频 | 天天天综合网 | 中文字幕在线观 | 一区在线观看视频 | 99免费在线观看 | 91香蕉国产在线观看软件 | 日韩城人在线 | av午夜电影 | 美女视频黄是免费的 | 日韩成人免费电影 | 在线观看成人 | 草久视频在线 | 成年人免费在线播放 | 九九九电影免费看 | 亚洲欧美色婷婷 | 国产精品免费观看久久 | 欧美日韩不卡一区二区三区 | 91桃色免费观看 | 久久夜靖品| 亚洲欧美成人在线 | 天天摸天天干天天操天天射 | 久久久免费av | 欧美日韩一区二区三区在线免费观看 | 国产美女网站视频 | 中文字幕视频在线播放 | 国产精品毛片一区二区 | 日韩欧美aaa | 日本在线观看中文字幕无线观看 | 干狠狠| 婷婷成人在线 | 国产青草视频在线观看 | 免费毛片一区二区三区久久久 | 九九热免费在线视频 | 国产裸体视频网站 | 成年人国产在线观看 | 一区二区三区不卡在线 | 天天在线操 | 中文字幕精品视频 | 亚洲精品国产精品国自产在线 | 99视频一区 | 久久91网| 91手机视频 | 欧美一级性生活片 | 五月婷婷综合在线视频 | 国产日产精品久久久久快鸭 | 免费一级片久久 | 丁香婷婷色综合亚洲电影 | 久久免费国产精品 | 激情综合色综合久久 | 8x成人免费视频 | 色婷婷精品大在线视频 | 国产精品乱码久久久 | 国产精品伦一区二区三区视频 | 亚洲国产精品视频在线观看 | 国产一级免费片 | 粉嫩av一区二区三区四区在线观看 | 国产高清区 | 成人中文字幕在线观看 | 六月色丁香 | 亚洲国产成人精品在线 | 国产亚洲情侣一区二区无 | 黄色小网站在线观看 | 国产欧美综合在线观看 | 久久久综合九色合综国产精品 | 99在线精品免费视频九九视 | 99精品免费 | 亚洲精品国产精品国自产观看 | 狠狠操综合网 | 天天色天天操天天爽 | 九九久久精品视频 | 成人国产一区二区 | 一区二区三区免费在线观看 | 久久免费视频一区 | 人人爽人人爽av | www.黄色小说.com | 天天爱天天操天天射 | 免费网站v| 中文字幕在线免费观看视频 | 日韩在线视频免费看 | 91桃色免费视频 | 日韩网站在线免费观看 | 国产色女 | 在线观看一区二区视频 | 久久露脸国产精品 | 日韩免费av片| 日韩欧美观看 | 精品a视频 | 中文字幕在线观看你懂的 | 91热视频 | 色a综合 | 一本一道久久a久久精品 | 日韩视频一二三区 | 日日夜夜网 | 午夜精品视频一区 | 久草在线资源免费 | 91看片在线播放 | 少妇bbbb搡bbbb桶 | 亚洲最大av在线播放 | av中文字幕免费在线观看 | 欧美激情xxxx | 91亚洲精品久久久久图片蜜桃 | 国产精品白虎 | 色av色av色av | 一级特黄aaa大片在线观看 | av在线免费观看网站 | 久久久午夜精品理论片中文字幕 | 国产精品 久久 | 91亚·色| 欧美亚洲精品一区 | 国产理伦在线 | 免费观看mv大片高清 | 国产aaa毛片 | 亚洲精品视频在线播放 | 99亚洲精品 | 男女精品久久 | 最近高清中文在线字幕在线观看 | 日韩天天干 | 日韩午夜在线 | 久久新视频 | 久久9999久久 | 香蕉色综合| 久久久精品久久日韩一区综合 | 99r在线精品 | av天天草| 精品福利在线视频 | www.久久久精品 | 久久国产欧美日韩精品 | 亚洲人在线7777777精品 | 97精品超碰一区二区三区 | 亚洲午夜在线视频 | 久久久精品国产一区二区三区 | 97久久久免费福利网址 | 欧美地下肉体性派对 | 欧美中文字幕久久 | 久久国产精品免费 | 天天综合天天做 | www.福利视频| 99re国产 | 精品视频www | 国产精品丝袜久久久久久久不卡 | 亚洲国产中文在线观看 | 国产一区视频免费在线观看 | 最近更新好看的中文字幕 | 日韩精品你懂的 | 国产精品九九九 | 日韩精品字幕 | 精品嫩模福利一区二区蜜臀 | 国产免费久久久久 | 久久成人国产精品入口 | 亚洲精品麻豆视频 | 国产精品视频久久久 | 99亚洲国产 | 五月天六月婷婷 | 国产专区视频在线 | 黄色免费电影网站 | 亚洲第一久久久 | 91精品一 | 91毛片视频| 日韩一区二区三区视频在线 | 久草资源在线 | 亚洲成人av电影在线 | 亚洲网站在线 | 日韩有码专区 | 亚洲黄色片| 在线免费亚洲 | 91色在线观看视频 | 国内精品久久久久国产 | 日韩中文字幕一区 | www.狠狠色.com | 91精品视频在线观看免费 | 天天躁日日躁狠狠躁av麻豆 | 国产精品国内免费一区二区三区 | 亚洲精品在线免费观看视频 | 亚洲欧洲视频 | 粉嫩一区二区三区粉嫩91 | 在线韩国电影免费观影完整版 | 亚洲精品xxxx | 亚洲 欧美日韩 国产 中文 | 中文免费在线观看 | 久久久久久久99精品免费观看 | 免费在线色 | 日韩在线免费电影 | 欧美日韩久久不卡 | 午夜av网站 | 欧美色噜噜噜 | 美女视频久久久 | 亚洲免费成人 | 免费在线国产精品 | 欧美色婷| 人人草在线视频 | 国产一区二区电影在线观看 | 国产91免费观看 | 国内一级片在线观看 | 国产午夜精品久久 | 狠狠干夜夜| 九九免费在线观看视频 | 中文成人字幕 | 精品久久久免费视频 | 99精品视频中文字幕 | 国产成人精品一区二 | 午夜色大片在线观看 | 天天综合网入口 | 黄色软件在线观看视频 | 国产精品久久久久久一二三四五 | 一区在线观看视频 | 亚洲精品中文在线 | 99热这里精品 | 国产精品av免费在线观看 | 在线播放 日韩专区 | 久久高清av| 在线观看日本高清mv视频 | 99精品视频免费看 | 91入口在线观看 | 午夜黄色影院 | 91香蕉嫩草| 色视频成人在线观看免 | 久久夜色精品国产欧美一区麻豆 | 精品人妖videos欧美人妖 | 91香蕉嫩草 | 91精品天码美女少妇 | 九九免费观看视频 | 中文字幕在线观看第二页 | 久久综合加勒比 | 国产免费小视频 | 亚洲理论片在线观看 | 香蕉网在线播放 | 一本到视频在线观看 | 91精品成人久久 | 国产成人av福利 | 亚洲精品久久久久久久不卡四虎 | 国产99久久| 在线 视频 一区二区 | 免费av网址在线观看 | 国产三级精品在线 | 国产精品一区二区久久久久 | 国产精品成人品 | 久久精品日本啪啪涩涩 | 欧美日韩不卡在线观看 | 日韩av手机在线观看 | 国产成人av电影在线观看 | 美女视频又黄又免费 | 日韩精品中文字幕有码 | 日韩国产精品久久久久久亚洲 | 99久久久久久久久 | 在线看91| 深夜免费小视频 | 91高清完整版在线观看 | 久久久免费少妇 | 精品久久一区二区三区 | 日韩欧美成 | 免费国产黄线在线观看视频 | 久久精品国产免费看久久精品 | 精品国产99国产精品 | 人人艹人人 | 日韩在线观看一区 | 五月婷婷一区二区三区 | 欧美一级爽| 亚洲国产精品久久久久婷婷884 | 日本久久91| 亚洲精品国产综合久久 | 久久99国产精品免费 | 综合激情久久 | av在线播放不卡 | 日韩一区视频在线 | 狠狠色伊人亚洲综合网站野外 | 日韩精品不卡在线观看 | 国产精品自产拍在线观看中文 | 成人h视频 | 91成人网页版| 一级久久精品 | 99热播精品| 色婷婷视频在线观看 | av免费高清观看 | 国产69久久精品成人看 | 99视频精品 | 久久精品国产亚洲 | 国产精品一区在线观看 | 国产精品第一视频 | 免费看黄的 | 午夜精品av | 久久久久久久久久久网 | 亚洲视频 在线观看 | 五月天高清欧美mv | 久久综合精品一区 | 色吊丝在线永久观看最新版本 | 国产丝袜制服在线 | 亚洲乱亚洲乱妇 | 亚洲欧美日韩精品久久奇米一区 | 免费看av在线 | 伊人婷婷久久 | 久久国产视屏 | 日本韩国欧美在线观看 | 99视频精品免费视频 | 高清有码中文字幕 | 婷婷色网视频在线播放 | 欧美调教网站 | av高清一区 | 日韩一二三 | 天天做天天干 | 九九在线高清精品视频 | 欧美ⅹxxxxxx | 国产精品久久免费看 | 欧美日韩国产综合网 | 日韩剧情 | 97视频在线看 | 久久国产精品影视 | 久久久高清 | 亚洲欧洲精品久久 | 免费91在线观看 | 国产精品18久久久久久久 | 国产精品一区二区三区在线看 | 国产不卡av在线播放 | 免费在线国产视频 | 99久久日韩精品免费热麻豆美女 | 欧美精品一级视频 | 97超碰在线免费观看 | 91爱在线| 久久好看| 久久精品视频观看 | 91亚洲精品久久久中文字幕 | 91热这里只有精品 | 国产高清无av久久 | 欧美日产一区 | 中文字幕精品一区二区精品 | 国产一在线精品一区在线观看 | av电影一区二区三区 | 99色在线观看 | 国产精品视频永久免费播放 | 免费观看不卡av | 久久综合九色99 | 手机在线日韩视频 | 国产精品99精品久久免费 | 91视视频在线直接观看在线看网页在线看 | 五月天综合激情 | 国产精品一区二区吃奶在线观看 | 久久激情视频 久久 | 西西44人体做爰大胆视频 | 欧美a√在线 | 亚洲精区二区三区四区麻豆 | 欧美一区二区日韩一区二区 | 日韩精品偷拍 | 九九综合久久 | 97视频网站 | 免费h视频 | 免费网站看av片 | 97国产情侣爱久久免费观看 | 久久视频6 | 日本爱爱免费视频 | 91网免费看 | 国产女v资源在线观看 | 亚洲精品乱码久久久久v最新版 | 国产精品一区二 | 日韩视频一区二区在线 | 日韩激情第一页 | 亚洲欧洲一区二区在线观看 | www免费黄色 | 国产成人精品一区二区在线 | 免费三级av| 99精品一区二区三区 | 久久久亚洲精品 | 国产亚洲精品久久19p | 亚洲欧美日韩国产一区二区三区 | 99在线观看视频 | 日韩精品一区二区在线 | 色婷婷丁香| 国产淫片 | 欧美一区二区三区免费看 | 久久精品国产免费观看 | 久久99久久精品 | 国产一区二区高清不卡 | 久久久久夜色 | 中文字幕 婷婷 | 日本精品一区二区三区在线观看 | 人人插人人射 | 免费视频网 | av免费电影网站 | 超碰97.com | 中文字幕在线看视频国产中文版 | 久久精品成人欧美大片古装 | 综合国产在线观看 | 国产成人在线免费观看 | 国产精品第72页 | 亚洲精品乱码久久久久久久久久 | 亚洲a在线观看 | av再线观看 | 久草在线手机视频 | 久久久影院一区二区三区 | 色婷婷在线视频 | 天天av天天 | 超碰在线9 | 久久久久亚洲精品 | 激情久久一区二区三区 | 国产一区欧美二区 | 在线观看一区二区视频 | 欧美三级免费 | 2000xxx影视| 免费在线一区二区 | 欧美a免费 | 国产成人三级在线 | 国产高清av免费在线观看 | 亚洲乱码久久 | 久久久久久蜜av免费网站 | 美女免费视频一区 | 日韩三级久久 | 日本精品一区二区三区在线播放视频 | 婷婷亚洲综合 | 天天插综合 | 97av视频在线观看 | 日韩精品不卡在线观看 | 欧美亚洲xxx | 久久久久 免费视频 | 在线午夜 | 99久久网站| 91精品久久久久久粉嫩 | 91色在线观看视频 | 99爱国产精品 | 超级碰碰免费视频 | 精品久久一区二区 | 色综合久久久久久中文网 | 国产精品一区二区三区99 | 九九99| 国内精品久久久久久久影视简单 | 亚洲精品国偷拍自产在线观看 | 日韩精品久久中文字幕 | 一区二区伦理电影 | 国产伦理一区二区三区 | 97视频在线免费观看 | 成人免费中文字幕 | 综合色中色 | 亚一亚二国产专区 | 中文字幕999 | 在线免费91 | 在线精品亚洲一区二区 | 日韩视频www| 国产高清视频免费在线观看 | 免费日韩视频 | 最近最新中文字幕 | 人人爽夜夜爽 | 日韩精品免费在线播放 | 亚洲国产午夜 | 日韩两性视频 | 人人爽人人插 | 精品a级片 | 91精品国产综合久久福利不卡 | 欧美一级片免费观看 | 91av在线免费观看 | 在线看v片成人 | 久久天天躁狠狠躁夜夜不卡公司 | 久久精品视频免费观看 | 久久亚洲精品电影 | 精品久久久久久亚洲综合网 | 亚洲免费小视频 | 久草在线99 | 成年人视频在线免费 | 久久99久久99 | 手机av电影在线观看 | 亚洲精品日韩一区二区电影 | 国产精品久久久久久一二三四五 | 成人免费在线播放 | 天堂久色 | 国产精国产精品 | a爱爱视频| 久久精品国亚洲 | 久久伊人精品一区二区三区 | 黄色免费大全 | 九九九九九九精品任你躁 | 中文字字幕在线 | 中文资源在线播放 | 国产专区一 | 91理论片午午伦夜理片久久 | 免费看一及片 | 五月婷婷激情综合网 | 91中文字幕网 | 欧美日韩国产精品一区二区 | 国产精品mv在线观看 | 91精品老司机久久一区啪 | 中文字幕日韩电影 | 色婷婷狠狠五月综合天色拍 | 中文字幕在线观看免费高清完整版 | 色999视频 | 嫩嫩影院理论片 | 综合精品在线 | 成人久久免费 | 黄色一级免费电影 | 综合网在线视频 | 亚洲一级片在线看 | 成人理论电影 | 国产精品一区在线观看 | 日韩三级在线观看 | 免费视频91 | 久久韩国免费视频 | 国产免费叼嘿网站免费 | 成人av教育 | 国产精品久久久久久久久久免费看 | 久久综合九色欧美综合狠狠 | 久久午夜免费观看 | 国产精品福利av | 五月天天天操 | 亚洲婷婷综合色高清在线 | 中文字幕在线免费看线人 | 亚洲伦理一区 | 黄色国产成人 | 日韩在线视频国产 | 毛片基地黄久久久久久天堂 | 国产 日韩 欧美 中文 在线播放 | 黄色软件视频网站 | 人人舔人人射 | 一区二区三区 亚洲 | 日韩av五月天| 免费在线成人av电影 | 久久在线免费观看视频 | 九九在线精品视频 | 五月丁婷婷 | 九九热国产视频 | 人人爽人人av | 日本 在线 视频 中文 有码 | 久久少妇av| 免费色视频在线 | 国产专区免费 | 少妇性bbb搡bbb爽爽爽欧美 | 久久天天操| 国产一区视频免费在线观看 | 久久免费国产精品1 | 中文在线免费一区三区 | 一色av| 亚洲精品动漫成人3d无尽在线 | 亚洲综合色视频 | 97国产人人| 欧美日韩精品在线 | 91色影院| 超级碰碰碰碰 | 天堂素人在线 | 国产麻豆果冻传媒在线观看 | 91福利社区在线观看 | 亚洲精品乱码白浆高清久久久久久 | 国产一区网址 | 97精品久久人人爽人人爽 | 精品99在线| 久久久久久久久毛片 | 日韩在线 一区二区 | 日本二区三区在线 | 久久8精品 | 久色 网 | 韩日电影在线免费看 | 国产成人精品久久久久蜜臀 | 国产精品免费观看国产网曝瓜 | 免费在线观看午夜视频 | 西西444www大胆高清图片 | 国产亚洲免费观看 | 国产在线黄 | 欧美日韩成人 | 国产精品黄色在线观看 | 国产经典 欧美精品 | 一区三区视频在线观看 | 亚洲精品国产自产拍在线观看 | 久久亚洲免费 | 超碰国产人人 | 欧美精品一区在线 | 亚洲一二三区精品 | 天天操天天摸天天干 | 麻花传媒mv免费观看 | 日本三级久久 | 一区二区久久久久 | 国产成人一区二区三区在线观看 | 国产精品成人免费一区久久羞羞 | 日韩中出在线 | 去干成人网 | 国产精品资源在线观看 | 免费在线一区二区三区 | 天天操天天摸天天干 | 国产美女免费看 | 国产精品美女999 | 日韩国产精品一区 | www.com黄 | 成人免费ⅴa | 久草热久草视频 | 伊人永久在线 | 亚洲精品美女在线观看 | 国产精品免费av | 国产一区二区久久 | 开心激情网五月天 | 久久精品一区二区三区四区 | 久久久91精品国产一区二区精品 | 天堂va在线高清一区 | 欧美精品亚洲精品 | 97视频久久久 | 国产精品毛片一区二区 | 色视频 在线 | 91视频在线看 | 97免费视频在线 | 久久1区 | 天天天天天天干 | 久久99爱视频 | 国内精品久久久久久久97牛牛 | 成人cosplay福利网站 | 欧美韩国在线 | 国产偷v国产偷∨精品视频 在线草 | 国产成人一区二区啪在线观看 | 毛片网站在线看 | 正在播放日韩 | 成人国产网址 | 欧美日韩国产一区二区三区在线观看 | 亚洲天堂va| 2022久久国产露脸精品国产 | 久久久亚洲麻豆日韩精品一区三区 | 美女网站视频一区 | 亚洲精欧美一区二区精品 | 久久精品成人热国产成 | 久久免费公开视频 | 黄色成人毛片 | 丁香花在线视频观看免费 | 91欧美视频网站 | 欧美性春潮 | 亚洲www天堂com | 人人澡人| 中文国产在线观看 | 色狠狠一区二区 | 天天操天天操天天 | 久久久精品亚洲 | 久久蜜臀av | 五月丁色 | 99热都是精品 | 欧美xxxx性xxxxx高清 | 亚洲一区在线看 | 亚洲日本一区二区在线 | 免费看片亚洲 | www.com.黄| 91传媒在线 | 92国产精品久久久久首页 | 国产乱对白刺激视频在线观看女王 | 国产亚洲成人网 | 成人在线观看影院 | 伊人精品在线 | 麻豆视频入口 | 久久不卡电影 | 在线成人一区二区 | 久久成人精品电影 | 97视频中文字幕 | 我要色综合天天 | 国产一级片网站 | 欧美少妇18p| 成人av影视观看 | 久久精品国产久精国产 | 国产在线不卡精品 | 国产黄色精品视频 | 日韩欧美在线观看一区二区三区 | 成人小视频在线播放 | 色爱成人网| 欧美日韩综合在线 | 久久99精品久久久久久久久久久久 | 在线观看亚洲免费视频 | 伊人久久国产 | 激情丁香综合五月 | 久草视频看看 | 日本久久免费电影 | 精品国产乱码久久久久久1区2匹 | 99精品福利视频 | 国产免费xvideos视频入口 | 中文字幕色在线视频 | 91免费高清观看 | 国产一区影院 | 天天操天天操天天操天天操 | av电影免费在线播放 | 国内综合精品午夜久久资源 | 国产五十路毛片 | 国产高h视频 | 久久香蕉电影 | 天天爽天天爽夜夜爽 | 国产麻豆果冻传媒在线观看 | 黄色小说网站在线 | 毛片基地黄久久久久久天堂 | 天天操天天射天天爱 | 一区二区中文字幕在线播放 | 国产成人精品在线观看 | 免费在线国产黄色 | 91在线视频| 国语自产偷拍精品视频偷 | 91精品老司机久久一区啪 | 中文字幕二区三区 | 丁香综合| 色婷婷激婷婷情综天天 | 91亚洲精品久久久蜜桃借种 | 精品一区中文字幕 | 欧美日韩在线第一页 | 色窝资源 | 正在播放五月婷婷狠狠干 | 免费久久久久久久 | 久久久久久久久黄色 | 深夜免费福利视频 | 免费高清国产 | 在线 视频 一区二区 | 黄色片免费在线 | 又爽又黄又刺激的视频 | 国产精品系列在线 | 91黄在线看 | 成人一区二区三区在线 | 91精品导航| 国产一区在线视频播放 | 天堂中文在线视频 | 五月婷婷在线综合 | 91成年视频 | 久草网站 | 高潮久久久久久久久 | 五月激情综合婷婷 | 九九国产精品视频 | 日韩 在线a| 亚洲综合色丁香婷婷六月图片 | 久久成人福利 | 成人中心免费视频 | 国产精品久久久久久久免费 | 婷婷综合视频 | 久久第四色 | 午夜av大片| 狠狠狠干狠狠 | 欧洲精品亚洲精品 | 天天色图 | 亚洲国产影院 | 国产亚洲日本 | 欧美在线观看视频一区二区三区 | 亚洲天堂毛片 | 午夜精品影院 | 国产精品久久久久久影院 | 精品专区一区二区 | 免费看一级黄色 | 狠狠色丁香婷综合久久 | 久草免费福利在线观看 | 国内外成人免费在线视频 | 亚洲欧美激情插 | 黄色.com| 精品久久久久久亚洲综合网站 | 日躁夜躁狠狠躁2001 | 婷婷在线网站 | 天堂av色婷婷一区二区三区 | 色插综合| 久久99精品一区二区三区三区 | 久久久www成人免费精品张筱雨 | 国产精品久久久久久久av大片 | 97超碰中文字幕 | 91丝袜美腿 | 激情电影影院 | 成人av av在线 | 国产 日韩 在线 亚洲 字幕 中文 | 欧美午夜一区二区福利视频 | 国产精品午夜在线观看 | 狠狠干干 | 高清免费av在线 | 中文字幕亚洲综合久久五月天色无吗'' | 肉色欧美久久久久久久免费看 | 亚洲精品国产精品乱码在线观看 | 欧美日韩免费视频 | 欧美日韩在线观看不卡 | 69av网| 欧美污在线观看 | 国产精品久久久久aaaa九色 | 久久成人一区二区 | 黄色国产区 | 蜜臀精品久久久久久蜜臀 | 最近中文字幕大全中文字幕免费 | 久久香蕉国产精品麻豆粉嫩av | 五月天丁香综合 | 国产资源 | 欧美 日韩 性 | 亚洲视频1 | 亚洲国产片色 | 在线成人高清电影 | 欧美aaa级片| 国产一区二区在线观看免费 | a在线免费观看视频 | 欧美激情第八页 | 伊人伊成久久人综合网站 | 色综合天天色综合 | 91精品在线视频观看 | 成年人毛片在线观看 | 亚洲最大av在线播放 | 国产一级二级视频 | 九九视频在线观看视频6 | jizz18欧美18 | 国产视频网站在线观看 | 国产vs久久 | 久草在线视频免赞 | 日韩三级av | 一区二区精品在线 | 国产九色视频在线观看 | 久久久www免费电影网 | 国产亚洲精品av | 一级免费片| 日本三级大片 | 免费色视频在线 | 国产视频一区二区三区在线 | 日韩草比 | 成年人网站免费在线观看 | 国内久久精品视频 | 综合久久精品 | 丝袜美腿一区 | 日韩av一卡二卡三卡 | 日韩精品一区二区三区中文字幕 | 久久情爱 | 国产精品黄色在线观看 | 在线观看视频亚洲 | 成人免费大片黄在线播放 | 久久久国产电影 | 精品a级片 | 久久手机免费观看 | 四虎成人av | 婷婷六月色 | 色婷婷综合久久久久中文字幕1 | 色吊丝在线永久观看最新版本 | 丁香免费视频 | 午夜免费在线观看 | 日韩精品中文字幕在线播放 | 国产精品专区h在线观看 | 免费观看9x视频网站在线观看 | 在线观看亚洲a | 99热亚洲精品 | 久精品视频在线 | 日韩精品视频一二三 | 午夜视频福利 | 免费性网站| 国产日韩精品一区二区三区在线 | 国产精品手机播放 | 久久视频热| 一级a性色生活片久久毛片波多野 | 日韩大片免费在线观看 | 麻豆影视网 | 色老板在线视频 | 在线观看视频国产 | 一区二区 不卡 | 久久久久久久毛片 | 在线视频国产区 | 狠狠狠狠狠狠狠 | 日日摸日日碰 | 亚洲永久精品一区 | 国产亚洲精品中文字幕 | 久久草精品 | 国产精品嫩草影院123 | 天天操综合 | 99精品亚洲 | 91完整版在线观看 | 欧美成天堂网地址 | 欧美成人精品欧美一级乱黄 | 99超碰在线播放 | 天天拍夜夜拍 | 天天干,天天射,天天操,天天摸 | 黄色小网站免费看 | 精品久久久久久久久久岛国gif | 夜夜爽88888免费视频4848 | 黄色aaa级片 | 在线免费看黄网站 | 一区在线免费观看 | 国产婷婷一区二区 | 日韩理论在线视频 | 国产精品免费在线播放 | 最新成人在线 | 亚洲在线看 | 黄色在线看网站 | 日本乱视频 | 91福利国产在线观看 | 欧美一二三区在线观看 | 久久综合久久综合这里只有精品 | 青青草国产精品 | 日韩黄视频 | 国产精品免费一区二区三区 | 超碰激情在线 | 国产亚洲在 | 91精品国产九九九久久久亚洲 | 一区二区精品国产 | 国产精品1区2区 | 国产aaa大片 | 狠狠做深爱婷婷综合一区 | 国产精品精 | 日韩中文字幕视频在线观看 | av中文字幕在线播放 | 久草在线视频首页 |