一、什么是设备树
在没有使用设备树的时候,使用的是c文件,mach-xxx文件夹内存储的c源码。
DTS是采用树形结构描述版及设备的文件。将描述扳级信息的文件与linux内核分离出来。dts描述扳级信息,dtsi描述SOC级信息。
设备树有DTS,DTB,DTC。DTS是源码文件,DTB是编译的二进制文件,DTC是编译器。
1、编译dts
编译所有的dts文件。
make dtbs
编译指定的dts文件。
make imx6ull-emmc.dtb
或者在arch/arm/boot/dts目录下的Makefile中有说明编译那些文件,宏条件在defconfig中设置过了,所以才会编译很多dts文件。
二、DTS语法
详细的语法规则请参考《 Devicetree SpecificationV0.2.pdf 》和《Power_ePAPR_APPROVED_v1.12.pdf》这两份文档。
dts文件首先是文件层,使用如下,代表一个dts文件的开始
/dts-v1/;
紧接着,dts文件会#include包含dtsi文件(soc的信息),层层包含着的dtsi文件中都有一个根节点,这些根节点都是代表同一个根节点。
其次,通过一个这样的结构,代表根节点的声明。根节点中可以放入属性,compatible与model。接着放入其他一级子节点。
/ {
/*skelection.dtsi中包含的属性的文件*/
#address-cells = <1>;
#size-cells = <1>;
chosen { };
aliases { };
memory { device_type = "memory"; reg = <0 0>; };
/*imx6ull-14x14-emmc.dtsi文件*/
model = "Freescale i.MX6 ULL 14x14 EVK Board";
compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";
chosen {
};
memory {
};
backlight {
};
pxp_v4l2 {
};
/*Imx6ull.dtsi文件*/
aliases{};
cpus{};
intc: interrupt-controller@00a01000 {};
clocks {};
soc{};
};
在根节点外有一些追加的结点。其中追加使用的是标签,可以通过标签找到主要的结点。
&Lable{
};
1.设备结点名称信息创建
<结点标签>:<结点名字>。Lable可以访问结点。
Label:<node-name>@<name-address>
2.结点中的信息
①字符串compatible
用于描述兼容性的,与驱动程序进行匹配,如果驱动程序中也有compatible属性一致就可以进行匹配,它可以跟多个兼容字符串属性。这个字符串在dts中有一份,在mach-xxx.c中的源码也有一份,当能匹配到的时候,就代表该设备结点开发版可以兼容的,如果不兼容,那么该结点使用不了。
compatible="abc";
②32位无符号整数reg
reg 属性一般用于描述设备地址空间资源信息,一般都是某个外设的寄存器地址范围信息。描述外设的地址的其实位置+长度。
reg由父节点的address-cells ,size-cells决定的。一般对于我们的开发版而言,是不会使用#address-cells= <2>来表示的,这是由于虚拟内存空间大小决定的。
/*Example 2*/
/{
#address-cells = <1>;//表示用一个32位的数来描述地址
#size-cells = <1>;//表示用0个32位的数来描述该地址的大小
node{
reg=<0x02409000 , 0x4000>;
};
};
/*Example 2*/
/ {
#address-cells = <0x2>; //使用2个32来描述address。
#size-cells = <0x1>; //使用1个32来描述size。
memory {
reg = <0x90000000 00000000 0x800000>;
// 0x90000000 00000000 是存取 memory 的 address
// 0x800000 是 memory 的 size。
};
}
③字符串列表
compatiable="zbc","fts";
④ranges属性
是一个矩阵表,也可以不填。
矩阵表内容分别:子总线地址空间物理地址,副总线地址空间物理地址,子地址空间长度。
arm中一般不使用该属性。
⑤model , status
model也是。当使能一个外设后,status会使用ok,挂在根文件系统后,就会能看到被使能的结点。
⑥address-cells ,size-cells
该属性决定子节点的reg属性。
⑦特殊的属性
三、dtsi文件简要分析
我们在参数手册中的ARM Paltform MEMORY MAP里面有映射外设地址。
比如说下图所示,显示了系统地址内存映射信息,在根据如下的外设显示,去DTSI文件中找到相应的代码,可以确定是SOC的信息。
soc {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
interrupt-parent = <&gpc>;
ranges;
......
ocrams: sram@00900000 {
compatible = "fsl,lpm-sram";
reg = <0x00900000 0x4000>;
};
四、设备树的体现
挂载根文件系统以后可以查看设备树结点信息。
在/proc/device-tree目录下存放着一级结点的信息。
内核启动以后会解析设备树文件,然后呈现在/proc/device-tree目录中。
五、特殊结点
alias结点:
用于使用新的名称代表标签
chosen结点:
将Uboot的bootargs环境变量传递给Linux内核作为命令行参数。
六、OF内核的操作函数
驱动如何获取到设备树中的结点信息?
使用of函数获取设备树的属性。位于include/linux/of.h文件中存储。
/*找到结点函数*/
extern struct device_node *of_find_node_by_name(struct device_node *from,
const char *name); //根据名字
extern struct device_node *of_find_node_by_type(struct device_node *from,
const char *type); //根据类型
static inline struct device_node *of_find_node_by_path(const char *path);
//根据路径
/*获取子结点,父节点*/
extern struct device_node *of_get_parent(const struct device_node *node);
extern struct device_node *of_get_next_parent(struct device_node *node);
extern struct device_node *of_get_next_child(const struct device_node *node,
/*查找属性property*/
struct property {
char *name;
int length;
void *value;
struct property *next;
unsigned long _flags;
unsigned int unique_id;
struct bin_attribute attr;
};
extern struct property *of_find_property(const struct device_node *np,
const char *name,int *lenp); //查找属性
extern int of_property_count_elems_of_size(const struct device_node *np,
const char *propname, int elem_size); //查找属性的元素大小
extern int of_property_read_u32_index(const struct device_node *np,
const char *propname,
u32 index, u32 *out_value); //读取索引
extern 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_u8(const struct device_node *np,
const char *propname,
u8 *out_value) //驱动属性的值
extern int of_property_read_string(struct device_node *np,
const char *propname,
const char **out_string); //读取字符串
extern int of_n_addr_cells(struct device_node *np); //读取#address-cells
extern int of_n_size_cells(struct device_node *np); //读取#size-cells
1.读取属性例程代码
int ret = 0;
/*
backlight {
compatible = "pwm-backlight";
pwms = <&pwm1 0 5000000>;
brightness-levels = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100>;
default-brightness-level = <50>;
status = "okay";
};
*/
struct device_node *backlight_nd = NULL;
struct property *backlight_pro=NULL;
const char *name ;
u32 def_value = 0 , elemsize = 0;
u32 *brival ;
u8 i = 0;
/*找到 “/backlight” 设备结点*/
backlight_nd = of_find_node_by_path("/backlight");
if (backlight_nd == NULL){
printk("ERROR TO GET backlight_nd");
return;
}
/*获取属性*/
backlight_pro = of_find_property(backlight_nd, "compatible", NULL);
if (backlight_pro == NULL){
printk("ERROR TO GET backlight_pro");
return;
}else{
printk("compatible = %s\r\n" ,(char *)backlight_pro->value);
}
/*读取字符串属性*/
ret = of_property_read_string(backlight_nd, "status", &name);
if(ret < 0){
printk("ERROR TO GET of_property_read_string");
}else{
printk("status = %s\r\n" , name);
}
/*读取数字的属性值*/
ret = of_property_read_u32(backlight_nd, "default-brightness-level", &def_value);
if(ret < 0){
printk("ERROR TO GET of_property_read_u32");
}else{
printk("default-brightness-level = %d\r\n" , def_value);
}
/*读取数组类型的属性的数量*/
elemsize = of_property_count_elems_of_size(backlight_nd,"brightness-levels", sizeof(u32));
if(elemsize < 0){
printk("ERROR TO GET of_property_count_elems_of_size");
}else{
printk("brightness-levels = %d\r\n" , elemsize);
}
/*动态地申请数组的空间*/
brival = kmalloc( elemsize * sizeof(u32), GFP_KERNEL);
if(!brival){
printk("FAILED KMALLOC");
return -1;
}
/*读取数组数据*/
ret = of_property_read_u32_array(backlight_nd , "brightness-levels", brival , elemsize);
if(ret < 0){
printk("ERROR TO GET of_property_read_u16_array");
}else{
*brival = 0;
for(i = 0 ; i < elemsize ; i++)
printk("brightness-levels[%d] = %d\r\n" ,i, *(brival+i));
}
/*释放Kmalloc*/
kfree(brival);
2.驱动函数调用的打印过程
/mydrivers # insmod led_driver.ko
compatible = pwm-backlight
status = okay
default-brightness-level = 50
brightness-levels = 101
brightness-levels[0] = 0
brightness-levels[1] = 1
......
brightness-levels[98] = 98
brightness-levels[99] = 99
brightness-levels[100] = 100
/mydrivers #
*绑定信息文档
在/Document/devicetree/bindings路径下存储着各类外设的绑定属性信息的写法,我们可以参照文档进行学习。只是作为一个参考信息。
\