網(wǎng)站是如何建立的網(wǎng)絡(luò)營(yíng)銷推廣seo
文章目錄
- 1 什么是設(shè)備樹(shù)?
- 2 DTS、DTB和DTC
- 3 DTS語(yǔ)法
- 3.1 dtsi頭文件
- 3.2 設(shè)備節(jié)點(diǎn)
- 3.3 標(biāo)準(zhǔn)屬性
- 3.4 根節(jié)點(diǎn)compatible屬性
- 3.5 向節(jié)點(diǎn)追加或修改內(nèi)容
- 4 創(chuàng)建小型模板設(shè)備樹(shù)
- 5 設(shè)備樹(shù)在系統(tǒng)中的體現(xiàn)
- 6 綁定信息文檔
- 7 設(shè)備樹(shù)常用OF操作函數(shù)
- 7.1 查找節(jié)點(diǎn)的OF函數(shù)
- 7.2 查找父/子節(jié)點(diǎn)的OF函數(shù)
- 7.3 提取屬性值的OF函數(shù)
- 7.4 其他常用的OF函數(shù)
系列文章:
Linux驅(qū)動(dòng)開(kāi)發(fā)——字符設(shè)備驅(qū)動(dòng)開(kāi)發(fā)
Linux驅(qū)動(dòng)開(kāi)發(fā)——LED驅(qū)動(dòng)開(kāi)發(fā)
Linux驅(qū)動(dòng)開(kāi)發(fā)——新字符設(shè)備驅(qū)動(dòng)開(kāi)發(fā)
1 什么是設(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è)備等等,如下圖所示:
在以前的內(nèi)核中,還沒(méi)有采用設(shè)備樹(shù),內(nèi)核源碼中有大量的arch/arm/mach-xxx和arch/arm/plat-xxx文件夾,用于存儲(chǔ)不同平臺(tái)的板級(jí)信息,但隨著芯片產(chǎn)業(yè)的發(fā)展,加入內(nèi)核中的板級(jí)信息日益龐大,導(dǎo)致內(nèi)核“虛胖”,所以引入了PowerPC等架構(gòu)已經(jīng)采用的設(shè)備樹(shù)。將這些描述板級(jí)硬件信息的內(nèi)容都從Linux中分離,用一個(gè)專屬的文件格式類描述,這個(gè)專屬的文件就叫做設(shè)備樹(shù),文件擴(kuò)展名為.dts。一個(gè)SOC可以出很多不同的班子,不同的板子肯定有相同的部分信息,將這些相同信息提取到一個(gè)文件中,其他dts文件引用這個(gè)文件,避免重復(fù)定義,這個(gè)通用文件就是dtsi文件,有點(diǎn)類似于C語(yǔ)言中的頭文件。
2 DTS、DTB和DTC
設(shè)備樹(shù)源文件為dts文件,該文件編譯之后,成為dtb文件,而編譯dts文件的工具是DTC工具。
該工具是以源碼的形式存在于內(nèi)核源碼中的,位于kernel/scripts/dtc目錄下
可以使用dtc工具編譯dts文件:
這里是Android系統(tǒng)內(nèi)核,首先需要執(zhí)行defconfig配置:
make ARCH=arm64 rockchip_defconfig android-11.config
然后編譯
make ARCH=arm64 dtbs
輸出如下:
CALL scripts/checksyscalls.sh
DTC arch/arm64/boot/dts/rockchip/px30-ad-d6-anx6345.dtb
DTC arch/arm64/boot/dts/rockchip/px30-ad-r35-mb-rk618-dual-lvds.dtb
DTC arch/arm64/boot/dts/rockchip/px30-ad-r35-mb-rk618-hdmi.dtb
DTC arch/arm64/boot/dts/rockchip/px30-ad-r35-mb-rk618-hdmi-lvds.dtb
DTC arch/arm64/boot/dts/rockchip/px30-ad-r35-mb-rk618-lvds.dtb
DTC arch/arm64/boot/dts/rockchip/px30-evb-ddr3-lvds-v10.dtb
DTC arch/arm64/boot/dts/rockchip/px30-evb-ddr3-v10.dtb
DTC arch/arm64/boot/dts/rockchip/px30-evb-ddr3-v10-avb.dtb
...
也可以單獨(dú)編譯某一個(gè)dts文件
make ARCH=arm64 rockchip/rk3568-atk-evb1-ddr4-v10.dtb
默認(rèn)內(nèi)核編譯的腳本中會(huì)包含源碼樹(shù)文件的編譯。
3 DTS語(yǔ)法
3.1 dtsi頭文件
dtsi頭文件類似于C語(yǔ)言中的頭文件,包含一些公共的信息,可以在其他dts文件中導(dǎo)入
#include "rk3568-atk-evb1-ddr4-v10.dtsi"
#include "rk3568-linux.dtsi"
dts文件中除了可以引用dtsi文件,還可以引用.h頭文件,甚至也可以引用dts文件。但是遵從編寫(xiě)習(xí)慣,還是將dtsi文件作為設(shè)備樹(shù)的頭文件,而不使用.h頭文件。
一般.dtsi文件中描述的是SOC的內(nèi)部外設(shè)信息,比如cpu架構(gòu)、主頻、外設(shè)寄存器地址范圍等。
#include <dt-bindings/clock/rk3568-cru.h>
...
#include <dt-bindings/thermal/thermal.h>
#include "rk3568-dram-default-timing.dtsi"/ {compatible = "rockchip,rk3568";interrupt-parent = <&gic>;#address-cells = <2>;#size-cells = <2>;aliases {csi2dphy0 = &csi2_dphy0;csi2dphy1 = &csi2_dphy1;csi2dphy2 = &csi2_dphy2;dsi0 = &dsi0;dsi1 = &dsi1;ethernet0 = &gmac0;ethernet1 = &gmac1;gpio0 = &gpio0;gpio1 = &gpio1;gpio2 = &gpio2;gpio3 = &gpio3;gpio4 = &gpio4;i2c0 = &i2c0;...mmc0 = &sdhci;...serial0 = &uart0;...spi0 = &spi0;...};cpus {#address-cells = <2>;#size-cells = <0>;cpu0: cpu@0 {device_type = "cpu";compatible = "arm,cortex-a55";reg = <0x0 0x0>;enable-method = "psci";clocks = <&scmi_clk 0>;operating-points-v2 = <&cpu0_opp_table>;cpu-idle-states = <&CPU_SLEEP>;#cooling-cells = <2>;dynamic-power-coefficient = <187>;};cpu1: cpu@100 {device_type = "cpu";compatible = "arm,cortex-a55";reg = <0x0 0x100>;enable-method = "psci";clocks = <&scmi_clk 0>;operating-points-v2 = <&cpu0_opp_table>;cpu-idle-states = <&CPU_SLEEP>;};cpu2: cpu@200 {device_type = "cpu";compatible = "arm,cortex-a55";reg = <0x0 0x200>;enable-method = "psci";clocks = <&scmi_clk 0>;operating-points-v2 = <&cpu0_opp_table>;cpu-idle-states = <&CPU_SLEEP>;};cpu3: cpu@300 {device_type = "cpu";compatible = "arm,cortex-a55";reg = <0x0 0x300>;enable-method = "psci";clocks = <&scmi_clk 0>;operating-points-v2 = <&cpu0_opp_table>;cpu-idle-states = <&CPU_SLEEP>;};idle-states {entry-method = "psci";CPU_SLEEP: cpu-sleep {compatible = "arm,idle-state";local-timer-stop;arm,psci-suspend-param = <0x0010000>;entry-latency-us = <100>;exit-latency-us = <120>;min-residency-us = <1000>;};};};
}
以上是rk3568.dtsi文件的節(jié)選,包含了cpu、uart、I2c相關(guān)的一些設(shè)備信息。
3.2 設(shè)備節(jié)點(diǎn)
- 根節(jié)點(diǎn)
設(shè)備樹(shù)采用樹(shù)形結(jié)構(gòu)描述板子上的設(shè)備信息,每個(gè)設(shè)備都是一個(gè)節(jié)點(diǎn),叫做設(shè)備節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)通過(guò)一些屬性信息來(lái)描述節(jié)點(diǎn)信息,屬性就是鍵-值對(duì)。
/ {...cpus {...};
}
這里的“/”是根節(jié)點(diǎn),每個(gè)設(shè)備樹(shù)文件只有一個(gè)根節(jié)點(diǎn)。如果引用的兩個(gè)文件都有根節(jié)點(diǎn),那么兩個(gè)根節(jié)點(diǎn)會(huì)進(jìn)行合并,合并成一個(gè)根節(jié)點(diǎn)。
- 子節(jié)點(diǎn)
aliases {...
}
cpus {...
}
以上位于根節(jié)點(diǎn)之下的為根節(jié)點(diǎn)的子節(jié)點(diǎn),設(shè)備樹(shù)中節(jié)點(diǎn)命名格式為:
node-name@unit-address
其中node-name是節(jié)點(diǎn)的名字,節(jié)點(diǎn)的名字能夠清晰的描述節(jié)點(diǎn)的功能,比如“uart1”表示UART1這個(gè)外設(shè)。
unit-address一般表示設(shè)備的地址或者寄存器首地址,如果沒(méi)有,則可以不要
查看cpu節(jié)點(diǎn),顯示如下:
cpu0:cpu@0
這里用:分為了兩部分,前面是節(jié)點(diǎn)的標(biāo)簽,后面是節(jié)點(diǎn)定義
- 節(jié)點(diǎn)屬性
一個(gè)節(jié)點(diǎn)會(huì)包含不同的屬性,不同的屬性保存不同的內(nèi)容,屬性都是鍵值對(duì),值可以為空或者任意的字節(jié)流,以下是常見(jiàn)的屬性數(shù)據(jù)形式: - 字符串
compatible="rockchip,rk3568";
- 32位無(wú)符號(hào)整數(shù)
reg=<0>;
- 字符串列表
compatible="rockchip,rk3568-evb", "rockchip,rk3568";
3.3 標(biāo)準(zhǔn)屬性
以下是節(jié)點(diǎn)中常用的一些屬性
- 1、compatible屬性
compatible屬性叫做“兼容性”屬性,是一個(gè)字符串列表,用于將設(shè)備和驅(qū)動(dòng)綁定起來(lái)。字符串列表用于選擇設(shè)備所要使用的驅(qū)動(dòng)程序,格式如下:
"manufacturer,model"
其中manufacture表示廠商,model一般是模塊對(duì)應(yīng)的驅(qū)動(dòng)名字。比如rk3568-atk-evb1-v10.dtsi中有一個(gè)MIPI攝像頭節(jié)點(diǎn),芯片采用SNOY公司的IMX415,屬性值如下:
compatible = "sony,imx415";
compatible可以有多個(gè)屬性值,比如:
compatible = "arm,cortex-a55-pmu", "arm,armv8-pmuv3";
在這樣的配置中,在查找設(shè)備對(duì)應(yīng)的驅(qū)動(dòng)時(shí),先將第一個(gè)兼容值在Linux內(nèi)核中查找,如果能夠找到則使用該驅(qū)動(dòng)文件,如果沒(méi)有,則查看第二個(gè)兼容值,以此類推,直到查找完所有的兼容值。
一般驅(qū)動(dòng)程序文件都會(huì)有一個(gè)OF匹配列表,用于保存compatible值,如果設(shè)備節(jié)點(diǎn)的compatible值和OF匹配列表中的任何一個(gè)值相等,則表示設(shè)備可以使用該驅(qū)動(dòng)。比如文件buildroot系統(tǒng)源碼中的imx415.c文件中:
static const struct of_device_id imx415_of_match[] = {{ .compatible = "sony,imx415" },{},
};
該數(shù)組中只有一個(gè)compatible值,當(dāng)設(shè)備樹(shù)中的哪個(gè)節(jié)點(diǎn)與該compatible值匹配的時(shí)候,那么這個(gè)節(jié)點(diǎn)就會(huì)使用此驅(qū)動(dòng)文件
- 2、model屬性
model屬性值是一個(gè)字符串,一般model描述開(kāi)發(fā)板的名字或者設(shè)備模塊信息,比如:
model = "Rockchip rk3568 EVB DDR4 V10 Board";
- 3、status屬性
表示設(shè)備狀態(tài),屬性值是字符串
值 | 描述 |
---|---|
“okay” | 表示設(shè)備可操作 |
“disabled” | 表示設(shè)備不可操作,但是未來(lái)可以變?yōu)榭刹僮鞯?#xff0c;比如熱插拔設(shè)備插入后 |
“fail” | 表示設(shè)備不可操作,設(shè)備檢測(cè)到了一系列錯(cuò)誤,設(shè)備也不太可能變成可操作 |
“fail-sss” | 和“fail”相同,后面的sss表示檢測(cè)到的內(nèi)容 |
- 4、#address-cells和#size-cells屬性
這兩個(gè)屬性都是無(wú)符號(hào)32位整數(shù),這兩個(gè)屬性可以用在任何擁有子節(jié)點(diǎn)的設(shè)備中,用于描述子節(jié)點(diǎn)的地址信息。#address-cells 屬性值決定了子節(jié)點(diǎn) reg 屬性中地址信息所占用的字長(zhǎng)(32 位),#size-cells 屬性值決定了子節(jié)點(diǎn) reg 屬性中長(zhǎng)度信息所占的字長(zhǎng)(32 位)。
這兩個(gè)屬性表明子節(jié)點(diǎn)如何編寫(xiě)reg屬性值,reg屬性和地址相關(guān),這兩個(gè)就是描述起始地址和地址長(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)。
- 5、reg屬性
(address,length)對(duì),描述設(shè)備地址空間資源信息或者設(shè)備地址信息,比如某個(gè)外設(shè)寄存器地址范圍信息,或者IIC設(shè)備地址等。
uart5: serial@fe690000 {compatible = "rockchip,rk3568-uart", "snps,dw-apb-uart";reg = <0x0 0xfe690000 0x0 0x100>;...
}
這里0xfe690000描述的是uart5寄存器首地址,長(zhǎng)度為0x100
- 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:子總線地址空間的物理地址,由父節(jié)點(diǎn)的#address-cells 確定此物理地址所占用的字長(zhǎng)。
parent-bus-address :父總線地址空間的物理地址,同樣由父節(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ì)于我們所使用的 RK3568 來(lái)說(shuō),子地址空間和父地址空間完全相同,因此會(huì)在 rk3568.dtsi 中找到大量的值為空的 ranges 屬性 - 7、name屬性
記錄節(jié)點(diǎn)名字,已被棄用 - 8、device_type屬性
用于描述設(shè)備的FCode,但是設(shè)備樹(shù)沒(méi)有FCode,所以此屬性不會(huì)使用。此屬性只能用于cpu節(jié)點(diǎn)或者memory節(jié)點(diǎn)。
3.4 根節(jié)點(diǎn)compatible屬性
每個(gè)節(jié)點(diǎn)都有compatible屬性,根節(jié)點(diǎn)也是,根節(jié)點(diǎn)的屬性內(nèi)容如下:
/ {model = "Rockchip RK3568 EVB1 DDR4 V10 Board";compatible = "rockchip,rk3568-evb1-ddr4-v10", "rockchip,rk3568";
};
rk3568-atk-evb1-ddr4-v10.dtsi中定義的根節(jié)點(diǎn)中的compatible包含兩個(gè)值。設(shè)備節(jié)點(diǎn)中的compatible用于匹配驅(qū)動(dòng)程序,根節(jié)點(diǎn)中的有什么作用呢?這里根節(jié)點(diǎn)第一個(gè)通常描述設(shè)備名稱,這里使用的就是“rk3568-evb1-ddr4-v10”這個(gè)設(shè)備。第二個(gè)值描述了設(shè)備使用的SOC,這里使用的是rk3568。
Linux內(nèi)核會(huì)根據(jù)根節(jié)點(diǎn)的compatible屬性查看是否支持此設(shè)備,如果支持的話就會(huì)啟動(dòng)Linux內(nèi)核。
include/linux/rockchip/cpu.h
static inline bool cpu_is_rk3568(void)
{if (rockchip_soc_id)return (rockchip_soc_id & ROCKCHIP_CPU_MASK) == ROCKCHIP_CPU_RK3568;return of_machine_is_compatible("rockchip,rk3568");
}
這里會(huì)判斷soc是否為rk3568,根據(jù)根節(jié)點(diǎn)的compatible屬性進(jìn)行判斷。
3.5 向節(jié)點(diǎn)追加或修改內(nèi)容
假設(shè)要將一個(gè)fxls8471的設(shè)備連接到rk3568的i2c5這個(gè)端口上,那么就需要向i25節(jié)點(diǎn)中添加一個(gè)fxls8471子節(jié)點(diǎn)
i2c5: i2c@fe5e0000 {compatible = "rockchip,rk3399-i2c";reg = <0x0 0xfe5e0000 0x0 0x1000>;clocks = <&cru CLK_I2C5>, <&cru PCLK_I2C5>;clock-names = "i2c", "pclk";interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;pinctrl-names = "default";pinctrl-0 = <&i2c5m0_xfer>;#address-cells = <1>;#size-cells = <0>;status = "disabled";fxls8471: fxls8471@1e {compatible = "fsl,fxls8471";reg = <0x1e>;};
};
這樣就添加了一個(gè)節(jié)點(diǎn),但是有個(gè)問(wèn)題是,這里添加的文件是rk3568.dtsi文件,而該文件是設(shè)備樹(shù)頭文件,所有使用SOC的版本都會(huì)引用這個(gè)文件,那么對(duì)于特定設(shè)備的修改缺影響了所有的該SOC的板子,這是不合理的。所以需要向設(shè)備追加數(shù)據(jù),目前該板子使用的設(shè)備樹(shù)文件為rk3568-atk-evb1-ddr4-v10.dsi,因此可以在這個(gè)文件中追加
&i2c5 {status = "okay";clock-frequency = <400000>;fxls8471: fxls8471@1e {compatible = "fsl,fxls8471";reg = <0x1e>;};
}
&i2c5表示訪問(wèn)i2c5這個(gè)label所對(duì)應(yīng)的節(jié)點(diǎn),label是通過(guò)aliases設(shè)置的,這樣就完成了子節(jié)點(diǎn)的追加。同時(shí)修改了i2c5這個(gè)設(shè)備的狀態(tài),從disabled改為okay。
4 創(chuàng)建小型模板設(shè)備樹(shù)
/ {compatible = "rockchip,rk3568-evb1-ddr4-v10","rockchip,rk3568";cpus {#address-cells = <2>;#size-cells = <0>;cpu0: cpu@0 {device_type = "cpu";compatible = "arm,cortex-a55";reg = <0x0 0x0>;};cpu1: cpu@100 {device_type = "cpu";compatible = "arm,cortex-a55";reg = <0x0 0x100>;};cpu2: cpu@200 {device_type = "cpu";compatible = "arm,cortex-a55";reg = <0x0 0x200>;};cpu3: cpu@300 {device_type = "cpu";compatible = "arm,cortex-a55";reg = <0x0 0x300>;};}uart2: serial@fe660000 {compatible = "rockchip,rk3568-uart", "snps,dw-apb-uart";reg = <0x0 0xfe660000 0x0 0x100>;};spi0: spi@fe610000 {compatible = "rockchip,rk3568-spi","rockchip,rk3066-spi";reg = <0x0 0xfe610000 0x0 0x1000>;};i2c5: i2c@fe5e0000 {compatible = "rockchip,rk3568-i2c","rockchip,rk3399-i2c";reg = <0x0 0xfe5e0000 0x0 0x1000>;};
};
這里創(chuàng)建了一個(gè)小型設(shè)備樹(shù),描述rk3568的cpu、uart2、spi0和i2c5這幾個(gè)節(jié)點(diǎn)
5 設(shè)備樹(shù)在系統(tǒng)中的體現(xiàn)
可以通過(guò)根文件系統(tǒng)的/proc/device-tree目錄查看設(shè)備樹(shù),每一個(gè)節(jié)點(diǎn)都會(huì)在這個(gè)目錄下生成一個(gè)文件夾
查看根節(jié)點(diǎn)屬性
以上為model屬性
以上是compatible屬性
以上是cpus節(jié)點(diǎn),其下的子節(jié)點(diǎn)也會(huì)創(chuàng)建目錄
以上是aliases節(jié)點(diǎn),目錄下有很多文件,每個(gè)文件內(nèi)容就是其節(jié)點(diǎn)的真實(shí)名字
以上是aliases中的i2c0文件內(nèi)容
以上是chosen子節(jié)點(diǎn),重點(diǎn)是bootargs參數(shù),是為了uboot向Linux內(nèi)核傳遞數(shù)據(jù)的。
Kernel command line: storagemedia=emmc androidboot.storagemedia=emmc androidboot.mode=normal androidboot.dtb_idx=0 androidboot.dtbo_idx=0 androidboot.verifiedbootstate=orange androidboot.serialno=daf9b9fe169b1e37 console=ttyFIQ0 androidboot.baseband=N/A androidboot.wificountrycode=CN androidboot.veritymode=enforcing androidboot.hardware=rk30board androidboot.console=ttyFIQ0 androidboot.verifiedbootstate=orange firmware_class.path=/vendor/etc/firmware init=/init rootwait ro loop.max_part=7 buildvariant=eng earlycon=uart8250,mmio32,0xfe660000 androidboot.boot_devices=fe310000.sdhci,fe330000.nandc
從Android的內(nèi)核日志中可以看到kernel command line就是chosen中bootargs中的值。這個(gè)是Android中的,這里會(huì)比設(shè)備樹(shù)中的chosen定義多一些數(shù)據(jù),多出來(lái)的這部分是uboot傳遞給Linux內(nèi)核的,boot會(huì)對(duì)將其與設(shè)備樹(shù)中的定義進(jìn)行組合,并傳遞給kernel。
6 綁定信息文檔
設(shè)備樹(shù)描述設(shè)備信息,不同的設(shè)備信息不同,反映到設(shè)備樹(shù)中就是屬性不同。如果需要添加一個(gè)硬件對(duì)應(yīng)的節(jié)點(diǎn),可以通過(guò)Linux內(nèi)核源碼中的Documentation/devicetree/bindings目錄下的文檔進(jìn)行配置,這些文檔稱為綁定文檔。
比如添加一個(gè)i2c文檔,可以查閱Documentation/devicetree/bindings/i2c/i2c-rk3x.txt文件:
* Rockchip RK3xxx I2C controllerThis driver interfaces with the native I2C controller present in Rockchip
RK3xxx SoCs.Required properties :- reg : Offset and length of the register set for the device- compatible: should be one of the following:- "rockchip,rv1108-i2c": for rv1108- "rockchip,rv1126-i2c": for rv1126- "rockchip,rk3066-i2c": for rk3066- "rockchip,rk3188-i2c": for rk3188- "rockchip,rk3228-i2c": for rk3228- "rockchip,rk3288-i2c": for rk3288- "rockchip,rk3328-i2c", "rockchip,rk3399-i2c": for rk3328- "rockchip,rk3399-i2c": for rk3399- interrupts : interrupt number- clocks: See ../clock/clock-bindings.txt- For older hardware (rk3066, rk3188, rk3228, rk3288):- There is one clock that's used both to derive the functional clockfor the device and as the bus clock.- For newer hardware (rk3399): specified by name- "i2c": This is used to derive the functional clock.- "pclk": This is the bus clock.Required on RK3066, RK3188 :- rockchip,grf : the phandle of the syscon node for the general registerfile (GRF)- on those SoCs an alias with the correct I2C bus ID (bit offset in the GRF)is also required.Optional properties :- clock-frequency : SCL frequency to use (in Hz). If omitted, 100kHz is used.- i2c-scl-rising-time-ns : Number of nanoseconds the SCL signal takes to rise(t(r) in I2C specification). If not specified this is assumed to bethe maximum the specification allows(1000 ns for Standard-mode,300 ns for Fast-mode) which might cause slightly slower communication.- i2c-scl-falling-time-ns : Number of nanoseconds the SCL signal takes to fall(t(f) in the I2C specification). If not specified this is assumed tobe the maximum the specification allows (300 ns) which might causeslightly slower communication.- i2c-sda-falling-time-ns : Number of nanoseconds the SDA signal takes to fall(t(f) in the I2C specification). If not specified we'll use the SCLvalue since they are the same in nearly all cases.Example:aliases {i2c0 = &i2c0;
}i2c0: i2c@2002d000 {compatible = "rockchip,rk3188-i2c";reg = <0x2002d000 0x1000>;interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;#address-cells = <1>;#size-cells = <0>;rockchip,grf = <&grf>;clock-names = "i2c";clocks = <&cru PCLK_I2C0>;i2c-scl-rising-time-ns = <800>;i2c-scl-falling-time-ns = <100>;
};
這里描述了i2c設(shè)備節(jié)點(diǎn)配置包含的屬性,如reg、compatible等,還有一個(gè)示例。
7 設(shè)備樹(shù)常用OF操作函數(shù)
設(shè)備樹(shù)描述了設(shè)備的詳細(xì)信息,這些信息通過(guò)Linux內(nèi)核提供了一系列的函數(shù)來(lái)獲取,這一系列的函數(shù)都有一個(gè)統(tǒng)一的前綴“of_”,這些of函數(shù)定義在include/linux/of.h文件中
7.1 查找節(jié)點(diǎn)的OF函數(shù)
Linux內(nèi)核使用device_node結(jié)構(gòu)體來(lái)描述一個(gè)節(jié)點(diǎn)
struct device_node {const char *name;const char *type;phandle phandle;const char *full_name;struct fwnode_handle fwnode;struct property *properties;struct property *deadprops; /* removed properties */struct device_node *parent;struct device_node *child;struct device_node *sibling;
#if defined(CONFIG_OF_KOBJ)struct kobject kobj;
#endifunsigned long _flags;void *data;
#if defined(CONFIG_SPARC)const char *path_component_name;unsigned int unique_id;struct of_irq_controller *irq_trans;
#endif
};
查找節(jié)點(diǎn)有關(guān)的OF函數(shù)有5個(gè):
- of_find_node_by_name
extern struct device_node *of_find_node_by_name(struct device_node *from, const char *name);
通過(guò)節(jié)點(diǎn)名稱查找,from表示開(kāi)始節(jié)點(diǎn),如果為NULL,表示從根節(jié)點(diǎn)開(kāi)始查找整個(gè)設(shè)備樹(shù),name表示要查找的節(jié)點(diǎn)名字。
- of_find_node_by_type
extern struct device_node *of_find_node_by_type(struct device_node *from, const char *type);
通過(guò)device_type屬性來(lái)查找指定的節(jié)點(diǎn),from和第一個(gè)一樣,type表示device_type
- of_find_compatible_node
extern struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compat);
通過(guò)device_type和compatible這兩個(gè)屬性查找節(jié)點(diǎn),device_type可以為NULL,表示忽略掉device_type屬性。
- of_find_matching_node_and_match
struct of_device_id {char name[32];char type[32];char compatible[128];const void *data;
};extern 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);
通過(guò)of_device_id匹配表來(lái)查找節(jié)點(diǎn),match表示找到的匹配的of_device_id
- of_find_node_by_path
static inline struct device_node *of_find_node_by_path(const char *path)
{return of_find_node_opts_by_path(path, NULL);
}
通過(guò)路徑來(lái)查找指定的節(jié)點(diǎn)
7.2 查找父/子節(jié)點(diǎn)的OF函數(shù)
- of_get_parent
extern struct device_node *of_get_parent(const struct device_node *node);
用于獲取指定節(jié)點(diǎn)的父節(jié)點(diǎn)
3. of_get_next_child
extern struct device_node *of_get_next_child(const struct device_node *node,struct device_node *prev);
node表示父節(jié)點(diǎn),prev表示前一個(gè)子節(jié)點(diǎn),也就是從哪一個(gè)子節(jié)點(diǎn)開(kāi)始遍歷查找對(duì)應(yīng)的子節(jié)點(diǎn),設(shè)置為NULL表示從node父節(jié)點(diǎn)的第一個(gè)子節(jié)點(diǎn)開(kāi)始查找。
7.3 提取屬性值的OF函數(shù)
Linux內(nèi)核中使用property表示設(shè)備樹(shù)中的屬性
struct property {char *name;int length;void *value;struct property *next;
#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)unsigned long _flags;
#endif
#if defined(CONFIG_OF_PROMTREE)unsigned int unique_id;
#endif
#if defined(CONFIG_OF_KOBJ)struct bin_attribute attr;
#endif
};
Linux提供了讀取該屬性值的OF函數(shù)
4. of_find_property
extern struct property *of_find_property(const struct device_node *np,const char *name,int *lenp);
np表示設(shè)備節(jié)點(diǎn),name表示屬性的名字,lenp表示屬性值的字節(jié)數(shù),返回找到的property
5. of_property_count_elems_of_size
extern 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),propname表示屬性的名字,elem_size表示元素長(zhǎng)度。
6. of_property_read_u32_index
extern int of_property_read_u32_index(const struct device_node *np,const char *propname,u32 index, u32 *out_value);
該函數(shù)用于從屬性中獲取指定標(biāo)號(hào)的u32類型數(shù)據(jù)值。
np表示設(shè)備節(jié)點(diǎn),propname表示屬性名稱,index表示讀取的值的標(biāo)號(hào),out_value表示讀取到的值。
返回值0表示成功,負(fù)值表示讀取失敗,-EINVAL 表示屬性不存在,-ENODATA 表示沒(méi)有要讀取的數(shù)據(jù),-EOVERFLOW 表示屬性值列表太小
7. of_property_read_u8_array、of_property_read_u16_array、of_property_read_u32_array 、of_property_read_u64_array
static inline int of_property_read_u8_array(const struct device_node *np,const char *propname, u8 *out_values, size_t sz)
static inline int of_property_read_u16_array(const struct device_node *np,const char *propname, u16 *out_values, size_t sz)
static inline int of_property_read_u32_array(const struct device_node *np,const char *propname,u32 *out_values, size_t sz)
static inline int of_property_read_u64_array(const struct device_node *np,const char *propname,u64 *out_values, size_t sz)
分別讀取屬性的u8、u16、u32、u64類型的數(shù)組,sz表示要讀取數(shù)組元素?cái)?shù)量。
8. of_property_read_u8、of_property_read_u16、of_property_read_u32、of_property_read_u64
static inline int of_property_read_u8(const struct device_node *np,const char *propname,u8 *out_value)
static inline int of_property_read_u16(const struct device_node *np,const char *propname,u16 *out_value)
static inline int of_property_read_u32(const struct device_node *np,const char *propname,u32 *out_value)
static inline int of_property_read_u64(const struct device_node *np,const char *propname,u64 *out_value)
分別讀取屬性的u8、u16、u32、u64類型的數(shù)組
9. of_property_read_string
extern int of_property_read_string(const struct device_node *np,const char *propname,const char **out_string);
讀取字符串類型的屬性值
10. of_n_addr_cells
extern int of_n_addr_cells(struct device_node *np);
用于獲取#address-cells屬性值
11. of_n_size_cells
extern int of_n_size_cells(struct device_node *np);
用于獲取#size-cells屬性值
7.4 其他常用的OF函數(shù)
- of_device_is_compatible
static inline int of_device_is_compatible(const struct device_node *device,const char *name)
用于查看節(jié)點(diǎn)的compatible屬性是否包含name指定的字符串,也就是檢查節(jié)點(diǎn)的兼容性。
2. of_get_address
static inline const __be32 *of_get_address(struct device_node *dev, int index,u64 *size, unsigned int *flags)
用于獲取地址相關(guān)的屬性,主要是“reg”或者“assigned-addresses”屬性。dev是設(shè)備節(jié)點(diǎn),index表示讀取的地址標(biāo)號(hào),如reg中的地址index,size表示地址長(zhǎng)度。flags表示IORESOURCE_IO、IORESOURCE_MEM。
3. of_translate_address
extern const __be32 *of_get_address(struct device_node *dev, int index,u64 *size, unsigned int *flags);
用于將從設(shè)備樹(shù)讀取到的物理地址轉(zhuǎn)換為虛擬地址
4. of_address_to_resource
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 中,定義如下:
struct resource {resource_size_t start;resource_size_t end;const char *name;unsigned long flags;unsigned long desc;struct resource *parent, *sibling, *child;ANDROID_KABI_RESERVE(1);ANDROID_KABI_RESERVE(2);ANDROID_KABI_RESERVE(3);ANDROID_KABI_RESERVE(4);
};
對(duì)于32位的SOC,resource_size_t是u32類型,其中start表示起始地址,end表示結(jié)束地址,name是這個(gè)資源的名字,flags是自愿標(biāo)志位,一般表示資源類型,可選的資源標(biāo)志定義在include/linux/ioport.h文件中:
#define IORESOURCE_BITS 0x000000ff /* Bus-specific bits */#define IORESOURCE_TYPE_BITS 0x00001f00 /* Resource type */
#define IORESOURCE_IO 0x00000100 /* PCI/ISA I/O ports */
#define IORESOURCE_MEM 0x00000200
#define IORESOURCE_REG 0x00000300 /* Register offsets */
#define IORESOURCE_IRQ 0x00000400
#define IORESOURCE_DMA 0x00000800
#define IORESOURCE_BUS 0x00001000#define IORESOURCE_PREFETCH 0x00002000 /* No side effects */
#define IORESOURCE_READONLY 0x00004000
#define IORESOURCE_CACHEABLE 0x00008000
#define IORESOURCE_RANGELENGTH 0x00010000
#define IORESOURCE_SHADOWABLE 0x00020000#define IORESOURCE_SIZEALIGN 0x00040000 /* size indicates alignment */
#define IORESOURCE_STARTALIGN 0x00080000 /* start field is alignment */#define IORESOURCE_MEM_64 0x00100000
#define IORESOURCE_WINDOW 0x00200000 /* forwarded by bridge */
#define IORESOURCE_MUXED 0x00400000 /* Resource is software muxed */#define IORESOURCE_EXT_TYPE_BITS 0x01000000 /* Resource extended types */
#define IORESOURCE_SYSRAM 0x01000000 /* System RAM (modifier) */#define IORESOURCE_EXCLUSIVE 0x08000000 /* Userland may not map this resource */#define IORESOURCE_DISABLED 0x10000000
#define IORESOURCE_UNSET 0x20000000 /* No address assigned yet */
#define IORESOURCE_AUTO 0x40000000
#define IORESOURCE_BUSY 0x80000000 /* Driver has marked this resource busy */
...
一般常見(jiàn)的資源標(biāo)志就是 IORESOURCE_MEM 、 IORESOURCE_REG 和IORESOURCE_IRQ 等。
of_address_to_resource函數(shù)就是提取reg屬性值,然后將其轉(zhuǎn)換成resource結(jié)構(gòu)體類型:
extern int of_address_to_resource(struct device_node *dev, int index,struct resource *r);
- of_iomap
extern void __iomem *of_iomap(struct device_node *device, int index);
該函數(shù)用于直接內(nèi)存映射,之前通過(guò)ioremap函數(shù)完成物理地址到虛擬地址的映射,采用設(shè)備樹(shù)開(kāi)發(fā)之后通過(guò)of_iomap函數(shù)來(lái)獲取內(nèi)存地址所對(duì)應(yīng)的虛擬地址。of_iomap本質(zhì)是將reg屬性中的地址信息轉(zhuǎn)換為虛擬地址,如果reg屬性有多段的話,可以通過(guò)index參數(shù)來(lái)指定映射哪一段內(nèi)存