设备树相关知识学习笔记整理

设备树不是用来写驱动,而是用来给内核里的驱动程序指定硬件信息

1、设备树的语法

1.1 DTS文件格式

DTS(Device Tree Source)文件是一种描述硬件设备和设备树的源代码文件。它是用于描述硬件平台和设备树的一种规范格式,用于在Linux内核中配置和管理硬件设备。

DTS文件使用的是一种基于文本的语言,它采用了树状结构来描述硬件设备和设备之间的关系。DTS文件通常以.dts为扩展名。
通过编写DTS文件,可以描述硬件设备的各种属性和连接关系,包括设备的寄存器地址、中断号、时钟源、GPIO引脚等。Linux内核在启动过程中会解析DTS文件,根据设备树的描述来进行硬件的初始化和驱动的匹配。这样可以实现对不同硬件平台的统一管理和配置。
DTS的文件布局

/dts-v1/; // 表示版本
[memory reservations] // 格式为: /memreserve/ <address> <length>;
/{
 [property definitions]
 [child nodes]
};

1.2 node格式

设备树中的基本单元,被称为node,其格式为:

 [label:] node-name[@unit-address] {
 [properties definitions]
 [child nodes]
 };

label 是标号,可以省略。label 的作用是为了方便地引用 node。例如:

/dts-v1/;
/ {
  uart0: uart@fe001000 {
  compatible="ns16550";
  reg=<0xfe001000 0x100>;
  };
 };

可以使用下面 2 种方法来修改 uart@fe001000 这个 node

// 在根节点之外使用 label 引用 node:
&uart0 {
 status = “disabled”;
};
或在根节点之外使用全路径:
&{/uart@fe001000} {
 status = “disabled”;
};

1.3 properties 的格式

简单地说,properties 就是name=value,value 有多种取值方式

//格式1
[label:] property-name = value;
//格式2(没有值)
[label:] property-name;
//Property取值只有3种:
//arrays of cells(1 个或多个 32 位数据, 64 位数据使用 2 个 32 位数据表示)
interrupts = <17 0xc>;//32bit 
clock-frequency = <0x00000001 0x00000000>;//64bit
//string(字符串)
compatible = "simple-bus";

//bytestring(1 个或多个字节) 
local-mac-address = [00 00 12 34 56 78]; // 每个 byte 使用 2 个 16 进制数来表示
local-mac-address = [000012345678]; // 每个 byte 使用 2 个 16 进制数来表示

//也可以是各种值的组合
compatible = "ns16550", "ns8250";
example = <0xf00f0000 19>, "a strange property format";

1.4 dts文件包含dtsi文件

在内核的 arch/arm/boot/dts目录下就有能用的设备树模板,一般命名为 xxxx.dtsii表示include,被别的文件引用的。
dtsi 文件跟 dts 文件的语法是完全一样的。
dts 中可以包含.h 头文件,也可以包含 dtsi 文件,在.h 头文件中可以定义一些宏。

1.5 常用的属性

1.5.1 #address-cells、#size-cells

cell 指一个 32 位的数值,
address-cells:address 要用多少个 32 位数来表示;
size-cells:size 要用多少个 32 位数来表示
下例中,address-cells 为 1,所以 reg 中用 1 个数来表示地址,即用 0x80000000 来表示地址;size-cells 为 1,所以 reg 中用 1 个数来表示大小,即用 0x20000000 表示.

/ {
#address-cells = <1>;
#size-cells = <1>;
memory {
	reg = <0x80000000 0x20000000>;
 	};
};

1.5.2 compatible

compatible表示“兼容”,对于某个 LED,内核中可能有 A、B、C 三个驱动都支持它,那可以这样
写:

led {
compatible = "A","B", "C";
};

内核启动时,就会为这个 LED 按这样的优先顺序为它找到驱动程序:A、B、C。
根节点下也有 compatible 属性,用来选择哪一个“machine desc”:一个内核可以支持 machine A,
也支持 machine B,内核启动后会根据根节点的 compatible 属性找到对应的 machine desc 结构体,执行其中的初始化函数。
compatible 的值,建议取这样的形式:manufacturer,model,即“厂家名,模块名”。

1.5.3 model

model 属性与 compatible 属性有些类似,但是有差别。
compatible 属性是一个字符串列表,表示你的硬件兼容 A、B、C 等驱动;
model 用来准确地定义这个硬件是什么。
比如根节点中可以这样写:

/ {
	compatible = "my,A","my,B";
	model = "hardwordName";
};
//这里表示这个单板,可以兼容内核中的"A",也兼容内核中的"B",但是它是什么板,用model属性明确

1.5.4 status

dtsi 文件中定义了很多设备,但是在你的板子上某些设备是没有的。这时你可以给这个设备节点添加一个 status 属性,设置为disabled

&uart1 {
 	status = "disabled";
};
//value可取值
//okay:设备正常运行
//disabled:设备不可操作,但是后面可以恢复正常工作
//fail:发生了严重错误,需要修复
//fail-sss:发生了严重错误,需要修复;sss表示错误信息

1.5.5 reg

reg 的本意是 register,用来表示寄存器地址。
但是在设备树里,它可以用来描述一段空间。反正对于 ARM 系统,寄存器和内存是统一编址的,即访问寄存器时用某块地址,访问内存时用某块地址,在访问方法上没有区别。
reg 属性的值,是一系列的address size,用多少个 32 位的数来表示 address 和 size,由其父节
点的#address-cells、#size-cells 决定。

/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
memory {
	reg = <0x80000000 0x20000000>;
};
};

1.5.6 interrupts

interrupts表示设备的中断信息。interrupts属性用于描述设备的中断线路,包括中断控制器的引脚号和中断触发方式。
interrupts属性的值是一个整数数组,每个元素表示一个中断线路。数组的第一个元素表示中断控制器的引脚号,后续的元素表示中断触发方式和其他中断相关的属性。
示例:

interrupts = <0 2 0x04>;
//表示设备的中断控制器引脚号为0,中断触发方式为2(上升沿触发),其他中断相关属性为0x04

1.5.7 常用的结点

(1) dts文件必须有一个根节点:

/dts-v1/;
/ {
model = "SMDK24440";
compatible = "samsung,smdk2440";
#address-cells = <1>;
#size-cells = <1>;
};

根节点中必须有这些属性:

#address-cells 
// 在它的子节点的 reg 属性中, 使用多少个 u32 整数来描述地址(address)
#size-cells
 // 在它的子节点的 reg 属性中, 使用多少个 u32 整数来描述大小(size)
compatible 
// 定义一系列的字符串, 用来指定内核中哪个 machine_desc 可以支持本设备
 // 即这个板子兼容哪些平台
 // uImage : smdk2410 smdk2440 mini2440 ==> machine_desc
 model
 // 咱这个板子是什么
 // 比如有 2 款板子配置基本一致, 它们的 compatible 是一样的
 // 那么就通过 model 来分辨这 2 款板子

(2) CPU节点
一般不需要我们设置,在 dtsi 文件中都定义好了:

cpus {
	#address-cells = <1>;
	#size-cells = <0>;
	cpu0: cpu@0 {
 		.......
	}
};

(3) memory 节点
芯片厂家不可能事先确定你的板子使用多大的内存,所以 memory 节点需要板厂设置,比如:

memory {
   reg = <0x80000000 0x20000000>;
};

(4) chosen节点
我们可以通过设备树文件给内核传入一些参数,这要在 chosen 节点中设置 bootargs 属性:

chosen {
     bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
};

2、内核对设备树的处理

从源代码文件dts文件开始,设备树的处理过程为:
在这里插入图片描述

  • dts在PC机上被编译为dtb文件
  • u-bootdtb文件传给内核
  • 内核解析dtb文件,把每一个节点都转为device_node结构体
  • 对于某些device_node结构体,会被转为platform_device结构体

dtb里的每一个节点都会被转换为device_node结构体

//包含了设备树节点的各种属性和信息
struct device_node {
    const char *name;               // 节点的名称
    const char *type;               // 节点的节点所代表的设备的类型(device_type)
    struct device_node *parent;     // 指向父节点的指针
    struct device_node *child;      // 指向子节点的指针
    struct device_node *sibling;    // 指向兄弟节点的指针
    struct property *properties;    // 指向节点属性的指针
    struct property *deadprops;     // 指向已删除属性的指针
    struct node_ops *ops;           // 节点操作的指针
    void *data;                     // 指向节点的私有数据指针
    struct list_head allnext;       // 用于链表操作的指针
    struct fwnode_handle fwnode;    // 用于处理固件节点的句柄
    struct fwnode_handle *secondary;   // 用于处理辅助固件节点的指针
    struct of_phandle_args phandle_args;   // 用于处理phandle属性的参数
};
struct property {
    const char *name;
    unsigned int length;
    void *value;
    struct property *next;
    struct kobject *kobj;
    const struct of_device_id *of_match;
};

2.1 符合下列条件的节点会被转换为platform_device

(1) 根节点下含有compatile属性的子节点
(2) 含有特定 compatile 属性节点的子节点
如果一个节点的 compatile 属性,它的值是这4者之一:simple-bus,simple-mfd,ias,armba-bus,那么它的子节点(也需要包含compatile属性)也可以转换为platform_device
注意:总线I2C、SPI节点下的子节点:不转换为platform_device。某个总线下的子节点。应该交给对应的总线驱动程序来处理,他们不应该被转换为platform_device。

2.2 device_node结构体如何转为platform_device结构体

(1) platform_device 中含有 resource 数组, 它来自 device_node 的 reg, interrupts 属性;
(2) platform_device.dev.of_node 指向device_node, 可以通过它获得其他属性;

3、platform_device 如何与 platform_driver 配对

从设备树转换得来的 platform_device 会被注册进内核里,以后当我们每注册一个 platform_driver
时,它们就会两两确定能否配对,如果能配对成功就调用 platform_driver 的 probe 函数。
下图为platform_device与platform_driver 的匹配过程:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值