设备树文件理解基础入门

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:

本文是我个人对《devicetree-specification-v0.4》原文解读结合一些相关资料的整理笔记,适合人群:linux驱动开发工程师


提示:以下是本篇文章正文内容,下面案例可供参考

一、设备树是什么?

设备树(Device Tree)是一种用于描述硬件设备和系统配置的机制,设备树的作用就是描述一个硬件平台的硬件资源。设备树的使用可以提高系统的灵活性和可移植性。设备树是一种树数据结构,其节点用于描述系统中的设备。每个节点都有属性/值对,描述所表示设备的特征。每个节点只有一个父节点,但根节点除外,它没有父级。
在这里插入图片描述

二、设备树文件.dts

2.1.节点

节点有如下格式

节点标签:节点名称@单元地址{
    节点属性名 = …;
    子节点;
};

举例如下,其描述了STM32的外设DAC数模转换器的一些信息

dac: dac@40017000 {
	compatible = "st,stm32h7-dac-core";
	reg = <0x40017000 0x400>;
	clocks = <&rcc DAC12>;
	clock-names = "pclk";
	#address-cells = <1>;
	#size-cells = <0>;
	status = "disabled";

	dac1: dac@1 {
		compatible = "st,stm32-dac";
		#io-channels-cells = <1>;
		reg = <1>;
		status = "disabled";
	};

	dac2: dac@2 {
		compatible = "st,stm32-dac";
		#io-channels-cells = <1>;
		reg = <2>;
		status = "disabled";
	};
};

2.1.1. 节点标签

节点标签是节点名的简写,它的作用是当其它位置需要引用时可以使用节点标签来向该节点中追加/修改内容,追加内容举例如下

&dac {
	pinctrl-names = "default";
	pinctrl-0 = <&dac_ch1_pins_a &dac_ch2_pins_a>;
	vref-supply = <&vdda>;
	status = "okay";
	dac1: dac@1 {
		status = "okay";
	};
	dac2: dac@2 {
		status = "okay";
	};
};

2.1.2.节点名称

设备树中的每个节点都根据以下约定进行命名:
长度为1至31个字符,只能由如下字符组成
在这里插入图片描述

2.1.3.节点别名

aliases子节点的作用就是为其他节点起一个别名,在驱动中如果要查找一个节点,通常情况下我们可以使用“节点路径”一步步找到节点。 也可以使用别名“一步到位”找到节点。

aliases {
	i2c0 = &i2c0;
	i2c1 = &i2c1;
	i2c2 = &i2c2;
	serial0 = &uart0;
	serial1 = &uart1;
	serial2 = &uart2;
	serial3 = &uart3;
	serial4 = &uart4;
	serial5 = &uart5;
	d-can0 = &dcan0;
	d-can1 = &dcan1;
	usb0 = &usb0;
	usb1 = &usb1;
	phy0 = &usb0_phy;
	phy1 = &usb1_phy;
	ethernet0 = &cpsw_emac0;
	ethernet1 = &cpsw_emac1;
	spi0 = &spi0;
	spi1 = &spi1;
};

i2c0: i2c@11000 {
	compatible = "marvell,mv64xxx-i2c";
	#address-cells = <1>;
	#size-cells = <0>;
	interrupts = <31>;
	timeout-ms = <1000>;
	clocks = <&coreclk 0>;
	status = "disabled";
};

uart0: serial@12000 {
	compatible = "snps,dw-apb-uart";
	reg = <0x12000 0x100>;
	reg-shift = <2>;
	interrupts = <41>;
	reg-io-width = <1>;
	clocks = <&coreclk 0>;
	status = "disabled";
};

例如,假设你有另一个设备需要引用 uart0 节点:

some_device {
    uart = <&serial0>;
};

2.1.3.单元地址

它的值要和节点“reg”属性的第一个地址一致。如果节点没有“reg”属性值,则必须省略 @符号和单元地址

2.1.4.节点路径名

设备树中的节点可以通过指定从根节点到所有后代节点到所需节点的完整路径来唯一标识,类似文件路径,举例如下

/node-name-1/node-name-2/node-nam
/cpus/cpu@1
/dac@40017000/dac@1

2.1.5.节点属性

设备树中的每个节点都有描述节点特征的属性。属性由名称和值组成,编写设备树最主要的内容是编写节点的节点属性,通常情况下一个节点代表一个设备,属性名称的命名要求:长度为1至31个字符,只能由如下字符组成
在这里插入图片描述

节点属性分为标准属性自定义属性,也就是说我们在设备树中可以根据自己的实际需要定义、添加设备属性。 标准属性的属性名是固定的,自定义属性名可按照要求自行定义。

2.1.5.1标准属性

2.1.5.2.compatible属性

属性值类型:字符串
compatible属性值由一个或多个字符串组成,有多个字符串时使用“,”分隔开,客户端程序应使用此字符串列表来选择设备驱动程序。
例:

compatible = "st,stm32mp157a-dk1", "st,stm32mp157";
2.1.5.3.model属性

属性值类型:字符串
model属性用于指定设备的制造商和型号,推荐使用“制造商, 型号”的格式
例:

model = "ARM RealView EB Cortex A9 MPCore";
2.1.5.4.status属性

属性值类型:字符串
通过status可以去禁止设备或者启用设备,可用的操作状态如下表。默认情况下不设置status属性设备是使能的。
例:

dac2: dac@2 {
	status = "okay";
};
2.1.5.5. phandle属性

phandle 属性指定节点的数字标识符,该标识符在 设备树中是唯一的。phandle 属性值由需要引用该属性关联的节点的其他节点使用。DTS 中的大多数 devicetree将不包含显式 phandle 属性。DTC 工具自动将 DTS 编译为二进制 DTB 格式时插入 phandle 属性。
例:
在下面一个片段中,pic@10000000 是一个独立的节点,并且通过 phandle 引用被 another-device-node 使用。这种方式使得 pic@10000000 节点可以被多个其他节点共享。

pic@10000000 {
   phandle = <1>;
   interrupt-controller;
   reg = <0x10000000 0x100>;
};
another-device-node {
   interrupt-parent = <1>;
};
2.1.5.6.#address-cells

#address-cells#size-cells属性同时存在,它们用在有子节点的设备节点(节点),用于设置子节点的“reg”属性的“书写格式”,描述应如何寻址子设备节点。如果缺省,则客户端程序应假定#address-cells 单元的默认值为 2,#size-cells的默认值为 1。

soc {
   #address-cells = <1>;
   #size-cells = <1>;
   
   serial@4600 {
      compatible = "ns16550";
      reg = <0x4600 0x100>;
      clock-frequency = <0>;
      interrupts = <0xA 0x8>;
      nterrupt-parent = <&ipic>;
   };
};

其中,0x4600为基地址,0x100为大小

 reg = <0x4600 0x100>;
2.1.5.7.reg属性

属性值: 编码为任意数量的 (address, length) 对。
reg 属性描述设备资源在其父总线定义的地址空间内的地址。最常见的是,这意味着 memory-mapped IO register blocks 的偏移量和长度,但在某些 bus 类型上可能具有不同的含义。根节点定义的地址空间中的地址是 CPU 实际地址。
指定地址和长度所需的信元数是特定于总线的,由器件节点父项中的 #address-cells 和 #size-cells 属性指定。如果父节点为#size-cells单元格指定值 0,则应省略 reg 值中的 length 字段。
假设有

#address-cells = <1>;
#size-cells = <1>;

假设片上系统中的器件有两个寄存器块,一个 32 字节的块位于 SOC 的偏移量 0x3000,另一个 256 字节的块位于偏移量 0xFE00。reg 属性将按如下方式编码

reg = <0x3000 0x20 0xFE00 0x100>
2.1.5.8.ranges属性

值类型: 编码为任意数量的 (child-bus-address, parent-busaddress, length) 三元组。
ranges 属性提供了一种定义总线地址空间(子地址空间)和总线节点的父地址空间(父地址空间)之间的映射或转换的方法。

以ranges=<0x0 0x10 0x20>为例,表示将子地址的从0x0~(0x0 + 0x20)的地址空间映射到父地址的0x10~(0x10 + 0x20)。
例:

ranges = <0x0 0xe0000000 0x001000>;

表示将0x0 ~(0x0+0x001000)地址映射到0xe0000000 ~(0xe0000000+0x001000)

2.1.5.2自定义属性

2.1.6. 追加/修改节点内容

“&adc1”表示向“节点标签”为“adc1”的节点追加/修改属性值,

&adc1 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_adc1>;
	status = "okay";
};
  • &adc1: 这是一个对名为 adc1 的设备节点的引用。这个节点通常在设备树的其他地方定义,表示一个 ADC (模拟到数字转换器) 设备。

  • pinctrl-names = “default”;: 这个属性指定了引脚控制(pin control)配置的名字。在设备树中,引脚控制配置可以有多个命名,“default” 是一个常见的命名,表示设备的默认引脚配置。

  • pinctrl-0 = <&pinctrl_adc1>;: 这个属性指向一个引脚控制配置的节点,< &pinctrl_adc1 > 是一个对名为 pinctrl_adc1 的节点的引用。pinctrl_adc1 节点定义了与 adc1 设备相关的具体引脚设置。

  • status = “okay”;: 这个属性表示该设备节点是启用的状态。设备树中的 status 属性通常可以设置为 “okay”、“disabled” 或 “reserved”。“okay” 表示设备是可用的,系统将会初始化并使用它。

2.2.中断和中断映射

在 devicetree 中存在一个逻辑中断树,它表示平台硬件中中断的层次结构和路由。
中断源到中断控制器的物理连接在 devicetree 中用 interrupt-parent 属性表示。表示中断生成设备的节点包含一个 interrupt-parent 属性,该属性具有一个 phandle 值,该值指向设备中断路由到的设备,通常是中断控制器。如果中断生成设备没有 interrupt-parent 属性,则假定其中断父级是其 devicetree 父级。每个中断生成设备都包含一个 interrupts 属性,其值描述该设备的一个或多个中断源。每个源都用称为中断说明符的信息表示。在这里插入图片描述

2.2.1.中断生成设备节点的属性

2.2.1.1.interrupt属性

值类型: 编码为任意数量的中断说明符
设备节点的 interrupts 属性定义设备生成的一个或多个中断。interrupts 属性的值由任意数量的中断说明符组成。

interrupts = <0xA 8>;
  • 0xA 是中断控制器的中断号,通常指的是中断号的偏移或中断源。
  • 8 是中断的触发类型或优先级,具体含义取决于硬件和中断控制器的定义。
2.2.1.2.interrupt-paren属性

值类型: < phandle >
由于中断树中节点的层次结构可能与 devicetree 不匹配,因此可以使用 interrupt-parent 属性来显式中断父级的定义。

interrupt-parent = <&adc>;
  • interrupt-parent 属性指明了当前设备的中断控制器,即哪个设备负责处理和管理中断信号。
  • <&adc> 是中断控制器的引用,指向一个设备树节点的标签,这个标签是中断控制器节点的名字。

2.2.2. 中断控制器节点的属性

2.2.2.1.#interrupt-cells属性

值类型: u32

interrupt-cells 是设备树中一个用来描述中断信息的属性。它定义了中断控制器节点下,中断描述所需的单元数目。这通常用于告诉设备树如何解析中断描述中的数据。

用法

  1. 定义: interrupt-cells 属性通常在中断控制器节点中定义。例如:

    interrupt-controller@12300 {
        compatible = "vendor,interrupt-controller";
        #interrupt-cells = <2>;
        ...
    };
    
  2. 解释: 上述定义中的 #interrupt-cells = <2>; 表示中断描述需要两个单元。这两个单元通常包括中断号和中断类型(有时可能包括优先级等)。

  3. 子节点中的使用: 当设备节点需要定义中断时,使用 interrupts 属性来提供具体的中断信息。例如:

    my_device@1000 {
        compatible = "vendor,my-device";
        interrupts = <1 0>; // 这里使用了两单元,符合上面定义的 #interrupt-cells = <2>;
        ...
    };
    

    在这个例子中,<1 0> 代表了中断号和中断类型(例如边沿触发或电平触发),符合 #interrupt-cells 属性所指定的单元数。

2.2.2.2.interrupt-controlle属性

interrupt-controller 属性的存在将节点定义为 interrupt controller 节点。

gic@12300 {
    compatible = "arm,gic-v2";
    reg = <0x12300 0x1000>; // 控制器寄存器的基地址和长度
    interrupt-controller;    // 标记这是一个中断控制器
    #interrupt-cells = <3>;  // 描述中断的单元数,通常是中断编号、触发类型等
    interrupt-parent = <&gic>; // 指定中断的父节点,这里指向自身或其他中断控制器
};

三、设备树描述实例

3.1.Led灯

由MCU手册已知GPIO外设寄存器地址为0x50002000,加上各GPIO寄存器偏移就是各寄存器地址

/*添加led节点*/
rgb_led{
    #address-cells = <1>;
    #size-cells = <1>;
    compatible = "fire,rgb_led";

    /*红灯节点*/
    ranges;
    rgb_led_red@0x50002000{
            compatible = "fire,rgb_led_red";
            reg = < 0x50002000 0x00000004
                    0x50002004 0x00000004
                    0x50002008 0x00000004
                    0x5000200C 0x00000004
                    0x50002018 0x00000004
                    0x50000A28 0x00000004>;
            status = "okay";
    };

3.2.SPI 外设

#define MBUS_ID(target,attributes) (((target) << 24) | ((attributes) << 16))

spi0: spi@10600 {
	reg = <MBUS_ID(0xf0, 0x01) 0x10600 0x28>, /* control */
	      <MBUS_ID(0x01, 0x1e) 0 0xffffffff>, /* CS0 */
	      <MBUS_ID(0x01, 0x5e) 0 0xffffffff>, /* CS1 */
	      <MBUS_ID(0x01, 0x9e) 0 0xffffffff>, /* CS2 */
	      <MBUS_ID(0x01, 0xde) 0 0xffffffff>, /* CS3 */
	      <MBUS_ID(0x01, 0x1f) 0 0xffffffff>, /* CS4 */
	      <MBUS_ID(0x01, 0x5f) 0 0xffffffff>, /* CS5 */
	      <MBUS_ID(0x01, 0x9f) 0 0xffffffff>, /* CS6 */
	      <MBUS_ID(0x01, 0xdf) 0 0xffffffff>; /* CS7 */
	#address-cells = <1>;
	#size-cells = <0>;
	cell-index = <0>;
	interrupts = <30>;
	clocks = <&coreclk 0>;
	status = "disabled";
};

3.2.I2c外设

i2c1: i2c@11100 {
	compatible = "marvell,mv64xxx-i2c";
	#address-cells = <1>;
	#size-cells = <0>;
	interrupts = <32>;
	timeout-ms = <1000>;
	clocks = <&coreclk 0>;
	status = "disabled";
};

3.2.Eeprom设备

&i2c0 {
	pinctrl-names = "default";
	pinctrl-0 = <&i2c0_pins>;
	clock-frequency = <400000>;
	status = "okay";

	tps: pmic@2d {
		reg = <0x2d>;
	};

	i2c_tmp102: temp@4b {
		compatible = "ti,tmp102";
		reg = <0x4b>;
		status = "disabled";
	};

	i2c_eeprom: eeprom@52 {
		compatible = "atmel,24c32";
		pagesize = <32>;
		reg = <0x52>;
		status = "disabled";
	};

	i2c_rtc: rtc@68 {
		compatible = "microcrystal,rv4162";
		reg = <0x68>;
		status = "disabled";
	};
};

3.2.UART 外设

uart0: serial@12000 {
	compatible = "snps,dw-apb-uart";
	reg = <0x12000 0x100>;
	reg-shift = <2>;
	interrupts = <41>;
	reg-io-width = <1>;
	clocks = <&coreclk 0>;
	status = "disabled";
};

总结

本文描述了设备树文件的组成

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值