这里有所有配置
https://www.raspberrypi.org/documentation/configuration/
设备树配置在
https://www.raspberrypi.org/documentation/configuration/device-tree.md
DT=DeviceTree
DTB= Device Tree Block
DTS=Device Tree Source
include的文件一般用dtsi结尾
树莓派最新的内核和固件开始使用设备树管理资源和内存以及模块的加载了。主要是缓和多驱动争取系统资源的问题。
并且允许HAT模块自动配置 (https://www.raspberrypi.org/blog/introducing-raspberry-pi-hats/)
当然当前的实现并不是一个纯的设备树系统,仍然有一些板子里面的代码创建了一些平台设备。。但是其他的外部接口。比如I2C I2S SPI以及音频设备现在必须用DTB来实例化。通过start.elf来传递给kernel
.最大的影响是将所有设备从开启状态,变成关闭状态,除非DTB需要这个设备。为了继续使用外部接口和相关设备。可能需要在config,txt里面添加一些新的设置,详细的介绍放在PART3里面。
PART1
设备树是用来描述系统硬件的。它应当包含了cpu的类型名字。内存配置和其他的外接设备。尽管通过列出硬件模块的确可以使得这些驱动模块被加载,但是设备树不应该用来描述软件。千万记得dt是系统无关的。因此任何linux特定的东西都不应该出现在这里。
DT通过分层次的节点来表示这些硬件。每个节点包含属性和子节点。属性是通过字节数组(大端字节序,字符串,数字或者一些随意的字节序列或者这些的组合)来命名的。
跟文件系统的节点和数据差不多。节点的位置和属性可以用斜线(/)分割的路径来表示。一个单独的/表示根节点。
1.1 基础的DTS语法
DTS是常常以.dts结尾的文本文件。和C语法有点像。用括号来分组。每行用分号结束,在一个闭合的括号后面必须要一个分号,和C的结构体差不多。
编译后的文件是FDT或者DTB,保存在dtb文件里面。
下面是一个简单的 .dts格式
/dts-v1/;
/include/ "common.dtsi";
/ {
node1 {
a-string-property = "A string";
a-string-list-property = "first string", "second string";
a-byte-data-property = [0x01 0x23 0x34 0x56];
cousin: child-node1 {
first-child-property;
second-child-property = <1>;
a-string-property = "Hello, world";
};
child-node2 {
};
};
node2 {
an-empty-property;
a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
child-node1 {
my-cousin = <&cousin>;
};
};
};
/node2 {
another-property-for-node2;
};
1 一个需要的头部 /dts-v1/ 应该是dts版本v1的意思
2 包含另外一个标准的common.dtsi。和C里面的include一样
3单一的一个节点 /
4一组子节点 node1和node2
5node1的子节点 child-node1 child-node2
6一个标签 cousin和一个标签的引用&cousin
7一些零散的属性
8一个重复的node2
属性是简单的键值对。值可以为空或者包含字节流。结构中的数据是没有编码的。
NULL结尾的字符串双引号
string-property = "a string";
32bit无符号整数用尖括号表示
cell-property = <0xbeef 123 0xabcd1234>;
随意的字节序用中括号 中间写16进制数据
binary-property = [01 23 45 67 89 ab cd ef];
混合型的数据用逗号分割
mixed-property = "a string", [01 23 45 67], <0x12345678>;
逗号也可以用来创建数据列表
string-list = "red fish", "blue fish";
1.2 关于/include/指令的一点东西
这个/include/指令一般是直接包含。类似于C语言的include. 但是dtb编译器的一个特性可能导致不同的处理方式。
假设两个节点是用绝对路径命名的,而且名字一样。那么可能会在一个dts文件里面出现两次。如果是这样的话。那么
这两个节点的属性是组合起来的,其中交叉和覆盖的属性根据需要后面的一个会覆盖前面的一个。
前面的里面,第二个出现的node2将一个新的属性添加到了原来的那个上
因此可以在dtsi里面写一个默认的值。反正后面会可以覆盖掉
1.3 标签和引用
常常需要在一个tree的地方用到另一个节点的一些属性。那么这里有四种方式。
一 使用路径字符串。
路径应该是很明显的。跟文件系统类比一下 /soc/i2s@7e203000是BCM2835和BCM2836的I2S设备的全路径。尽管很容易为属性构建一个路径。比如 /soc/i2s@7e203000/status但是标准API不会这么做。所以你先找到这个node。然后再选择他的属性
二 使用phandle
phandle是一个节点里面用phandle属性标记的一个独一无二的32bit整数。由于历史的原因,可能会出现多余的这些phandle。phandlees是连续的标号从1开始。0是无效的。
通常是DT编译器遇到的按照整数表达形式存储的节点的引用的时候分配的(也就是用尖括号括起来的。比如遇到<&node>这个时候就分配了一个整数)。通常是用label的形式。使用phandle节点的引用通常将对应整数值简单进行了编码。也没有一定要暗示他们就是phandles.。
三 使用 标签labels
和C语言里面的标签一样。一个标签给一个节点赋了一个名字。当变一起遇到以string格式或者整数格式存放的引用标签的时候会将他们转换成全路径。原来的标签在编译以后不会出现在编译后的文件里面。标签是没有属性的。只是一个命名 空间而已。
四 别名
别名和标签差不多。但是他们会以索引的形式出现在编译后的文件里面。他们保存在/aliases节点的属性下面。每个属性映射了一对别名和全路径。尽管别名会在源文件中出现。但是路径字符串通常作为标签的引用出现。而不是以全路径写出来。DT API通过检查路径的第一个字符来将路径字符串转换为节点。先查看路径是不是以斜线开头,如果不是斜线开头,必须通过/aliases表转换。
1.4 设备树的意义
构建一个设备树是很复杂的。但是下面还是提一下关键的几个点
compatible属性是硬件描述和软件驱动之间的联系。当系统遇到一个节点拥有compatible属性的时候。就开始寻找他的设备驱动数据库来寻找对应的驱动。linux上通常就是自动加载一些驱动模块。
status属性表明这个设备是启用还是禁用。如果是ok或者okay或者空的。就表示允许。如果是disable就表示禁用。所以可以将disabled放到dtsi里面。如果有需要,在对应的dts里面设置为OK
PART2 设备书的一些overlay OVERLAY是树莓派特有的。其他的有没有。后续这里我就不看了
现代的芯片是很复杂的。负载的设备树可能有好几百行。更进一步,把其他的组件和芯片放到一块板子上只会边个更麻烦。要让使得方便管理。尤其是相似的设备共用同一个组件的时候。更有必要把相同的元素放到.dtsi文件里面。然后多个.dts来包含他。
如何一个像PI一样的系统用了一些可选的组件。比如HATS。那么更麻烦。最终终每个可能的配置都需要一个设备树来描述。但是考虑到不同的基板和一些小配件仅仅需要几个GPIO针脚就共存,那么组合的数量就增加得很快了。
因此需要一个方式来描述这些占用一小部分设备树的可选组件。然后通过基本的设备树加上这些可选的组件构成完整的设备树。这些可选的元素就是overlay
2.1 片段
overlay包含大量的片段。每个包含一个节点和他的子节点。尽管听起来简单。但是语法起初很奇怪。
// Enable the i2s interface
/dts-v1/;
/plugin/;
/ {
compatible = "brcm,bcm2708";
fragment@0 {
target = <&i2s>;
__overlay__ {
status = "okay";
};
};
};
compatible字符串定义了这是属于BCM2708的。也就是BCM2835基芯片的一部分。如果是BCM2836你可以用 “brcm,bcm2709”,但是除非你要使用CPU芯片的某些特性。否则这两值一般是一样的。所以用brcm,bcm2708是比较合理的。然后来到了第一个片段。片段序号是从0开始依次增加的,如果某些片段丢失了,序号变化。就是失败。
每个片段包含两个部分。第一 target属性标记这个overlay该应用到哪个节点,第二 __overlay__他的内容将添加到目标节点里面。
上面的代码片段可以写成下面的形式
/dts-v1/;
/ {
compatible = "brcm,bcm2708";
};
&i2s {
status = "okay";
};
会出错
dtc -I dts -O dtb -o 2nd.dtbo 2nd-overlay.dts
提示
Label or path i2s not found
找不到i2s
这次用-@选项来解决引用问题
dtc -@ -I dts -O dtb -o 1st.dtbo 1st-overlay.dts
记得使用 sudo apt-get install device-tree-compiler 安装
一般来说 编译器会在scripts/dtc/dtc里面提供。当dtbs 被编译的时候 用到。
make ARCH=arm dtbs
可以使用dftdump来查看编译器生成的一些内容
$ fdtdump 1st.dtbo
/dts-v1/;
// magic: 0xd00dfeed
// totalsize: 0x106 (262)
// off_dt_struct: 0x38
// off_dt_strings: 0xe8
// off_mem_rsvmap: 0x28
// version: 17
// last_comp_version: 16
// boot_cpuid_phys: 0x0
// size_dt_strings: 0x1e
// size_dt_struct: 0xb0
/ {
compatible = "brcm,bcm2708";
fragment@0 {
target = <0xdeadbeef>;
__overlay__ {
status = "okay";
};
};
__fixups__ {
i2s = "/fragment@0:target:0";
};
};
这里i2s变成了 0xdesdbeef。而且后面多了一个fixup
这个包含了一些映射的属性,映射了phandle和目标节点。这里就是target和-0xdeadbeef。但是片段可能包含一些没有解决的引用。需要进一步处理。
如果写了很复杂的片段。可能有其他两个节点。 __local_fixups__和__symbols__
回到1.3节。说原来的标签在编译后的文件里面不会出现。如果使用了-@的时候还是会出现的。除此之外每个label会产生一个__sybols__节点。银蛇了标签到路径。和别名节点一样。