设备树简介

  描述设备树的文件叫做 DTS(DeviceTree Source),这个 DTS 文件采用树形结构描述板级设备,也就是电路板上的设备信息。比如CPU 数量、 内存基地址、 IIC 接口上接了哪些设备、 SPI 接口上接了哪些设备等等。
  设备树(Flattened Device Tree),将描述板级硬件信息的内容从 Linux 内中分离开来,用一个专属的文件格式来描述,这个专属的文件就叫做设备树,文件扩展名为.dts。 一个 SOC 可以作出很多不同的板子,这些不同的板子共同的信息可以提取出来作为一个通用的文件,其他的.dts 文件直接引用这个通用文件即可,这个通用文件就是.dtsi 文件,类似于 C 语言中的头文件。一般.dts 描述板级信息(也就是开发板上有哪些 IIC 设备、 SPI 设备等), .dtsi 描述 SOC 级信息(也就是 SOC 有几个 CPU、主频是多少、各个外设控制器信息等)。

一、DTS、 DTB 和 DTC

  DTS 是设备树源码文件, DTB 是将DTS 编译以后得到的二进制文件。将.dts 编译为.dtb需要用到 DTC 工具。DTC 工具源码在 Linux 内核的 scripts/dtc 目录下。

二、DTS 语法

2.1 、dtsi 头文件

  和 C 语言一样,设备树也支持头文件,设备树的头文件扩展名为.dtsi。例如在 树莓派内核文件bcm2711-rpi-4-b.dts中有如下所示内容:

#include "bcm2711.dtsi"
#include "bcm2835-rpi.dtsi"

  因此在.dts 设备树文件中,可以通过“#include”来引用.h、 .dtsi 和.dts 文件。一般.dtsi 文件用于描述 SOC 的内部外设信息,比如 CPU 架构、主频、外设寄存器地址范围,比如 UART、 IIC 等等。

2.2、设备节点

  设备树是采用树形结构来描述板子上的设备信息的文件,每个设备都是一个节点,叫做设备节点,每个节点都通过一些属性信息来描述节点信息,属性就是键—值对。例如bcm2835-rpi.dtsi设备树文件内容

#include <dt-bindings/power/raspberrypi-power.h>

/ {
    
    
	leds {
    
    
		compatible = "gpio-leds";

		act {
    
    
			label = "ACT";
			default-state = "keep";
			linux,default-trigger = "heartbeat";
		};
	};

	soc {
    
    
		firmware: firmware {
    
    
			compatible = "raspberrypi,bcm2835-firmware", "simple-bus";
			mboxes = <&mailbox>;
			dma-ranges;

			firmware_clocks: clocks {
    
    
				compatible = "raspberrypi,firmware-clocks";
				#clock-cells = <1>;
			};
		};

		power: power {
    
    
			compatible = "raspberrypi,bcm2835-power";
			firmware = <&firmware>;
			#power-domain-cells = <1>;
		};

		vchiq: mailbox@7e00b840 {
    
    
			compatible = "brcm,bcm2835-vchiq";
			reg = <0x7e00b840 0x3c>;
			interrupts = <0 2>;
		};
	};
};

&gpio {
    
    
	pinctrl-names = "default";

	gpioout: gpioout {
    
    
		brcm,pins = <6>;
		brcm,function = <BCM2835_FSEL_GPIO_OUT>;
	};

	alt0: alt0 {
    
    
		brcm,pins = <4 5 7 8 9 10 11>;
		brcm,function = <BCM2835_FSEL_ALT0>;
	};
};

&i2c0if {
    
    
	status = "okay";
	clock-frequency = <100000>;
};

&i2c0mux {
    
    
	pinctrl-0 = <&i2c0_gpio0>;
	/* pinctrl-1 varies based on platform */
	status = "okay";
};

&i2c1 {
    
    
	pinctrl-names = "default";
	pinctrl-0 = <&i2c1_gpio2>;
	status = "okay";
	clock-frequency = <100000>;
};

&usb {
    
    
	power-domains = <&power RPI_POWER_DOMAIN_USB>;
};

&vec {
    
    
	power-domains = <&power RPI_POWER_DOMAIN_VEC>;
	status = "okay";
};

&dsi0 {
    
    
	power-domains = <&power RPI_POWER_DOMAIN_DSI0>;
};

&dsi1 {
    
    
	power-domains = <&power RPI_POWER_DOMAIN_DSI1>;
};

  “/”是根节点,每个设备树文件只有一个根节点,两个文件根节点的内容会合并成一个根节点。“leds”、“soc”是子节点。设备树中节点命名格式如下:

node-name@unit-address

  其中“node-name”是节点名字,为 ASCII 字符串,节点名字应该能够清晰的描述出节点的功能,比如“uart1”就表示这个节点是 UART1 外设。“unit-address”一般表示设备的地址或寄存器首地址,如果某个节点没有地址或者寄存器的话“unit-address”可以不要,比如“cpu@0”、

“interrupt-controller@00a01000”。

  我们看到的有节点命名却如下所示:

vchiq: mailbox@7e00b840

  上述命令并不是“node-name@unit-address”这样的格式,而是用“:”隔开成了两部分,“:”前面的是节点标签(label),“:”后面的才是节点名字,格式如下所示:

label: node-name@unit-address

  引入 label 的目的就是为了方便访问节点,可以直接通过&label 来访问这个节点,比如通过&vchiq就可以访问“mailbox@7e00b840”这个节点,而不需要输入完整的节点名字。
  每个节点都有不同属性,不同的属性又有不同的内容,属性都是键值对,值可以为空或任意的字节流。设备树源码中常用的几种数据形式如下所示:

2.2.1、字符串

  compatible = “gpio-leds”;
  上述代码设置 compatible 属性的值为字符串“gpio-leds”。

扫描二维码关注公众号,回复: 13164296 查看本文章

2.2.2、 32 位无符号整数

  reg = <0>;
  上述代码设置 reg 属性的值为 0, reg 的值也可以设置为一组值,比如:
  reg = <0 0x123456 100>;

2.2.3、字符串列表

  属性值也可以为字符串列表,字符串和字符串之间采用“,”隔开,如下所示:
  compatible = “raspberrypi,bcm2835-firmware”, “simple-bus”;
  上述代码设置属性 compatible 的值为“raspberrypi,bcm2835-firmware”和“simple-bus”。

2.3、标准属性

  节点是由一堆的属性组成,节点都是具体的设备,不同的设备需要的属性不同。

2.3.1、 compatible 属性

  compatible 属性的值是一个字符串列表, compatible 属性用于将设备和驱动绑定起来。字符串列表用于选择设备所要使用的驱动程序, compatible 属性的值格式如下所示:

"manufacturer,model"

  其中 manufacturer 表示厂商, model 一般是模块对应的驱动名字。一般驱动程序文件都会有一个 OF 匹配表,此 OF 匹配表保存着一些 compatible 值,如果设备节点的 compatible 属性值和 OF 匹配表中的任何一个值相等,那么就表示设备可以使用这个驱动。

2.3.2、 model 属性

  model 属性值也是一个字符串,一般 model 属性描述设备模块信息,比如名字什么的,比如:
  model = “wm8960-audio”;

2.3.3、 status 属性

  status 属性是和设备状态有关的, status 属性值也是字符串,字符串是设备的状态信息,可选的状态:
  “okay”   表明设备是可操作的。
  “disabled”   表明设备当前是不可操作的,但是在未来可以变为可操作的,比如热插拔设备插入以后。至于 disabled 的具体含义还要看设备的绑定文档。
  “fail”   表明设备不可操作,设备检测到了一系列的错误,而且设备也不大可能变得可操作。
  “fail-sss”   含义和“fail”相同,后面的 sss 部分是检测到的错误内容。

2.3.4、 #address-cells 和#size-cells 属性

  这两个属性的值都是无符号 32 位整形, #address-cells 和#size-cells 这两个属性可以用在任何拥有子节点的设备中,用于描述子节点的地址信息。 #address-cells 属性值决定了子节点 reg 属性中地址信息所占用的字长(32 位), #size-cells 属性值决定了子节点 reg 属性中长度信息所占的字长(32 位)。 #address-cells 和#size-cells 表明了子节点应该如何编写 reg 属性值,一般 reg 属性都是和地址有关的内容,和地址相关的信息有两种:起始地址和地址长度, reg 属性的格式一为:
  reg = <address1 length1 address2 length2 address3 length3……>
  每个“address length”组合表示一个地址范围,其中 address 是起始地址, length 是地址长度, #address-cells 表明 address 这个数据所占用的字长, #size-cells 表明 length 这个数据所占用的字长,比如:

spi4 {
    
    
    compatible = "spi-gpio";
    #address-cells = <1>;
    #size-cells = <0>;

    gpio_spi: gpio_spi@0 {
    
    
      compatible = "fairchild,74hc595";
      reg = <0>;
    };
};
aips3: aips-bus@02200000 {
    
    
    compatible = "fsl,aips-bus", "simple-bus";
    #address-cells = <1>;
    #size-cells = <1>;

    dcp: dcp@02280000 {
    
    
      compatible = "fsl,imx6sl-dcp";
      reg = <0x02280000 0x4000>;
    };
};

  第 3, 4 行,节点 spi4 的#address-cells = <1>, #size-cells = <0>,说明 spi4 的子节点 reg 属性中起始地址所占用的字长为 1,地址长度所占用的字长为 0。
  第 8 行,子节点 gpio_spi: gpio_spi@0 的 reg 属性值为 <0>,因为父节点设置了#addresscells = <1>, #size-cells = <0>,因此 addres=0,没有 length 的值,相当于设置了起始地址,而没有设置地址长度。
  第 14, 15 行,设置 aips3: aips-bus@02200000 节点#address-cells = <1>, #size-cells = <1>,说明 aips3: aips-bus@02200000 节点起始地址长度所占用的字长为 1,地址长度所占用的字长也为 1。
  第 19 行,子节点 dcp: dcp@02280000 的 reg 属性值为<0x02280000 0x4000>,因为父节点设置了#address-cells = <1>, #size-cells = <1>, address= 0x02280000, length= 0x4000,相当于设置了起始地址为 0x02280000,地址长度为 0x40000。

2.3.5、 reg 属性

  reg 属性前面已经提到过了, reg 属性的值一般是(address, length)对。 reg 属性一般用于描述设备地址空间资源信息,一般都是某个外设的寄存器地址范围信息

2.3.6、 ranges 属性

  ranges属性值可以为空或者按照(child-bus-address,parent-bus-address,length)格式编写的数字矩阵, ranges 是一个地址映射/转换表, ranges 属性每个项目由子地址、父地址和地址空间长度这三部分组成:
  child-bus-address:子总线地址空间的物理地址,由父节点的#address-cells 确定此物理地址所占用的字长。
  parent-bus-address: 父总线地址空间的物理地址,同样由父节点的#address-cells 确定此物理地址所占用的字长。
  length: 子地址空间的长度,由父节点的#size-cells 确定此地址长度所占用的字长。
  如果 ranges 属性值为空值,说明子地址空间和父地址空间完全相同,不需要进行地址转换,对于 I.MX6ULL 来说,子地址空间和父地址空间完全相同,因此会在 imx6ull.dtsi中找到大量的值为空的 ranges 属性,如下所示:

	soc {
    
    
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "simple-bus";
		interrupt-parent = <&gpc>;
		ranges;
		......
		}

  ranges 属性不为空的示例代码如下所示:

soc {
    
    
	compatible = "simple-bus";
	#address-cells = <1>;
 	#size-cells = <1>;
	ranges = <0x0 0xe0000000 0x00100000>;

	serial {
    
    
		device_type = "serial";
		compatible = "ns16550";
		reg = <0x4600 0x100>;
		clock-frequency = <0>;
		interrupts = <0xA 0x8>;
		interrupt-parent = <&ipic>;
	};
};

  第 5 行,节点 soc 定义的 ranges 属性,值为<0x0 0xe0000000 0x00100000>,此属性值指定了一个 1024KB(0x00100000)的地址范围,子地址空间的物理起始地址为 0x0,父地址空间的物理起始地址为 0xe0000000。
  第 10 行, serial 是串口设备节点, reg 属性定义了 serial 设备寄存器的起始地址为 0x4600,寄存器长度为 0x100。经过地址转换, serial 设备可以从 0xe0004600 开始进行读写操作,0xe0004600=0x4600+0xe0000000。

猜你喜欢

转载自blog.csdn.net/xxxx123041/article/details/119959805