侧边栏壁纸
博主头像
Into The Abyss 博主等级

My Life is a Death Race

  • 累计撰写 34 篇文章
  • 累计创建 7 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

Linux设备树学习

Administrator
2024-03-27 / 0 评论 / 0 点赞 / 587 阅读 / 0 字

Linux设备树(Device Tree)是一种描述硬件信息的数据结构,用于在嵌入式系统中描述硬件的结构和连接关系。它是一种平台无关的描述方式,允许在不修改内核源代码的情况下描述硬件的特性。主要用于arm架构。

DTS、DTB、DTC之间的关系

DTS:相当于c源文件;
DTB:相当于c源文件编译出的二进制可执行文件
DTC:相当于编译器,位于linux/scripts/dtc目录下
编译DTB,可以在linux内核目录下使用make dtbs,这个会编译所有的DTB文件。如果想要编译指定的DTB,可以使用make xxx.dtb,将xxx替换为你想要编译的设备树名

DTS语法

设备树也有头文件,可以使用#include<xxx.dtsi>引入
DTS以/开始,DTS文件以节点为基本单位来描述硬件设备和组件。每个节点用于描述一个硬件设备或硬件组件。节点通过花括号 {} 括起来,节点名位于花括号之前,节点属性和子节点位于花括号之间。
节点可以包含一系列属性,用于描述硬件设备的特性和配置。属性由属性名和属性值组成,属性值可以是整数、字符串、数组等形式。
还可以使用&节点名进行属性的追加
例如:

intc:interrupt-controller@00a01000{
/* intc 为节点名
interrupt-controller 为标签名
@00a01000 一般为外设寄存器的起始地址,有时候是设备地址或有其他含义
*/
}

系统启动之后,dts中的设备树信息可以在/proc/device-tree/目录下完整体现

特殊节点

aliases 节点通常位于设备树的顶层,其中包含一系列设备别名及其对应的设备节点路径。当设备需要在设备树中被引用时,可以使用这些别名来代替完整的设备节点路径,从而简化设备树的描述。

aliases {
    serial0 = &uart0; // 定义了名为 serial0 的别名,其对应的设备节点路径是 uart0
    serial1 = &uart1; // 定义了名为 serial1 的别名,其对应的设备节点路径是 uart1
};

在这个例子中,定义了两个设备别名 serial0 和 serial1,分别对应着设备节点 uart0 和 uart1。当需要引用这些设备时,可以直接使用别名,而不需要写出完整的设备节点路径。

使用设备别名的好处在于,如果设备节点的路径发生变化,只需在 aliases 节点中更新对应的别名即可,而不需要修改所有引用该设备的地方。

chosen 节点在设备树中具有特殊的作用,它用于指定一些全局的配置信息或者系统属性,供内核或引导加载程序使用。
一些常见的 chosen 节点的属性及其作用
bootargs:指定内核启动参数。这个属性通常包含了内核启动时的命令行参数,如启动内核时的控制台信息、根文件系统的位置等。

stdout-path:指定控制台输出设备的路径。这个属性描述了内核启动时输出信息的目标设备,如串口设备的路径。

linux,initrd-startlinux,initrd-end:指定内核镜像初始化 RAM Disk (initrd) 的位置。这两个属性指定了 initrd 的起始地址和结束地址。

其他:chosen 节点还可以包含其他一些全局属性,这些属性的具体作用取决于系统的需求和配置。

chosen {
    bootargs = "console=ttyS0,115200 root=/dev/mmcblk0p1";
    stdout-path = "/soc/serial@12340000";
    linux,initrd-start = <0x80000000>;
    linux,initrd-end = <0x82000000>;
};

特殊的属性

#address-cells#size-cells 是设备树中的一种特殊属性,用于描述设备节点地址和大小的单元数。这两个属性通常出现在具有子节点的设备节点中,并且用于指定子节点中地址和大小描述的格式。两个属性的值都为无符号32位整型
#address-cells:指定设备子节点地址部分的单元数。每个单元表示一个地址单元的位宽,例如 32 位或 64 位。这个属性的值决定了设备节点中地址部分的格式。
#size-cells:指定设备子节点大小部分的单元数。每个单元表示一个大小单元的位宽,例如 32 位或 64 位。这个属性的值决定了设备节点中大小部分的格式。

compatible属性:用于描述设备节点与设备驱动程序之间的兼容关系。该属性的作用是告诉内核,该设备节点描述的硬件设备与哪个设备驱动程序兼容,从而内核能够正确地加载相应的设备驱动程序来管理这个设备。
通常是一个字符串或一个字符串列表,可以是设备名称、厂商名称、设备型号等,也可以是一些特定的字符串,用于指定特定的设备类型或功能。

reg属性:描述设备的地址空间,包括设备的起始地址和大小,用于告诉内核设备的物理地址范围。

interrupts属性:描述设备的中断信息,包括中断号、中断类型等,用于告诉内核设备的中断配置。

status属性:描述设备的状态,通常包括 “okay” 表示设备正常,“disabled” 表示设备被禁用。

clocks属性:描述设备所需的时钟源信息,包括时钟控制器的名称和时钟分频等配置。

uart0: serial@12340000 {
    compatible = "vendor,my_uart";
    reg = <0x12340000 0x1000>; // 说明设备的起始地址为0x12340000,长度为0x1000;如果他有父节点那么#address-cells = <1>;#size-cells = <1>;
    #address-cells = <1>; // 指定地址单元的位宽为1,即32位
    #size-cells = <0>; // 指定大小单元的位宽为0,即没有大小信息
    ...
};

ranges属性:ranges属性用于描述设备之间的地址映射关系。在设备树中,不同设备的地址空间可能是不连续的,而ranges属性可以帮助内核将不同设备的地址空间映射到连续的地址空间中,使得操作系统能够正确地访问这些设备。
dma-ranges属性:dma-ranges属性用于描述DMA设备和CPU之间的地址映射关系。DMA设备通常会使用自己的地址空间进行数据传输,而dma-ranges属性可以帮助内核将DMA设备的地址空间映射到CPU可访问的物理地址空间中,以便CPU能够与DMA设备进行数据交换。
ranges属性dma-ranges属性用法相似,格式通常是: ,其中child-bus-address表示设备的地址空间在子总线上的起始地址,parent-bus-address表示设备地址空间在父总线上的起始地址,size表示地址空间的大小。

以树莓派4B设备树中的linux/arch/arm/boot/dts/broadcom/bcm2711.dtsi为例说明一下

/ {
	#address-cells = <2>;
	#size-cells = <1>;
	soc {
		ranges = <0x7e000000  0x0 0xfe000000  0x01800000>,
			     <0x7c000000  0x0 0xfc000000  0x02000000>,
				 <0x40000000  0x0 0xff800000  0x00800000>;
		dma-ranges = <0xc0000000  0x0 0x00000000  0x40000000>;
		...
	}
	...
}

soc节点中通常包含一些常用的外设,如gpio,uart,iic,spi等,因为他的父节点中“#address-cells = <2>;#size-cells = <1>;” 所以"/"对应的子节点soc中的地址占两个位置,大小占一个位置,所以<>里面有四个值。
对于<0x7e000000 0x0 0xfe000000 0x01800000>,源地址范围为0x7e000000 到 0x7e000000+0x01800000-1=0x7f7fffff,映射到系统总线的地址范围为0xfe000000 到 0xff7fffff。
对于<0x7c000000 0x0 0xfc000000 0x02000000>,源地址范围为0x7c000000 到 0x7x000000+0x02000000-1=0x7dffffff,映射到系统总线的地址范围为0xfc000000 到 0xfdffffff。
对于<0x40000000 0x0 0xff800000 0x00800000>,源地址范围为0x40000000 到 0x40000000+0x00800000-1=0x407fffff,映射到系统总线的地址范围为0xff800000 到 0xffffffff。
对于<0xc0000000 0x0 0x00000000 0x40000000>,源地址范围为0xc0000000 到 0xc0000000+0x40000000-1=0xffffffff,映射到系统总线的地址范围为0x00000000 到 0x3fffffff。
这也就是为什么明明数据手册中gpio的起始基地址为0x7e200000,但是使用时需要使用0xfe200000

其他一些具体的用法可以参考linux/Documentation/devicetree/bindings/目录下的绑定文档

linux内核of操作函数

在 Linux 驱动开发中,“of” 操作函数指的是用于操作设备树(Device Tree)的函数。通过"of"操作函数驱动程序可以从设备树中获取设备节点的属性、资源信息等,并根据这些信息进行设备的初始化、注册和配置。“of” 操作函数的定义可以参考linux/include/linux/of.h,常见的设备树操作函数包括:

of_find_node_by_name():根据节点名称查找设备树中的节点。
of_get_child_by_name():查找指定节点的子节点。
of_property_read_u32():读取设备树节点中的属性值(32位整数)。
of_find_node_by_path() :用于通过路径查找设备树中的节点。这个函数允许驱动程序通过设备树路径来获取特定节点的引用,而不仅仅是通过节点名称
of_find_property():在设备树节点中查找指定名称的属性,并返回属性结构的引用。
of_property_count_elems_of_size():计算属性中具有指定大小的元素的数量。
这些函数允许驱动程序通过设备树获取设备的各种属性和资源信息,从而完成设备的初始化和配置。这样可以使得驱动程序更加通用,不需要硬编码设备的信息,而是根据设备树中描述的信息进行配置。
对应的函数原型:

struct device_node *of_find_node_by_name(struct device_node *np, const char *name);
struct device_node *of_get_child_by_name(const struct device_node *parent, const char *name);
int of_property_read_u32(const struct device_node *np, const char *propname, u32 *out_value);
struct device_node *of_find_node_by_path(const char *path);
const struct property *of_find_property(const struct device_node *np, const char *name, int *lenp);
int of_property_count_elems_of_size(const struct device_node *np,const char *propname, int size);

0

评论区