设备树是用一个类似文本的dts文件来描述所有硬件相关的内容. dtb是dts文件编译后得到的文件.
dtc命令可以把dts文件编译成dtb, 也可以把dtb文件反编译成dts.
H5所用的dts文件在内核源码目录arch/arm64/boot/dts/allwinner/里.
dtsi文件是多个方案的设备树共用部分,像c的头文件可在设备树文件里include引用.
现H5方案用的dts是: sun50i-h5-nanopi-neo2.dts
dtc命令主要用法:
dtc -I 输入格式(dts/dtb) -O 输出格式(dts/dtb) -o 输出文件名 输入文件...
把dtb文件反编译成dts文件:
dtc -I dtb -O dts -o my.dts sun50i-h5-nanopi-neo2.dtb
把dts文件编译成dtb文件:
dtc -I dts -O dtb -o my.dtb my.dts
参考文档: https://elinux.org/Device_Tree_Usage
设备树是由节点和属性组成, 一个节点可以有多个子节点, 子节点里还可以有它的子节点和属性…
节点名和属性名可以自定义, 也有些专门用途的属性.
设备树是由根节点”/”开始的, 用”{}”表示其作用范围. 如:
/ { /* 根节点 */
node1 { /* 子节点 */
property = "string"; /* 子节点属性 */
child-node1 { /* 子节点的子节点 */
child-property = <1>; /* 子节点的子节点属性 */
};
child-node2 { /* 子节点属性 */
};
};
node2 {
};
};
----------
node1和node2分别描述一个设备的信息.
每个节点或每个属性语句都需要以分号结尾.
节点属性的值只分成3种:
1) 字符串, 以双引号括起来. 如: what = "hello"; 表示what属性的值是一个字符串"hello"
2) 32位无符号整形数, 以<>符号括起来. 如: cout = <11>; 表示count属性的值是一个整型数11
3) 字节数组, 以[]符号括起来. 如: mymac = [11 22 33 44 55 66]; 表示mymac属性值是一个char数组.
//注意字节数组里内容默认是16进制的,0x即使不写也是按16进制数来处理.
属性值可以有连续多个值,需要使用逗号隔开就可以了. 如:
mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;
string-list = "red fish", "blue fish";
与地址相关的节点以"name@基地址"方式来命名, name只能是英文,31个字符长度.
无需写基地址的节点则可以以"name"方式直接来命名.
如果设备的基地址由片选和内部片选号(一般为0)组成,则"name@片选号,内部片选号"的方式来命名
ethernet@0,0 {
compatible = "smc,smc91c111";
reg = <0 0 0x1000>;
};
name通常是一种设备的类型,不是具体的型号.
节点的compatible属性用于与设备驱动匹配使用(非常重要). 它是一个字符串数组.
compatible属性值可以有多个字符串,表示此设备是有兼容多种角色的功能.
注意属性值里的通配符是不会起作用的.
地址相关的属性.
reg属性表示相关的配置寄存器基地址及地址长度,可以表示多个基地址及其长度.
写法: "reg = <address1 length1 address2 length2 address3 length3 ...>"
reg属性值里的顺序可以是: "基地址 地址长度 基地址 地址长度"
也可以是: "基地址 基地址 ... 地址长度 地址长度 ... "
#address-cells属性表示子节点reg属性值里多少个基地址后才可能会有地址长度,如果#size-cells为0则表示reg属性值里全是基地址
#size-cells属性表示子节点reg属性值里多少个地址长度才可能会有另外的基地址
如:
/ {
#address-cells = <1>; //表示子节点里的reg属性值里一个基地址后就可能有地址长度或另一个基地址
#size-cells = <1>; //表示reg属性值里一个基地址后就有一个地址长度
serial@101f0000 {
compatible = "arm,pl011";
reg = <0x101f0000 0x1000 >;
};
gpio@101f3000 {
compatible = "arm,pl061";
reg = <0x101f3000 0x1000
0x101f4000 0x0010>;
};
};
external-bus {
#address-cells = <2>;
#size-cells = <1>;
//表示它的子节点里的reg属性值里可以是: "基地址 基地址 地址长度"
也可以是: "基地址 基地址 地址长度 基地址 基地址 地址长度 ..."
...
};
ranges属性可以在根节点制定好具体控制器的基地址表格,而控制器只需写上ranges里的序号作基地址即可.
如smc91c111的基地址为0x10100000, a1234-i2c-bus的基地址为0x10160000, k8f1315ebm的基地址为0x30000000
/ {
...
external-bus {
#address-cells = <2>
#size-cells = <1>;
ranges = <0 0 0x10100000 0x10000 // Chipselect 1, Ethernet
//当访问地址0x10100000时,Ethernet则占用地址总线和数据总线
1 0 0x10160000 0x10000 // Chipselect 2, i2c controller
2 0 0x30000000 0x1000000>; // Chipselect 3, NOR Flash
// 第一列表示片选线的序号(用于确定哪个控制器工作), 第二列表示内部片选线序号(控制器的内部片选)
ethernet@0,0 { /* 这里的"0,0"是指ranges表格里的<0 0 ...项 */
compatible = "smc,smc91c111";
reg = <0 0 0x1000>;
};
i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
#address-cells = <1>;
#size-cells = <0>;
reg = <1 0 0x1000>;
rtc@58 {
compatible = "maxim,ds1338";
reg = <58>;
};
};
flash@2,0 {
compatible = "samsung,k8f1315ebm", "cfi-flash";
reg = <2 0 0x4000000>;
};
};
};
中断相关的属性:
interrupt-controller;属性用于标明此节点设备会接收处理中断信号.
#interrupt-cells; 属性用于标明当前节点里的interrupts属性值里的数值个数.
interrupt-parent; 使用指定的节点作父节点, 相当于在当前位置展开父节点内容
interrupts;属性指定使用的中断号
/ {
...
interrupt-parent = <&intc>; //引用intc节点
...
serial@101f0000 {
compatible = "arm,pl011";
reg = <0x101f0000 0x1000 >;
interrupts = < 1 0 >;
};
serial@101f2000 {
compatible = "arm,pl011";
reg = <0x101f2000 0x1000 >;
interrupts = < 2 0 >;
};
gpio@101f3000 {
compatible = "arm,pl061";
reg = <0x101f3000 0x1000
0x101f4000 0x0010>;
interrupts = < 3 0 >;
};
intc: interrupt-controller@10140000 {
compatible = "arm,pl190";
reg = <0x10140000 0x1000 >;
interrupt-controller;
#interrupt-cells = <2>;
};
};
aliases节点用于设置别名.
aliases {
ethernet0 = ð0;
serial0 = &serial0;
};
chose节点用于设置内核里使用的默认参数, 如bootargs
chosen {
bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1:/nfs console=ttyS0,115200";
};