活动地址:CSDN21天学习挑战赛
【Linux驱动开发】设备树详解(一)设备树基础介绍
【Linux驱动开发】设备树详解(二)设备树语法详解
【Linux驱动开发】设备树详解(三)设备树Kernel解析
dts
文件是一种ASCII
文本格式的设备树描述,它有以下几种特性:
每个设备树文件都有一个根节点,每个设备都是一个节点。
节点间可以嵌套,形成父子关系,这样就可以方便的描述设备间的关系。
每个设备的属性都用一组key-value对(键值对)来描述。
每个属性的描述用;
结束
记住上面的几个核心特性,往下看!
/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
:表示根节点下的两个子节点child-node1
、child-node2
:表示子节点node1
下的两个子节点a-string-property = "A string";
:字符串属性,用双引号表示cell-property = <0xbeef 123 0xabcd1234>;
:32bit的无符号整数,用尖括号表示binary-property = [0x01 0x23 0x45 0x67];
:二进制数据用方括号表示a-string-list-property = "first string", "second string";
:用逗号表示字符串列表
DeviceTree
的结构非常简单,由两种元素组成:Node
(节点)和Property
(属性)。
[label:] node-name[@unit-address] {
[properties definitions]
[child nodes]
}
想象一下,一棵大树,每一个树干都认为是一个节点,每一片树叶,想作一个属性!
/dts-v1/;
/ {
compatible = "acme,coyotes-revenge";
#address-cells = <1>;
#size-cells = <0>;
cpus {
cpu@0 {
compatible = "arm,cortex-a9";
reg = <0>;
};
cpu@1 {
compatible = "arm,cortex-a9";
reg = <1>;
};
};
serial@101f0000 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "arm,pl011";
reg = <0x101f0000 0x1000 >;
};
};
下面几个属性是基本属性
/dts-v1/;
:表示一个dts
设备树文件/
:表示根节点compatible = "acme,coyotes-revenge";
compatible
: “兼容性” 属性,这是非常重要的一个属性兼容属性,由该属性值来匹配对应的驱动代码。"acme,coyotes-revenge"
:该值遵循"manufacturer,model"
格式manufacturer
表示芯片厂商,model
表示驱动名称
compatible
是一个字符串列表。列表中的第一个字符串指定节点在表单中表示的确切设备"
。, " 例如,飞思卡尔 MPC8349 片上系统 (SoC) 有一个串行设备,可实现 National Semiconductor ns16550 寄存器接口。因此,MPC8349 串行设备的 compatible 属性应为:
compatible = "fsl,mpc8349-uart", "ns16550"
. 在这种情况下,fsl,mpc8349-uart
指定确切的设备,并ns16550
声明它与 National Semiconductor 16550 UART 的寄存器级兼容。
cpus
:表示一个子节点,该子节点下又有两个子节点,分别为cpu0
和cpu1
。cpu@0
:遵循[@
]格式
:ascii字符串,表示节点名称
:单元地址,设备的私有地址,在节点reg
属性中描述。下面几个属性与寻址相关的
#address-cells
:表示reg
属性中表示地址字段的单元个数,每个单元32bit,即用多少个32bit
单元表示地址信息。
#size-cells
:表示reg
属性中表示长度字段的单元个数,每个单元32bit,即用多少个32bit
单元表示长度信息。
reg
:该属性一般用于描述设备地址空间资源信息,一般都是某个外设的寄存器地址范围信息。其式为reg =
。每个地址值都是一个或多个 32 位整数的列表,称为单元格。同样,长度值可以是单元格列表,也可以是空的。
以
cpu
节点为例:
cpu@0 {
compatible = "arm,cortex-a9";
reg = <0>;
};
其#address-cells=1
表示reg
属性中描述地址字段,所需32bit
的单元个数为1,#size-cells=0
表示reg
属性中没有表示长度的单元,即reg=<0>
再以
serial
节点为例:
serial@101f0000 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "arm,pl011";
reg = <0x101f0000 0x1000 >;
};
该设备都被分配一个基址,以及被分配区域的大小
其#address-cells=1
表示reg
属性中描述地址字段需要1个32bit
单元,#size-cells=1
表示reg
属性中描述长度字段需要2个单元,即reg=<0x101f0000 0x1000>
0x101f0000
:表示serial
的控制器起始地址0x1000
:表示serial
控制器所占用的大小
地址映射部分还要了解一个属性
,为什么要引入这个属性呢?
根节点与根节点的直接子节点,都使用了CPU
的地址分配空间,但是根节点的非直接子节点,并不会自动实用CPU
的地址空间,因此需要手动用
属性分配。
如上述的serial
节点,属于根节点下的直接子节点,无需手动再次分配地址空间,而下面所述的 external-bus
节点,其内部的子节点就需要再次分配!
/dts-v1/;
/ {
compatible = "acme,coyotes-revenge";
#address-cells = <1>;
#size-cells = <1>;
...
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>;
};
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>;
};
};
};
该总线使用了不同的寻址方式,分析一下
external-bus
节点:
#address-cells = <2>
:用两个单元表示地址#size-cells = <1>
:用一个单元表示长度reg = <0 0 0x1000>
:第一个0
表示片选号,第二个0
表示基于片选的偏移,第三个表示偏移的大小
这种抽象的表示,如何映射到CPU
地址区域呢?```属性来帮助!
ranges = <0 0 0x10100000 0x10000 // Chipselect 1, Ethernet
1 0 0x10160000 0x10000 // Chipselect 2, i2c controller
2 0 0x30000000 0x1000000>; // Chipselect 3, NOR Flash
range:表示了不同设备的地址空间范围,表中的每一项都是一个元组,包含子地址、父地址以及子地址空间中区域的大小,这三个字段。
#address-cells
决定,如前面的0 0
、0 1
#address-cells
决定,如0x10100000
、0x10160000
#size-cells
决定,如0x10000
、0x10000
经过映射后,总线的地址映射如下:
- Offset 0 from chip select 0 is mapped to address range 0x10100000…0x1010ffff
- Offset 0 from chip select 1 is mapped to address range 0x10160000…0x1016ffff
- Offset 0 from chip select 2 is mapped to address range 0x30000000…0x30ffffff
/dts-v1/;
/ {
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 >;
};
intc: interrupt-controller@10140000 {
compatible = "arm,pl190";
reg = <0x10140000 0x1000 >;
interrupt-controller;
#interrupt-cells = <2>;
};
};
如上
interrupt-controller
:声明一个节点是接收中断信号的设备,也就是中断控制器#interrupt-cells
:interrupt-controller
节点下的一个属性,表明中断标识符用多少个单元表示interrupt-parent
:设备节点中的一个属性,选择哪个中断控制器interrupts
:设备节点的一个属性,中断标识符列表,其单元个数取决于#interrupt-cells
根据设备树,我们了解到:
interrupt-controller@10140000
intc
标签,为中断控制器的别名,方便引用#interrupt-cells = <2>;
:中断标识符用两个单元格表示interrupt-parent = <&intc>;
:选择中断控制器interrupts = < 1 0 >;
:表示一个中断,第一个值用于表明中断线编号,第二个值表明中断类型,如高电平,低电平,跳变沿等
aliases {
ethernet0 = ð0;
serial0 = &serial0;
};
aliases
:正如其名,别名属性,使用方式:property = &label;
chosen {
bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200";
};
chosen
:该属性并不表示一个真实的设备,但是提供一个空间,用于传输固件和Linux
之间的数据,像启动参数,