目录
基本数据格式
设备树组成:节点和属性。属性是键值对,并且节点可以同时包含属性和子节点。
.dts简单树
/dts-v1/;
/ {
node1 {
a-string-property = "A string";
a-string-list-property = "first string", "second string";
// hex is implied in byte arrays. no '0x' prefix is required
a-byte-data-property = [01 23 34 56];
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 {
};
};
};
- 此简单树没有实际用处,仅显示节点和属性结构。
- 一个根节点:"/"。
- 两个子节点:"node1"和"node2"。
- node1节点的子节点:"child-node1"和"child-node2"。
- 以及一队散落在树上的属性。
- 属性是简单的键值对,值可以为空或者任意字节流。
- 文本字符串以双引号表示:a-string-property = "A string";
- 单元是由尖括号分割的32位无符号整数:a-cell-property = <0X1 0X2222 3 0X12345678>;
- 二进制数据用方括号分割:a-byte-data-property = [01 23 34 56];
- 可以通过逗号将不同的表示形式的数据连接起来:mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;
- 逗号还用于创建字符串列表:a-string-list-property = "first string", "second string";
基本概念:
假设ABC公司制造的名为"Poseidon"的设备,设备基本信息如下:
- 1个32位ARM CPU
- 处理器本地总线连接到内存映射的串行端口,spi总线控制器,i2c控制器,中断控制器和外部总线桥
- 256MB SDRAM基于0地址
- 2个基于0x101F1000和0x101F2000的串行端口
- 基于0x101F3000的GPIO控制器
- 基于0x10170000且具有以下器件的SPI控制器
- MMC插槽,SS引脚连接到GPIO#1
- 带有以下设备的外部总线桥:SMC SMC91111以太网设备连接到基于0x10100000的外部总线
- 基于以下设备的基于0x10160000的i2c控制器:Maxim DS1338实时时钟。响应从地址1101000(0x58)
- 基于0x30000000的64MB NOR闪存
第一步:构建框架结构。
需要唯一的机器标识,是设备树中的最小结构。
compatible指定系统名称,包含了格式为" <manufacturer>,<model>"的字符串,指定确切的设备和生产商可以避免命名空间上冲突,linux操作系统会根据compatible值来决定如何在机器上运行。
/dts-v1/;
/{
compatible = "ABC, Poseidon";
};
第二步:CPU进行详细描述。
为每一个cpu添加一个名为"cpus"的容器节点以及一个节点。本测试系统是双核的Cortex a9,故要描述两个cpu。cpu节点中的每个compatible属性都是一个字符串,该属性通过字符串的形式指定确切的cpu模型,和顶层属性一样。
/dts-v1/;
/{
compatible = "ABC, Poseidon";
cpus{
cpu@0{
compatible = "arm, cortex-a9";
};
cpu@1{
compatible = "arm, cortex-a9";
};
};
};
补充:节点名称
- 每一个节点的名称都必须以<name>@<unit-address>的形式。
- name是一个简单的ascii字符串,最大长度为31个字符。通常是根据代表的设备类型命名。例如3ca以太网适配器节点将使用名称ethernet,而不是3ca。
- 如果节点使用地址描述设备,则包含unit-address属性,单元地址是用于访问设备的住地址,并在节点的reg属性中列出。
- 兄弟节点必须具有唯一的名称,但是多个节点使用相同的通用名称也是允许的,只要地址不同。例如(serial@10001001和serial@1000ffff)。
第三步:设备进行描述。
- 系统中的每个设备都有一个设备树节点表示,暂且没有使用地址范围和中断处理的时候,暂时不填写。
- 每一个设备节点都要有一个compatible属性。
- flash节点属性包含2个字符串,在后续说明。
- 节点的名称反映的是设备的类型,而不是特定的型号,尽可能使用已定义的通用节点名称。
/dts-v1/;
/{
compatible = "ABC, Poseidon";
cpus{
cpu@0{
compatible = "arm, cortex-a9";
};
cpu@1{
compatible = "arm, cortex-a9";
};
};
serial@10001001{
compatible = "arm, p1011";
};
serial@10001001{
compatible = "arm, p1012";
};
gpio@101f30000{
compatible = "arm, p1060";
}
interrupt-controller@10140000{
compatible = "arm, p1190";
}
spi@10115000{
compatible = "arm, p1022";
};
external-bus{
ethernet@0,0{
compatible = "smc, smc91c111";
};
i2c@1,0{
compatible = "acme, a1234-i2c-bus";
rtc@58{
compatible = "maxin, ds1222";
};
};
flash@2,0{
compatible = "samsung, k8f1315ebm", "cfi-flash";
};
};
};
compatible属性详解
- compatible是每个节点必须有的属性,作用是操作系统决定将某个驱动程序绑定到设备的键值。
- compatible是字符串列表。列表中的第一个字符串指定节点以什么形式表示确切的设备"<manufacturer>, <model>"。
- 这种做法允许将现有设备驱动程序绑定到较新的设备,同时仍然唯一地标识确切的硬件。
- 请勿使用通配符兼容的值,例如“ fsl,mpc83xx-uart”或类似的值。
例如,飞思卡尔MPC8349片上系统(SoC)具有ns16550寄存器接口的串行设备。因此,MPC8349串行设备的兼容属性应为:compatible = "fsl, mpc8349-uart", "ns16550"。在这种情况下,fsl,mpc8349-uart指定确切的设备,并使用ns16550声明该设备与美国国家半导体16550 UART的寄存器级别兼容。
寻址方式
可寻址的设备使用以下属性将地址信息放入到设备树中:
- reg
- #address-cells
- #size-cells
- 每个可寻址设备都会获得一个reg,它是形式的元组列表reg = <address1 length1 [address2 length2] [address3 length3] ... >。每个元组代表设备使用的地址范围。每个地址值是一个或多个称为单元的 32位整数的列表。同样,长度值可以是单元格列表,也可以是空值。
- 由于地址和长度字段都是可变大小的变量,因此父节点中的#address-cells和#size-cells属性用于说明每个字段中有多少个单元格。换句话说,正确解释reg属性需要父节点的#address-cells和#size-cells值。
CPU寻址
CPU节点是最简单的情形,每个CPU都有分配一个唯一的ID,并且没有与CPU ID相关的大小。
cpus{
#address-cells = <1>;
#size-cells = <0>;
cpu@0{
compatible = "arm,cortex-a9";
reg = <0>;
};
cpu@1{
compatible = "arm,cortex-a9";
reg = <1>;
};
};
- 在该cpus节点中,#address-cells为1,#size-cells为0。这意味着子reg值是单个uint32,表示没有限制字段的地址。在这种情况下,两个#size-cellscpu的地址分别为0和1。对于cpu节点,其值为0,因为每个cpu仅分配了一个地址。
- 该reg值与节点名称中的值匹配。按照惯例,如果节点具有reg属性,则节点名称必须包含unit-address,即该reg属性中的第一个地址值。
内存映射的设备
与在cpu节点中找到的单个地址值不同的是,为内存映射的设备分配了它将响应的地址范围。 #size-cells用于说明每个子reg元组中的length字段有多大。在下面的示例中,每个地址值是1个单元(32位),每个长度值也是1个单元,这在32位系统上是典型的。64位机器可以为#address-cells和#size-cells使用值2,以在设备树中获得64位寻址。
/dts-v1/;
/{
#address-cells = <1>;
#size-cells = <1>;
...
serial@0x101f0000{
compatible = "arm, p1011";
reg = <0x101f0000 0x1000>;
};
serial@0x101f2000{
compatible = "arm, p1011";
reg = <0x101f2000 0x1000>;
};
gpio@101f3000{
compatible = "arm, p1061";
reg = <0x101f3000 0x1000
0x101f4000 0x0010>;
};
interrupt-controller@10140000{
compatible = "arm, p1190";
reg = <0x10140000 0x1000>;
};
spi@10115000{
compatible = "arm, p1022";
reg = <0x10115000 0x1000>;
};
...
};
- 每个设备都分配有一个基地址,并分配了它的区域大小。在此示例中,为GPIO设备地址分配了两个地址范围;即:0x101f3000 ..0x101f3fff和0x101f4000..0x101f400f。
- 有些设备使用不同的寻址方案在总线上。例如,设备可以通过分立的芯片选择线连接到外部总线。由于每个父节点都为其子节点定义了寻址域,因此可以选择地址映射来最好地描述系统。下面的代码显示了连接到外部总线的设备的地址分配,芯片选择号已编码到该地址中。
external-bus{
#address-cells = <2>;
#size-cells = <1>;
ethernet@0,0{
compatible = "smc, smc91c111";
reg = <0 0 0x1000>;
};
i2c@1,0{
compatible = "acme, a1234-i2c-bus";
reg = <1 0 0x1000>;
rtc@58{
compatible = "maxim, ds1338";
};
};
flash@2,0{
compatible = "samsung, k8f1315ebm", "cfi-flash";
reg = <2 0 0x40000000>;
};
};
- 将external-bus2个单元格用作地址值,一个代表芯片选择编号,另一个代表相对于芯片选择基数的偏移量。长度字段保持为单个单元,因为仅地址的偏移部分需要具有范围。因此,在此示例中,每个reg条目包含3个单元格。芯片选择号,偏移量和长度。
- 由于地址域包含在节点及其子节点中,因此父节点可以自由定义对总线有意义的任何寻址方案。直接父级和子级节点之外的节点通常不必关心本地寻址域,并且必须将地址映射为从一个域到达另一个域。
非内存映射设备
其他设备未在处理器总线上映射到内存。它们可以具有地址范围,但不能由CPU直接访问。相反,父设备的驱动程序将代表CPU执行间接访问。以i2c设备为例,为每个设备分配了一个地址,但是没有与之关联的长度或范围。这看起来与CPU地址分配几乎相同。
i2c@1,0{
compatible = "acme, a1234-i2c-bus";
reg = <1 0 0x1000>;
rtc@58{
compatible = "maxim, ds1338";
};
};
地址范围
- 上面已经讨论了如何为设备分配地址,但是目前这些地址仅在设备节点本地。它尚未描述如何从这些地址映射到CPU可以使用的地址。
- 根节点始终描述CPU的地址空间视图。根的子节点已经在使用CPU的地址域,因此不需要任何显式映射。例如,serial @ 101f0000设备直接分配了地址0x101f0000。 不是根的直接子节点的节点不使用CPU的地址域。为了获得内存映射的地址,设备树必须指定如何将地址从一个域转换为另一个域。该ranges属性用于此目的。这是添加了ranges属性的示例设备树。
/dts-v1/
/{
compatible = "ABC, Poseidon";
#address-cells = <2>;
#size-cells = <1>;
ranges = <0 0 0x10100000 0x10000 //chip select 1,Ethernet
1 0 0x10160000 0x10000 //chip select 2,i2c controller
2 0 0x30000000 0x10000000>; //chip select 3,NOR Flash
ethernet@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>;
};
};
ranges是地址翻译的列表。范围表中的每个条目都是一个元组,其中包含子地址,父地址以及子地址空间中区域的大小。每个字段的大小取决于孩子的#address-cells值,父母的#address-cells值和孩子的#size-cells值。对于本例中的外部总线,子地址为2个单元,父地址为1个单元,大小也为1个单元。三个列表对应翻译为:
- 片选0的偏移量0映射到地址范围0x10100000..0x1010ffff
- 片选1的偏移量0映射到地址范围0x10160000..0x1016ffff
- 片选2的偏移量0映射到地址范围0x30000000..0x30ffffff
如果父级和子级地址空间相同,则节点可以添加一个空ranges属性。空范围属性的存在意味着子地址空间中的地址被1:1映射到父地址空间。
为什么都可以使用1:1映射来编写地址转换呢?一些总线(如PCI)具有完全不同的地址空间,其详细信息需要向操作系统公开。其他一些具有DMA引擎,需要知道总线上的真实地址。有时需要将设备分组在一起,因为它们都共享相同的软件可编程物理地址映射。是否应使用1:1映射在很大程度上取决于操作系统所需的信息以及硬件设计。
您还应该注意,rangesi2c @ 1,0节点中没有任何属性。其原因是,与外部总线不同,i2c总线上的设备未在CPU的地址域上进行内存映射。相反,CPU通过i2c @ 1,0设备间接访问rtc @ 58设备。缺少ranges属性意味着除父设备以外的任何设备都不能直接访问该设备。
中断如何使用
- 与遵循树的自然结构的地址范围转换不同,中断信号可以源自机器中的任何设备,也可以在机器中的任何设备上终止。与设备树中自然表示的设备寻址不同,中断信号表示为独立于树的节点之间的链接。四个属性用于描述中断连接:
- interrupt-controller -空属性,将节点声明为接收中断信号的设备
- #interrupt-cells-这是中断控制器节点的属性。它指出此中断控制器的中断说明符中有多少个单元(类似于#address-cells和#size-cells)。
- interrupt-parent-设备节点的属性,该设备节点包含与其相连的中断控制器的虚拟对象。没有中断父级属性的节点也可以从其父节点继承该属性。
- interrupts-设备节点的属性,其中包含一系列中断说明符,每个中断说明符对应一个设备上的中断输出信号。
- 一个中断符是数据的一个或多个单元(如通过#中断单元指定的),该其中断输入所述装置被附接到指定。多数设备只有一个中断输出,如下例所示,但设备上可能有多个中断输出。中断说明符的含义完全取决于中断控制器设备的绑定。每个中断控制器可以决定需要多少个单元来唯一定义一个中断输入。
/dts-v1/
/{
compatible = compatible = "ABC, Poseidon";
#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@0x101f0000{
compatible = "arm, p1011";
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 0 0x10100000 0x10000 // Chipselect 1, Ethernet
1 0 0x10160000 0x10000 // Chipselect 2, i2c controller
2 0 0x30000000 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>;
};
};
- 设备具有单个中断控制器,interrupt-controller@10140000。
- 标签'intc:'已添加到中断控制器节点,并且该标签用于为根节点中的interrupt-parent属性分配一个虚拟对象。此中断父级值成为系统的默认值,因为除非明确覆盖所有子节点,否则所有子节点都将继承它。
- 每个设备使用中断属性来指定不同的中断输入线。
- #interrupt-cells为2,因此每个中断说明符都有2个单元。此示例使用以下通用模式:使用第一个单元对中断线号进行编码,使用第二个单元对诸如高电平有效对低电平有效或边沿电平对电平敏感的标志进行编码。对于任何给定的中断控制器,请参阅控制器的绑定文档以了解说明符的编码方式。
设备特定数据
除了通用属性,还可以将任意属性和子节点添加到节点。只要遵循某些规则,就可以添加操作系统所需的任何数据。
- 首先,新的特定于设备的属性名称应使用制造商前缀,以使其与现有的标准属性名称不冲突。
- 其次,必须在绑定中记录属性和子节点的含义,以便设备驱动程序作者知道如何解释数据。绑定记录了特定的兼容值的含义,应具有的属性,其可能具有的子节点以及所代表的设备。每个唯一compatible值应具有其自己的绑定(或声明与另一个兼容值的兼容性)。新设备的绑定记录在此Wiki中。有关文档格式和审阅过程的说明,请参见主页。
- 第三,将新的绑定发布到devicetree-discuss@lists.ozlabs.org邮件列表中以进行检查。审查新的绑定会发现很多常见的错误,这些错误将来会引起问题。
特殊节点
aliases节点
完整路径通常引用一个特定的节点,例如/external-bus/ethernet@0,0,但是当用户真正想知道的是“哪个设备是eth0?”时,这变得很麻烦。该aliases节点可用于将短别名分配给完整的设备路径。例如:
aliases{
ethernet0 = ð0;
serial0 = &serial0;
};
chosen节点
该chosen节点并不代表真实的设备,而是充当在固件和操作系统之间传递数据的地方,例如引导参数。所选节点中的数据不代表硬件。通常,所选节点在.dts源文件中保留为空,并在引导时进行填充。在我们的示例系统中,固件可能将以下内容添加到所选节点:
chosen {
bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200";
};
来源:CSDN
作者:雲烟
链接:https://blog.csdn.net/baidu_33879812/article/details/104541104