1、起源:由于各家厂商重复添加代码到linux kernel中,导致内核充斥着大量重复代码。在linus爆发后,arm linux社区在linux kernel 3.1.y左右版本后,引进了其他体系(PowerPC:Flattened Device Tree)的设备树结构。
2、变化:
a、添加描述设备的配置文本(.dts)。
b、添加对配置文本解析的工具(dtc),将.dts编译成.dtb,.dtb可以单独编译,编译完成后可以追加到bootload或kernel镜像的后面 。
c、bootload需要传递设备树参数给内核。即从某个地址搬运.dtb到内存中,然后将地址传递给内核。
3、.dts与machine:.dts与machine的关系一般是一一对应。如果多个machine有共享的数据,则可以合成.dsti,相当于c语言的头文件。其他.dts要用的时候直接include就行。
4、.dts格式:.dts文件包含了节点和属性。节点可以包含子节点(形成树的结构)。节点表示machine上面的一个device,如:cpu, ram或rom。属性表示device的各种资源,由name和value组成。
例:ARM的local bus上内存映射区域分布了2个串口(分别位于0x101F1000 和0x101F2000)、GPIO控制器(位于0x101F3000)、SPI控制器(位于0x10170000)、中断控制器(位于0x10140000)和一个external bus桥;
External bus桥上又连接了SMC SMC91111 Ethernet(位于0x10100000)、I2C控制器(位于0x10160000)、64MB NOR Flash(位于0x30000000);
External bus桥上连接的I2C控制器所对应的I2C总线上又连接了Maxim DS1338实时钟(I2C地址为0x58)。
/ {
compatible = "acme,coyotes-revenge";
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&intc>;
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a9";
reg = <0>;
};
cpu@1 {
compatible = "arm,cortex-a9";
reg = <1>;
};
};
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>;
};
spi@10115000 {
compatible = "arm,pl022";
reg = <0x10115000 0x1000>;
interrupts = <4 0>;
};
external-bus {
#address-cells = <2>
#size-cells = <1>;
ranges = <0 00x10100000 0x10000 // Chipselect 1, Ethernet。
1 00x10160000 0x10000 // Chipselect 2, i2c controller。
2 00x30000000 0x1000000>; // Chipselect 3, NOR Flash。
ethernet@0,0 {
compatible = "smc,smc91c111";
reg = <0 0 0x1000>;
interrupts = <5 2>;
};
i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
#address-cells = <1>;
#size-cells = <0>;
reg = <1 0 0x1000>;
interrupts = <6 2>;
rtc@58 {
compatible = "maxim,ds1338";
reg = <58>;
interrupts = <7 3>;
};
};
flash@2,0 {
compatible = "samsung,k8f1315ebm", "cfi-flash";
reg = <2 0 0x4000000>;
};
};
};
5、编译成dtb后组织结构为:
file:///C:/Users/guanliang%20ding/AppData/Local/YNote/data/smart_john_ding@163.com/b26333ec6fcd424185d081430628657c/clipboard.png
注:
1、节点与具体设备一一对应。如总线控制器下挂载多个从设备。就会有控制器节点和从设备节点。2、具体配置参考内核doc。路径:Documentation/devicetree/bindings。
3、由于设备树和内核分开编译。所以极大的增加了内核的灵活性。某些情况下不需要重新编译内核就能将内核移植到另一个平台上。即平台管脚和内存布局定义相同,差别的只是资源在系统中的位置。这类设备的显著特点是同一个厂商。
4、设备树是从各类驱动中抽象并且分离出来的特性。如平台驱动的struct platform_device、i2c设备的struct i2c_board_info与spi设备的struct spi_board_info。
5、bootload将设备树从flash搬到ram中。然后内核读取设备树后,将其转化成对应的信息存储在/sys文件系统中。接着,驱动加载时,可由驱动或者驱动框架去读取。待驱动注册成功时,设备驱动会在/dev/目录下出现设备名。
6、设备树可以单独编译(# dtc xxx.dtb),也可以在内核中编译(# make xxx.dtb)。运行# make xxx.dtb可以将arch/arm/boot/dtc/中的xxx.dts编译成xxx.dtb。
7、fdt是dt的一种。是一种符合open firmware标准的启动部件。在linux内核中,所有跟fdt有关的函数都会以of_xxx开头。
8、设备树属于一种静态配置资源的方式,不同于x86的动态配置。x86动态配置主要体现在即插即用技术,例如pci总线。另外,还有地址重映射机制。任何地址空间都能动态的映射到cpu总线空间,而在嵌入式中一般是固定的。9、设备树中的内容。一部分由系统读取,并交给驱动。例如:reg、clk、gpio和interrupt。另外一部分可能由驱动自己去读取。所以,针对不同的设备树,需要根据驱动代码具体分析。
10、一个设备的设备树一般需要创建三个文件。*.h文件定义一些常量,*.dts文件用来放核心部件的配置,*.dtsi用来放外设配置。