目录
说明:此博客为我自己对正点原子提供的资料学习的记录,内容大部份来始于正点原子资料,你们也可以去下载正点原子的官方资料学习,内容丰富。侵权删。1.哪些是设备树
设备树(DeviceTree),将这个词分开就是“设备”和“树”,描述设备树的文件称作DTS(DeviceTreeSource),这个DTS文件采用树状结构描述板级设备linux移植时需要编译设备树文件吗,也就是开发板上的设备信息,例如CPU数目、内存基地址、IIC插口上接了什么设备、SPI插口上接了什么设备等等。
这是正点原子左老师画的图很形象。树的主干就是系统总线,IIC控制器、GPIO控制器、SPI控制器等都是接到系统主线上的分支。IIC控制器有分为IIC1和IIC2两种,其中IIC1上接了FT5206和AT2424CC0202这两个IIC设备,IIC2上只接了MPU6050这个设备。DTS文件的主要功能就是依照图示的结构来描述板子上的设备信息。
2.DTS、DTB和DTC
设备树源文件扩充名为.dts,而且我们移植Linux的时侯应使用.dtb文件。
DTS是设备树源码文件。
DTB是将DTS编译之后得到的二补码文件。
DTC就是将DTS编译成DTB的工具。
3.DTS句型3.1dtsi头文件
和C语言一样,设备树也支持头文件,设备树的头文件扩充名为.dtsi。
#include "imx6ull.dtsi"
在.dts设备树文件中,可以通过“#include”来引用.h、.dtsi和.dts文件。只是,我们在编撰设备枝杈文件的时侯最好选择.dtsi后缀。
通常.dtsi文件用于描述SOC的内部外设信息,例如CPU构架、主频、外设寄存器地址范围,例如UART、IIC等等。
3.2设备节点
设备树是采用树状结构来描述板子上的设备信息的文件,每位设备都是一个节点,称作设备节点,每位节点都通过一些属性信息来描述节点信息,属性就是键—值对。
“/”是根节点,每位设备树文件只有一个根节点。在设备树中节点命名格式如下:node-name@unit-address
其中“node-name”是节点名字,为ASCII字符串,节点名字应当才能清晰的描述出节点的功能,例如“uart1”就表示这个节点是UART1外设。“unit-address”一般表示设备的地址或寄存器首地址,假如某个节点没有地址或则寄存器的话“unit-address”可以不要,例如“cpu@0”、“interrupt-controller@00a01000”。
而且我们也会见到:cpu0:cpu@0这样的方式。上述命令并不是“node-name@unit-address”这样的格式,而是用“:”隔开成了两部份,“:”前面的是节点标签(label),“:”后面的才是节点名字,格式如下所示:
label:node-name@unit-address
引入label的目的就是为了便捷访问节点,可以直接通过&label来访问这个节点,例如通过&cpu0就可以访问“cpu@0”这个节点,而不须要输入完整的节点名字。
3.3标准属性
每位节点都有不同属性,不同的属性又有不同的内容,属性都是通配符对,值可以为空或任意的字节流。设备树源码中常用的几种数据方式如下所示:
①、字符串
compatible="arm,cortex-a7";
上述代码设置compatible属性的值为字符串“arm,cortex-a7”。
②、32位无符号整数
reg=;
上述代码设置reg属性的值为0,reg的值也可以设置为一组值,例如:
reg=;
③、字符串列表
属性值也可以为字符串列表,字符串和字符串之间采用“,”隔开,如下所示:
compatible="fsl,imx6ull-gpmi-nand","fsl,imx6ul-gpmi-nand";
上述代码设置属性compatible的值为“fsl,imx6ull-gpmi-nand”和“fsl,imx6ul-gpmi-nand”
3.3.1compatibe属性
compatible属性也称作“兼容性”属性,这是十分重要的一个属性!compatible属性的值是一个字符串列表,compatible属性用于将设备和驱动绑定上去。字符串列表用于选择设备所要使用的驱动程序,compatible属性的值格式如下所示:
"manufacturer,model"
其中manufacturer表示厂商,model通常是模块对应的驱动名子。例如音频设备节点上linux移植时需要编译设备树文件吗,I.MX6U-ALPHA开发板上的音频芯片采用的欧胜(WOLFSON)出品的WM8960,sound节点的compatible属性值如下:
compatible="fsl,imx6ul-evk-wm8960","fsl,imx-audio-wm8960";
属性值有两个,分别为“fsl,imx6ul-evk-wm8960”和“fsl,imx-audio-wm8960”,其中“fsl”表示厂商是飞思卡尔linux手机软件,“imx6ul-evk-wm8960”和“imx-audio-wm8960”表示驱动模块名子。
这个设备首先使用第一个兼容值在Linux内核上面查找,瞧瞧能不能找到与之匹配的驱动文件,假如没有找到的话就使用第二个兼容值查。
3.3.2model属性
model属性值也是一个字符串,通常model属性描述设备模块信息,例如名子哪些的,例如:
model="wm8960-audio";
3.3.3status属性
status属性看名子就晓得是和设备状态有关的,status属性值也是字符串,字符串是设备的状态信息,可选的状态如表所示:
3.3.4#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=
每位“addresslength”组合表示一个地址范围,其中address是起始地址,length是地址宽度,#address-cells表明address这个数据所占用的字长,#size-cells表明length这个数据所占用的字长,例如:
spi4 {
compatible = "spi-gpio";
#address-cells = <1>;
#size-cells = <0>;
gpio_spi: gpio_spi@0 {
compatible = "fairchild,74hc595";
reg = <0>;
};
};
/*因为#address-cells和#size-cells都是描述reg属性的,所以根据reg的值来看,
其address为0,无length,所以#address-cells值为1,#size-cells为0*/
aips3: aips-bus@02200000 {
compatible = "fsl,aips-bus", "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
dcp: dcp@02280000 {
compatible = "fsl,imx6sl-dcp";
reg = <0x02280000 0x4000>;
};
};
/*因为#address-cells和#size-cells都是描述reg属性的,所以根据reg的值来看,
其address为0x02280000,length为0x4000,所以#address-cells值为1,#size-cells为1*/
3.3.5reg属性
reg属性上面早已提及过了,reg属性的值通常是(address,length)对。reg属性通常用于描述设备地址空间资源信息,通常都是某个外设的寄存器地址范围信息,例如在imx6ull.dtsi中有如下内容:
aips3: aips-bus@02200000 {
compatible = "fsl,aips-bus", "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
dcp: dcp@02280000 {
compatible = "fsl,imx6sl-dcp";
reg = <0x02280000 0x4000>;
};
};
每位“addresslength”组合表示一个地址范围,其中address是起始地址,length是地址宽度,所以dcp寄存器的范围是0x2280000到0x2284000。
3.3.6ranges属性
ranges属性值可以为空或则根据(child-bus-address,parent-bus-address,length)格式编撰的数字矩阵,ranges是一个地址映射/转换表,ranges属性每位项目由子地址、父地址和地址空间厚度这三部份组成:
child-bus-address:子总线地址空间的数学地址,由父节点的#address-cells确定此化学地址所占用的字长。
parent-bus-address:父总线地址空间的数学地址,同样由父节点的#address-cells确定此化学地址所占用的字长。
length:子地址空间的宽度,由父节点的#size-cells确定此地址宽度所占用的字长。
假如ranges属性值为空值,说明子地址空间和父地址空间完全相同,不须要进行地址转换,
对于我们所使用的I.MX6ULL来说,子地址空间和父地址空间完全相同,因而会在imx6ull.dtsi中找到大量的值为空的ranges属性。
3.3.7name属性
name属性值为字符串,name属性用于记录节点名字linux 软件,name属性早已被弃用,不推荐使用name属性,一些老的设备树文件可能会使用此属性。
3.3.8device_type属性
device_type属性值为字符串,IEEE1275会用到此属性,用于描述设备的FCode,而且设备树没有FCode,所以此属性也被抛弃了。此属性只能用于cpu节点或则memory节点。
3.4根节点compatible属性
可以看出,compatible有两个值:“fsl,imx6ull-14x14-evk”和“fsl,imx6ull”。上面我们说了,设备节点的compatible属性值是为了匹配Linux内核中的驱动程序,这么根节点中的compatible属性是为了做哪些工作的?通过根节点的compatible属性可以晓得我们所使用的设备,通常第一个值描述了所使用的硬件设备名子,例如这儿使用的是“imx6ull-14x14-evk”这个设备,第二个值描述了设备所使用的SOC,例如这儿使用的是“imx6ull”这颗SOC。Linux内核会通过根节点的compoatible属性查看是否支持此设备,假如支持的话设备都会启动Linux内核。接出来我们就来学习一下Linux内核在使用设备树前后是怎样判定是否支持某款设备的。
3.4.1没使用设备树设备匹配方式
在没有使用设备树曾经,uboot会向Linux内核传递一个称作machineid的值,machineid也就是设备ID,告诉Linux内核自己是个哪些设备,瞧瞧Linux内核是否支持。Linux内核是支持好多设备的,针对每一个设备(板子),Linux内核都用MACHINE_START和MACHINE_END来定义一个machine_desc结构体来描述这个设备。
3.4.1使用设备树设备匹配方式
当Linux内核引入设备树以后就不再使用MACHINE_START了,而是换为了DT_MACHINE_START。DT_MACHINE_START和MACHINE_START基本相同,只是.nr的设置不同,在DT_MACHINE_START上面直接将.nr设置为~0。说明引入设备树之后不会再按照machineid来检测Linux内核是否支持某个设备了。
machine_desc结构体中有个.dt_compat成员变量,此成员变量保存着本设备兼容属性,示例代码43.3.4.5中设置.dt_compat=imx6ul_dt_compat,imx6ul_dt_compat表上面有"fsl,imx6ul"和"fsl,imx6ull"这两个兼容值。只要某个设备(板子)根节点“/”的compatible属性值与imx6ul_dt_compat表中的任何一个值相等,这么就表示Linux内核支持此设备。在3.4中说到,imx6ull-alientek-emmc.dts中根节点的compatible属性值有“fsl,imx6ull”。
4.设备树在系统中的彰显
Linux内核启动的时侯会解析设备树中各个节点的信息,而且在根文件系统的/proc/device-tree目录下按照节点名字创建不同文件夹。
5.设备树常用的OF操作函数5.1查找节点的OF函数(1)of_find_node_by_name函数
of_find_node_by_name函数通过节点名字查找指定的节点,函数原型下:
struct device_node *of_find_node_by_name(struct device_node *from,
const char *name);
函数参数和返回值涵义如下:
from:开始查找的节点,倘若为NULL表示从根节点开始查找整个设备树。
name:要查找的节点名字。
返回值:找到的节点,倘若为NULL表示查找失败。
(2)of_find_node_by_type函数
of_find_node_by_type函数通过device_type属性查找指定的节点,函数原型如下:
struct device_node *of_find_node_by_type(struct device_node *from, const char *type)
函数参数和返回值涵义如下:
from:开始查找的节点,倘若为NULL表示从根节点开始查找整个设备树。
type:要查找的节点对应的type字符串,也就是device_type属性值。
返回值:找到的节点,倘若为NULL表示查找失败。
(3)of_find_compatible_node函数
of_find_compatible_node函数依据device_type和compatible这两个属性查找指定的节点,函数原型如下:
struct device_node *of_find_compatible_node(struct device_node *from,
const char *type,
const char *compatible)
函数参数和返回值涵义如下:
from:开始查找的节点,倘若为NULL表示从根节点开始查找整个设备树。
type:要查找的节点对应的type字符串,也就是device_type属性值,可以为NULL,表示忽视掉device_type属性。
compatible:要查找的节点所对应的compatible属性列表。
返回值:找到的节点,倘若为NULL表示查找失败
(4)of_find_matching_node_and_match函数
of_find_matching_node_and_match函数通过of_device_id匹配表来查找指定的节点,函数原型如下:
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)
函数参数和返回值涵义如下:
from:开始查找的节点,倘若为NULL表示从根节点开始查找整个设备树。
matches:of_device_id匹配表,也就是在此匹配表上面查找节点。
match:找到的匹配的of_device_id。
返回值:找到的节点,倘若为NULL表示查找失败
(5)of_find_node_by_path函数
of_find_node_by_path函数通过路径来查找指定的节点,函数原型如下:
inline struct device_node *of_find_node_by_path(const char *path)
函数参数和返回值涵义如下:
path:带有全路径的节点名,可以使用节点的别称,例如“/backlight”就是backlight这个节点的全路径。
返回值:找到的节点,倘若为NULL表示查找失败
其实:学习过stm32以后,我认为配置设备树如同是在cubeMx上面配置一样,都是配置一些须要用到的外设,gpio、串口等,配置她们的电气属性;不同的是cubeMx是图形化界面,一些寄存器值可以依据选择的芯片和配置的电气属性手动生成,便捷许多;并且在设备树中就得自己查看芯片指南,而且似乎有一个专门配置的工具,还没去了解。