wap网站建设策划方案,厦门专业做网站公司,商城网站模板免费下载,今天发生的重大新闻事件一、什么是设备树#xff1f; 设备树(Device Tree)#xff0c;描述设备树的文件叫做 DTS(DeviceTree Source)#xff0c;这个 DTS 文件采用树形结构描述板级设备#xff0c;也就是开发板上的设备信息#xff1a; 树的主干就是系统总线#xff0c; IIC 控制器、 GPIO 控制…一、什么是设备树 设备树(Device Tree)描述设备树的文件叫做 DTS(DeviceTree Source)这个 DTS 文件采用树形结构描述板级设备也就是开发板上的设备信息 树的主干就是系统总线 IIC 控制器、 GPIO 控制器、 SPI 控制器等都是接到系统主线上的分支。IIC 控制器有分为 IIC1 和 IIC2 两种其中 IIC1 上接了 FT5206 和 AT24C02这两个 IIC 设备 IIC2 上只接了 MPU6050 这个设备。 一般.dts 描述板级信息(也就是开发板上有哪些 IIC 设备、 SPI 设备等) .dtsi 描述 SOC 级信息(也就是 SOC 有几个 CPU、主频是多少、各个外设控制器信息等)。 Linux 内核中 ARM 架构下有太多垃圾板级信息文件所以才引进设备树。 二、DTS、DTB 和 DTC DTS 是设备树源码文件 DTB 是将DTS 编译以后得到的二进制文件。将.c 文件编译为.o 需要用到 gcc 编译器那么将.dts 编译为.dtb 需要用到 DTC 工具。如果要编译 DTS 文件的话只需要进入到 Linux 源码根目录下然后执行如下命令
cd
cd /linux/atk-mpl/linux/my_linux/linux-5.4.31\make all
#或者
make dtbs# make all命令是编译 Linux 源码中的所有东西包括 uImage.ko 驱动模块以及设备树如果只是编译设备树的话建议使用“make dtbs”命令“make dtbs”会编译选中的所有设备树文件 如果只要编译指定的某个设备树比如 ST 官方编写的“stm32mp157d-ed1.dts”可以输入如下命令
make stm32mp157d-ed1.dtb 每个板子都有一个对应的 DTS 文件如何确定编译哪一个 DTS 文件呢 就拿我们手中的 STM32MP1 这个芯片对应的板子为例打开arch/arm/boot/dts/Makefile
dtb-$(CONFIG_ARCH_STM32) \ # 981 行开始stm32f429-disco.dtb \stm32f469-disco.dtb \stm32f746-disco.dtb \stm32f769-disco.dtb \stm32429i-eval.dtb \stm32746g-eval.dtb \stm32h743i-eval.dtb \stm32h743i-disco.dtb \stm32mp157a-avenger96.dtb \stm32mp157a-dk1.dtb \stm32mp157d-dk1.dtb \stm32mp157c-dk2.dtb \stm32mp157f-dk2.dtb \stm32mp157c-dk2-a7-examples.dtb \stm32mp157c-dk2-m4-examples.dtb \stm32mp157f-dk2-a7-examples.dtb \stm32mp157f-dk2-m4-examples.dtb \stm32mp157a-ed1.dtb \stm32mp157c-ed1.dtb \stm32mp157d-ed1.dtb \stm32mp157f-ed1.dtb \stm32mp157a-ev1.dtb \stm32mp157c-ev1.dtb \stm32mp157d-ev1.dtb \stm32mp157f-ev1.dtb \stm32mp157c-ev1-a7-examples.dtb \stm32mp157c-ev1-m4-examples.dtb \stm32mp157f-ev1-a7-examples.dtb \stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-atk.dtb # 这是之前加的 当选中 STM32MP1 这个 SOC 以后(CONFIG_ARCH_STM32y)所有使用到STM32MP1 这个 SOC 的板子对应的.dts 文件都会被编译为.dtb。如果我们使用 STM32MP1 新做了一个板子只需要新建一个此板子对应的.dts 文件然后将对应的.dtb 文件名添加到 dtb-$( CONFIG_ARCH_STM32)下这样在编译设备树的时候就会将对应的.dts 编译为二进制的.dtb 文件。 在哪用到了.dtb 文件呢其实就在Uboot开启操作系统的时候用到了。 三、DTS 语法 一般情况不会从头到尾写一个 .dts 文件大多时候都是去修改 SOC 厂商提供的 .dts 文件上修改。学习一遍 DTS 语法可以让我们修改 .dts 文件。 1. dtsi 头文件 在设备树中可以引用.h、.dtsi 和 .dts 文件。只是我们在编写设备树头文件的时候最好选择.dtsi 后缀。 前 STM32MP1 系列里有 stm32mp151、 stm32mp153和 stm32mp157 这三款 SOC其中 151 是外设最少的 153 和 157 的外设是在 151 的基础上逐渐增加的。因此 151 就相当于“基类” 153 和 157 是在 151 基础上得到的“派生类”。因此 ST就把最基本的外设资源都写在 stm32mp151.dtsi 文件里。 stm32mp151.dtsi 就是描述 151、 153 和 157 共有的外设信息的。
/ {#address-cells 1;#size-cells 1;cpus {#address-cells 1;#size-cells 0;cpu0: cpu0 {compatible arm,cortex-a7;...nvmem-cell-names part_number;#cooling-cells 2;};};cpu0_opp_table: cpu0-opp-table {compatible operating-points-v2;opp-shared;};...spi2: spi4000b000 {#address-cells 1;#size-cells 0;compatible st,stm32h7-spi;reg 0x4000b000 0x400;interrupts GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH;clocks rcc SPI2_K;resets rcc SPI2_R;dmas dmamux1 39 0x400 0x01,dmamux1 40 0x400 0x01;dma-names rx, tx;power-domains pd_core;status disabled;};
} 顶层节点是根节点/下面包含了两个子节点cpus和spi2。每个节点都使用花括号括起来并包含一系列属性和属性值。在cpus节点中定义了一个子节点cpu0用于描述第一个CPU的配置spi2节点中定义了SPI控制器的配置信息。 2. 设备节点 设备树是采用树形结构来描述板子上的设备信息的文件每个设备都是一个节点叫做设备节点每个节点都通过一些属性信息来描述节点信息 属性就是键—值对每个属性由一个键和一个值组成。以下是缩减后的设备树模板
/ { # / 是根节点每个设备树文件都只有一个根节点。#address-cells 1;#size-cells 1;aliases { # 子节点serial0 uart4;};cpus { # 子节点#address-cells 1;#size-cells 0;# 节点标签label:node-nameunit-addresscpu0: cpu0 { # 引入节点标签是为了可以直接通过 label来访问比如cpu0可以直接用cpu0访问compatible arm,cortex-a7;device_type cpu;reg 0;clocks scmi0_clk CK_SCMI0_MPU;clock-names cpu;operating-points-v2 cpu0_opp_table;nvmem-cells part_number_otp;nvmem-cell-names part_number;#cooling-cells 2;};};soc { # 子节点compatible simple-bus;#address-cells 1;#size-cells 1;interrupt-parent intc;ranges;sram: sram10000000 { # sram是soc的子节点compatible mmio-sram;reg 0x10000000 0x60000;#address-cells 1;#size-cells 1;ranges 0 0x10000000 0x60000;};};
}# 节点格式node-nameunit-address
# 其中 node-name 是节点名字比如uart1表示UART1外设unit-address表示设备地址或寄存器首地址没有地址可以不要比如 cpu0,soc
① 字符串
compatible arm,cortex-a7;
设置compatible 属性为字符串 arm,cortex-a7,。
② 32位无符号整数
reg 0;
设置 reg 属性的值为 0 reg 的值也可以设置为一组值。reg 0 0x123456 100;
③ 字符串列表
compatible st,stm32mp157d-atk, st,stm32mp157;
属性值也可以为字符串列表字符串和字符串之间采用“,”隔开。 3. 标准属性
① compatible 属性 compatible 属性也叫做“兼容性”属性这是非常重要的一个属性 compatible 属性的值是一个字符串列表 compatible 属性用于将设备和驱动绑定起来。字符串列表用于选择设备所要使用的驱动程序 compatible 属性的值格式如下所示
manufacturer,model
# 其中 manufacturer 表示厂商 model 一般是模块对应的驱动名字。 比如 stm32mp15xx-dkx.dtsi中有一个音频设备节点这个节点的音频芯片采用的Cirrus Logic公司出品的cs42l51
compatible cirrus,cs42l51;
# 属性值为“cirrus,cs42l51”其中‘cirrus’表示厂商是 Cirrus Logic“cs42l51”表示驱动模块名字 compatible 也可以多个属性值比如
compatible cirrus,my_cs42l51,cirrus,cs42l51;
# 这个设备首先使用第一个兼容值在 Linux 内核里面查找看看能不能找到与之匹配的驱动文件如果没有找到的话就使用第二个兼容值查以此类推直到查找完 compatible 属性中的所有值。 一般驱动程序文件都会有一个 OF 匹配表此 OF 匹配表保存着一些 compatible 值如果设备节点的 compatible 属性值和 OF 匹配表中的任何一个值相等那么就表示设备可以使用这个驱动。比如在文件cs42l51.c 中有如下内容
const struct of_device_id cs42l51_of_match[] {{ .compatible cirrus,cs42l51, },{ }
};# 数组 cs42l51_of_match 就是 cs42l51.c 这个驱动文件的匹配表此匹配表只有一个匹配值“cirrus,cs42l51”。如果在设备树中有哪个节点的 compatible 属性值与此相等那么这个节点就会使用此驱动文件。 ② model 属性 model 属性值也是一个字符串一般 model 属性描述开发板的名字或者设备模块信息比如
model STMicroelectronics STM32MP157C-DK2 Discovery Board; ③ status 属性 status 属性值也是字符串字符串是设备的状态信息
“okay”表明设备是可操作的。“disabled”表明设备当前是不可操作的但是在未来可以变为可操作的比如热插拔设备插入以后。至于 disabled 的具体含义还要看设备的绑定文档。“fail”表明设备不可操作设备检测到了一系列的错误而且设备也不大可能变得可操作。“fail-sss”含义和“fail”相同后面的 sss 部分是检测到的错误内容。 ④ #address-cells 和#size-cells 属性 这两个属性的值都是无符号 32 位整形 #address-cells 和#size-cells 这两个属性可以用在任何拥有子节点的设备中用于描述子节点的地址信息。 #address-cells 属性值决定了子节点 reg 属性中地址信息所占用的字长(32 位) #size-cells 属性值决定了子节点 reg 属性中长度信息所占的字长(32 位)。 #address-cells 和#size-cells 表明了子节点应该如何编写 reg 属性值一般 reg 属性都是和地址有关的内容和地址相关的信息有两种起始地址和地址长度 reg 属性的格式为
reg address1 length1 address2 length2 address3 length3……# 每个“address length”组合表示一个地址范围其中 address 是起始地址 length 是地址长度 #address-cells 表明 address 这个数据所占用的字长 #size-cells 表明 length 这个数据所占用的字长比如:
cpus {#address-cells 1;#size-cells 0;# 说明 cpus 的子节点 reg 属性中起始地址所占用的字长为 1地址长度所占用的字长为 0cpu0: cpu0 {compatible arm,cortex-a7;device_type cpu;reg 0; # 因为父节点设置了 address-cells和size-cells 因此 addres0没有 length 的值相当于设置了起始地址而没有设置地址长度clocks scmi0_clk CK_SCMI0_MPU;clock-names cpu;operating-points-v2 cpu0_opp_table;nvmem-cells part_number_otp;nvmem-cell-names part_number;#cooling-cells 2;};
};scmi_sram: sram2ffff000 {compatible mmio-sram;reg 0x2ffff000 0x1000;#address-cells 1;#size-cells 1;ranges 0 0x2ffff000 0x1000;scmi0_shm: scmi_shm0 {reg 0 0x80; # 设置了起始地址为0x0地址长度为 0x80};
}; ⑤ reg 属性 reg 属性的值一般是(address length) 。reg 属性一般用于描述设备地址空间资源信息或者设备地址信息比如某个外设的寄存器地址范围信息或者IIC期间的设备地址等。 ⑥ ranges 属性 ranges属性值可以为空或者按照(child-bus-address,parent-bus-address,length)格式编写的数字矩阵 ranges 是一个地址映射/转换表 ranges 属性每个项目由子地址、父地址和地址空间长度这三部分组成 child-bus-address子总线地址空间的物理地址由父节点的#address-cells 确定此物理地址所占用的字长。 parent-bus-address 父总线地址空间的物理地址同样由父节点的#address-cells 确定此物理地址所占用的字长。 length 子地址空间的长度由父节点的#size-cells 确定此地址长度所占用的字长。 如果 ranges 属性值为空值说明子地址空间和父地址空间完全相同不需要进行地址转换对于我们所使用的 stm32mp157 来说子地址空间和父地址空间完全相同因此会在stm32mp151.dtsi 中找到大量的值为空的 ranges 属性。
soc {compatible simple-bus;#address-cells 1;#size-cells 1;interrupt-parent intc;ranges 0 0x10000000 0x100000;
# 节点soc定义了ranges属性此属性值指定了一个 1024KB(0x100000)的地址范围子地址空间的物理起始地址为 0父地址空间的物理起始地址为 0x10000000sram: sram10000000 { compatible mmio-sram;reg 0x0 0x60000; # reg定义了sram设备起始地址为0寄存器长度为0x60000经过地址转换sram设备可以从0x10000000开始读写操作#address-cells 1;#size-cells 1;ranges 0 0x10000000 0x60000;};
}; 4. 根节点下的 compatible 属性 每个节点都有 compatible 属性根节点“/”也不例外在我们新建的 stm32mp157d-atk.dts文件中根节点的 compatible 属性内容如下
/ {model STMicroelectronics STM32MP157C-DK2 Discovery Board;compatible st,stm32mp157d-atk, st,stm32mp157; # 匹配Linux内核中的驱动程序# 描述设备 描述设备使用的SOC....
}; 通过根节点的 compatible 属性可以知道我们所使用的设备一般第一个值描述了所使用的硬件设备名字比如这里使用的是“stm32mp157d-atk”这个设备第二个值描述了设备所使用的 SOC比如这里使用的是“stm32mp157”这颗 SOC Linux内核会通过根节点的 compoatible 属性查看是否支持此设备如果支持的话设备就会启动 Linux内核。 当 Linux 内核引入设备树后就是换成 DT_MACHINE_START。
static const char *const stm32_compat[] __initconst {st,stm32f429,st,stm32f469,st,stm32f746,st,stm32f769,st,stm32h743,st,stm32mp151,st,stm32mp153,st,stm32mp157,NULL
};# st,stm32mp157”与 stm32_compat 中的“ st,stm32mp157”匹配因此 STM32MP157 开发板可以正常启动 Linux 内核。 只要某个设备(板子)根节点“/”的compatible 属性值与 stm32_compat 表中的任何一个值相等那么就表示 Linux 内核支持此设备。 stm32mp157d-atk.dts 中根节点的 compatible 属性值如下
compatible st,stm32mp157d-atk, st,stm32mp157; 总体来说
内核启动时加载设备树。设备树描述了硬件设备的结构和特性包括各个设备节点及其属性。
内核根据设备树中的根节点开始解析。根节点是设备树中的顶层节点通常以/表示。
内核读取根节点的compatible属性。该属性包含了设备的类型和兼容性信息。
内核遍历已加载的驱动程序列表尝试与根节点的compatible属性进行匹配。
如果找到匹配的驱动程序内核通过调用驱动程序中的初始化函数来初始化设备。
驱动程序的初始化函数会读取设备树中与设备相关的属性进行设备的初始化和配置。 5. 向节点追加或修改内容 什么时候需要追加和修改内容呢如果硬件被修改了那么我们需要同步修改设备树文件。假设现在有个六轴芯片fxls8471 fxls8471 要接到 STM32MP157D-ATK 开发板的 I2C1 接口上那么相当于需要在 i2c1这个节点上添加一个 fxls8471 子节点。先看一下 I2C1 接口对应的节点打开文件 stm32mp157.dtsi 文件
i2c1: i2c40012000 {compatible st,stm32mp15-i2c;reg 0x40012000 0x400;interrupt-names event, error;interrupts-extended exti 21 IRQ_TYPE_LEVEL_HIGH,intc GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH;clocks rcc I2C1_K;resets rcc I2C1_R;#address-cells 1;#size-cells 0;dmas dmamux1 33 0x400 0x80000001,dmamux1 34 0x400 0x80000001;dma-names rx, tx;power-domains pd_core;st,syscfg-fmp syscfg 0x4 0x1;wakeup-source;status disabled;};
}; 打开 stm32mp157d-atk.dts在根节点后添加以下代码
/*
追加的方法:
i2c1{// 要追加或修改的内容
}
*/i2c1 {pinctrl-names default, sleep; // 引脚控制名称pinctrl-0 i2c1_pins_b; // 默认引脚控制配置pinctrl-1 i2c1_pins_sleep_b; // 睡眠模式下的引脚控制配置status okay;clock-frequency 100000; // I2C时钟频率fxls84711e {compatible fsl,fxls8471;reg 0x1e; // 指定设备寄存器的地址position 0; interrupt-parent gpioh; // 定义中断信号的父节点interrupts 6 IRQ_TYPE_EDGE_FALLING; // 指定中断引脚为6并且为下降沿触发};
}; 最重要的其实就是通过 label 来访问节点然后直接在里面编写要追加或者修改的内容。 四、创建小型模板设备树 编写的这个设备树没有意义只让我们去掌握设备树的语法。 在编写设备树之前要先定义一个设备我们就以 STM32MP157 这个 SOC 为例我们需要在设备树里面描述的内容如下 1、这个芯片是由两个 Cortex-A7 架构的 32 位 CPU 和 Cortex-M4 组成。 2、STM32MP157 内部 sram起始地址为 0x10000000大小为 384KB(0x60000)。 3、STM32MP157 内部 timers6起始地址为 0x40004000大小为 25.6KB(0x400)。 4、STM32MP157 内部 spi2起始地址为 0x4000b000大小为 25.6KB(0x400)。 5、STM32MP157 内部 usart2起始地址为 0x4000e000大小为 25.6KB(0x400)。 6、STM32MP157 内部 i2c1起始地址为 0x40012000大小为 25.6KB(0x400)。 首先在 /linux/atk-mpl/linux/my_linux/linux-5.4.31/arch/arm/boot/dts 路径下去修改或者创造.dts/.dtsi 文件一般情况都是在dts目录下。首先搭建一个仅含有根节点“/”的基础的框架并创建一个 myfirstdevicetree.dts输入以下内容
/ {compatible st,stm32mp157d-atk, st,stm32mp157;
}; 1. 添加 cpus 节点
/* 此节点用于描述 SOC 内部的所有 CPU因为 STM32MP157 有两个 CPU所以在 cpus 下添加两个子节点分别为 cpu0 和 cpu1。*//* cpu 节点 */cpus {#address-cells 1;#size-cells 0;/* CPU0 节点 */cpu0:cpu0 {compatible arm,cortex-a7;device_type cpu;reg 0;};/* CPU1 节点 */ cpu1:cpu1 {compatible arm,cortex-a7;device_type cpu;reg 1;};} 2. 添加 soc 节点 像 uart iic 控制器等等这些都属于 SOC 内部外设因此一般会创建一个叫做 soc 的父节点来管理这些 SOC 内部外设的子节点
/* soc 节点 */soc {compatible simple-bus;#address-cells 1;#size-cells 1; // soc 子节点的 reg 属性中起始地占用一个字长地址空间长度也占用一个字长ranges; // ranges 属性为空说明子空间和父空间地址范围相同}; 3. 添加 sram 节点 sram 是 STM32MP157 内部 RAM M4 内核会用到 SRAM4。 sram是soc节点的子节点。sram起始地址为0x10000000大小为384KB
/* soc 节点 */soc {compatible simple-bus;#address-cells 1;#size-cells 1;ranges;/* sram 节点 */sram: sram10000000 { // 0x10000000 就是 sram 的起始地址compatible mmio-sram;reg 0x10000000 0x60000 // sram 内存的起始地址为 0x10000000大小为 0x60000ranges 0 0x10000000 0x60000;};}; 4. 添加 timers6、spi2、usart2 和 i2c1 节点
/* soc 节点 */soc {compatible simple-bus;#address-cells 1;#size-cells 1;ranges;/* sram 节点 */sram: sram10000000 {compatible mmio-sram;reg 0x10000000 0x60000ranges 0 0x10000000 0x60000;};/* timers6 节点 */timers6: timer40004000 {#address-cells 1;#size-cells 0;compatible st,stm32-timers;reg 0x40004000 0x400;};/* spi2 节点 */spi2: spi4000b000 {#address-cells 1;#size-cells 0;compatible st,stm32h7-spi;reg 0x4000b000 0x400;};/* usart2 节点 */usart2: serial4000e000 {compatible st,stm32h7-uart;reg 0x4000e000 0x400;};/* i2c1 节点 */i2c1: i2c40012000 {compatible st,stm32mp15-i2c;reg 0x40012000 0x400;};}; 五、设备树在系统中的体现 Linux 内核启动的时候会解析设备树中各个节点的信息并且在根文件系统的/proc/devicetree 目录下根据节点名字创建不同文件夹 1.根节点 / 各个属性 “#address-cells”、“#size-cells”、“compatible”、“model”和“name”这 5 个文件它们在设备树中就是根节点的5 个属性用命令 cat 可以看 model 和 compatible 文件内容 其实这些都是 / 根节点 model 和 compatible 属性值。 2.根节点 / 各子节点 进入/proc/device-tree/soc 目录中就可以看到 soc 节点的所有子节点 总结无论根节点还是子节点白色的都是该节点的属性蓝色的都是该节点的子节点进入soc节点也是一样。 六、特殊节点 在根节点“/”中有两个特殊的子节点 aliases 和 chosen。 1. aliases 子节点 打开 stm32mp157d-atk.dts 文件 aliases 节点内容如下
aliases {serial0 uart4;
}; aliases 节点的主要功能就是定义别名定义别名的目的就是为了方便访问节点。不过我们一般会在节点命名的时候会加上 label然后通过 label 来访问节点这样也很方便而且设备树里面大量的使用label 的形式来访问节点。 2. chosen 子节点 chosen 并不是一个真实的设备 chosen 节点主要是为了 uboot 向 Linux 内核传递数据重点是 bootargs 参数。一般.dts 文件中 chosen 节点通常为空或者内容很少。进入 chosen 节点发现 bootargs文件它内容为 这个跟我们在 Uboot 设置 bootargs 一样的值。 uboot 在启动 Linux 内核的时候会将 bootargs 的值传递给 Linux内核 bootargs 会作为 Linux 内核的命令行参数 Linux 内核启动的时候会打印出命令行参数(也就是 uboot 传递进来的 bootargs 的值) 。其实就是 uboot 中的 fdt_chosen 函数在设备树的 chosen 节点中加入了 bootargs 属性并且还设置了 bootargs 属性值。 七、设备树常用 OF 操作函数 设备树描述了设备的详细信息这些信息包括数字类型的、字符串类型的、数组类型的我们在编写驱动的时候需要获取到这些信息。比如设备树使用 reg 属性描述了某个外设的寄存器地址为 0X02005482长度为 0X400我们在编写驱动的时候需要获取到 reg 属性的0X02005482 和 0X400 这两个值然后初始化外设。 Linux 内核给我们提供了一系列的函数来获取设备树中的节点或者属性信息这一系列的函数都有一个统一的前缀“of_”所以在很多资料里面也被叫做 OF 函数。 1. 查找节点的 OF 函数 设备都是以节点的形式“挂”到设备树上的因此要想获取这个设备的其他属性信息必须先获取到这个设备的节点。 ① of_find_node_by_name 函数
/** description : 通过节点名字查找指定的节点* param - from : 开始查找的节点如果为 NULL 表示从根节点开始查找整个设备树* param - name : 查找的节点名字* return : 找到的节点如果为 NULL 表示查找失败*/
struct device_node *of_find_node_by_name(struct device_node *from, const char *name); ② of_find_node_by_type 函数
/** description : 通过 device_type 属性查找指定的节点* param - from : 开始查找的节点如果为 NULL 表示从根节点开始查找整个设备树* param - type : 查找的节点对应的 type 字符串也就是 device_type 属性值* return : 找到的节点如果为 NULL 表示查找失败*/
struct device_node *of_find_node_by_type(struct device_node *from, const char *type); ③ of_find_compatible_node 函数
/** description : 根据 device_type 和 compatible 这两个属性查找指定的节点* param - from : 开始查找的节点如果为 NULL 表示从根节点开始查找整个设备树* param - type : 查找的节点对应的 type 字符串也就是 device_type 属性值,可以为 NULL表示忽略掉 device_type 属性* param - compatible : 要查找的节点所对应的 compatible 属性列表* return : 找到的节点如果为 NULL 表示查找失败*/
struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compatible) ④ of_find_matching_node_and_match 函数
/** description : 通过 of_device_id 匹配表来查找指定的节点* param - from : 开始查找的节点如果为 NULL 表示从根节点开始查找整个设备树* param - matches : of_device_id 匹配表也就是在此匹配表里面查找节点* param - match : 找到的匹配的 of_device_id* return : 找到的节点如果为 NULL 表示查找失败*/
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); ⑤ of_find_node_by_path 函数
/** description : 通过路径来查找指定的节点* param - path : 带有全路径的节点名可以使用节点的别名比如“/backlight”就是 backlight 这个节点的全路径* return : 找到的节点如果为 NULL 表示查找失败*/
inline struct device_node *of_find_node_by_path(const char *path); 2. 查找父/子节点的 OF 函数 查找节点对应的父节点或者子节点的 OF 函数。 ① of_get_parent 函数
/** description : 获取指定节点的父节点(如果有父节点的话)* param - node : 要查找的父节点的节点* return : 找到的父节点*/
struct device_node *of_get_parent(const struct device_node *node); ② of_get_next_child 函数
/** description : 迭代的查找子节点* param - node : 父节点* param - prev : 前一个子节点也就是从哪一个子节点开始迭代的查找下一个子节点。可以设置为NULL表示从第一个子节点开始* return : 找到的下一个子节点*/
struct device_node *of_get_next_child(const struct device_node *node,struct device_node *prev); 3. 提取属性值的 OF 函数 节点的属性信息里面保存了驱动所需要的内容因此对于属性值的提取非常重要 Linux 内核中使用结构体 property 表示属性。 ① of_find_property 函数
/** description : 查找指定的属性* param - np : 设备节点* param - name : 设备名字* param - lenp : 属性值的字节数* return : 找到的属性*/
property *of_find_property(const struct device_node *np,const char *name,int *lenp); ② of_property_count_elems_of_size 函数
/** description : 获取属性中元素的数量比如 reg 属性值是一个数组那么使用此函数可以获取到这个数组的大小* param - np : 设备节点* param - proname : 需要统计元素数量的属性名字* param - elem_size : 元素长度* return : 得到的属性元素数量*/
int of_property_count_elems_of_size(const struct device_node *np,const char *propname,int elem_size); ③ of_property_read_u32_index 函数
/** description : 从属性中获取指定标号的 u32 类型数据值(无符号 32位)比如某个属性有多个 u32 类型的值* param - np : 设备节点* param - proname : 要读取的属性名字* param - index : 要读取的值标号* param - out_value : 读取到的值* return : 0 读取成功负值读取失败 -EINVAL 表示属性不存在 -ENODATA 表示没有要读取的数据 -EOVERFLOW 表示属性值列表太小*/
int of_property_read_u32_index(const struct device_node *np,const char *propname,u32 index,u32 *out_value); ④ of_property_read_u8_array 函数 (还有u16/u32/u64)
/** description : 读取属性中 u8、 u16、 u32 和 u64 类型的数组数据比如大多数的 reg 属性都是数组数据可以使用这 4 个函数一次读取出 reg 属性中的所有数据* param - np : 设备节点* param - proname : 要读取的属性名字* param - out_value : 读取到的数组值分别为 u8、 u16、 u32 和 u64* param - sz : 要读取的数组元素数量* return : 0 读取成功负值读取失败 -EINVAL 表示属性不存在 -ENODATA 表示没有要读取的数据 -EOVERFLOW 表示属性值列表太小*/
int of_property_read_u8_array(const struct device_node *np,const char *propname,u8 *out_values,size_t sz); ⑤ of_property_read_u8 函数 (还有u16/u32/u64)
/** description : 有些属性只有一个整形值这四个函数就是用于读取这种只有一个整形值的属性* param - np : 设备节点* param - proname : 要读取的属性名字* param - out_value : 读取到的数组值* return : 0 读取成功负值读取失败 -EINVAL 表示属性不存在 -ENODATA 表示没有要读取的数据 -EOVERFLOW 表示属性值列表太小*/
int of_property_read_u8(const struct device_node *np,const char *propname,u8 *out_value); ⑥ of_property_read_string 函数
/** description : 用于读取属性中字符串值* param - np : 设备节点* param - proname : 要读取的属性名字* param - out_string : 读取到的字符串值* return : 0读取成功负值读取失败*/
int of_property_read_string(struct device_node *np,const char *propname,const char **out_string); ⑦ of_n_addr_cells 函数
/** description : 用于获取#address-cells 属性值* param - np : 设备节点* return : 获取到的#address-cells 属性值*/
int of_n_addr_cells(struct device_node *np); ⑧ of_n_size_cells 函数
/** description : 用于获取#size-cells 属性值* param - np : 设备节点* return : 获取到的#size-cells 属性值*/
int of_n_size_cells(struct device_node *np); 4. 其他常用的 OF 函数
① of_device_is_compatible 函数
/** description : 用于查看节点的 compatible 属性是否有包含 compat 指定的字符串也就是检查设备节点的兼容性* param - device : 设备节点* param - compat : 要查看的字符串* return : 点的 compatible 属性中不包含 compat 指定的字符串正数节点的 compatible属性中包含 compat 指定的字符串*/
int of_device_is_compatible(const struct device_node *device,const char *compat); ② of_get_address 函数
/** description : 用于获取地址相关属性主要是“reg”或者“assigned-addresses”属性值* param - dev : 设备节点* param - index : 要读取的地址标号* param - size : 地址长度* param - flags : 参数比如 IORESOURCE_IO、 IORESOURCE_MEM 等* return : 读取到的地址数据首地址为 NULL 的话表示读取失败*/
const __be32 *of_get_address(struct device_node *dev,int index,u64 *size,unsigned int *flags); ③ of_translate_address 函数
/** description : 将从设备树读取到的地址转换为物理地址* param - dev : 设备节点* param - in_addr : 要转换的地址* return : 得到的物理地址如果为 OF_BAD_ADDR 的话表示转换失败*/
u64 of_translate_address(struct device_node *dev,const __be32 *addr); ④ of_address_to_resource 函数
/** description : 将 reg 属性值然后将其转换为 resource 结构体类型* param - dev : 设备节点* param - index : 地址资源标号* param - r : 得到的 resource 类型的资源值* return : 0成功负值失败*/
int of_address_to_resource(struct device_node *dev,int index,struct resource *r);struct resource {resource_size_t start;resource_size_t end;const char *name;unsigned long flags;unsigned long desc;struct resource *parent, *sibling, *child;
};
/* 对于 32 位的 SOC 来说 resource_size_t 是 u32 类型的。其中 start 表示开始地址 end 表示
结束地址 name 是这个资源的名字 flags 是资源标志位一般表示资源类型 */ ⑤ of_iomap 函数 of_iomap 函数用于直接内存映射在采用设备树以后大部分的驱动都使用 of_iomap 函数。of_iomap 函数本质上也是将 reg 属性中地址信息转换为虚拟地址如果 reg 属性有多段的话可以通过 index 参数指定要完成内存映射的是哪一段。
/** description : 用于直接内存映射* param - np : 设备节点* param - index : reg 属性中要完成内存映射的段如果 reg 属性只有一段的话 index 就设置为 0* return : 经过内存映射后的虚拟内存首地址如果为 NULL 的话表示内存映射失败*/
void __iomem *of_iomap(struct device_node *np,int index); 设备树重点设备树语法、设备树的 OF 操作函数。
总结设备树就是查询板子信息的我们驱动开发所需要的信息都会在上面比如设备名字、设备地址之类的都在里面简而言之设备树其实就是类似于查询手册一样。