【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第五十章 Linux设备树

news2024/11/15 11:19:22

i.MX8MM处理器采用了先进的14LPCFinFET工艺,提供更快的速度和更高的电源效率;四核Cortex-A53,单核Cortex-M4,多达五个内核 ,主频高达1.8GHz,2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT、4G模块、CAN、RS485等接口一应俱全。H264、VP8视频硬编码,H.264、H.265、VP8、VP9视频硬解码,并提供相关历程,支持8路PDM接口、5路SAI接口、2路Speaker。系统支持Android9.0(支持获取root限)Linux4.14.78+Qt5.10.1、Yocto、Ubuntu20、Debian9系统。适用于智能充电桩,物联网,工业控制,医疗,智能交通等,可用于任何通用工业和物联网应用、

【粉丝群】258811263


第四部分 Linux驱动进阶

第五十章 Linux设备树

本章导读

在前面章节中提到了设备树的相关内容。但是设备树具体是什么,有什么作用,在本章节中具体讲解一下。掌握设备树是编写设备驱动的一个重点内容,在旧版本的 Linux 内核中没有使用设备树,使用的是平台文件 arch/arm/plat-xxx 和 arch/arm/mach-xxx。但是随着内核的发展,平台文件变得非常冗余和复杂,因此在新版本的 Linux 内核代码中便使用设备树来描述硬件信息。

50.1章节讲解了设备树的由来

50.2章节讲解了什么是设备树

50.3章节讲解了DTS,DTC DTB的概念及关系

50.4章节讲解了DTS设备树语法结构

本章内容对应视频讲解链接(在线观看):

设备树的由来以及基本概 https://www.bilibili.com/video/BV1Vy4y1B7ta?p=24

设备树基本语法  https://www.bilibili.com/video/BV1Vy4y1B7ta?p=25

50.1 设备树的由来

要想了解为什么会有设备树,设备树是怎么来的,我们就要先来回顾一下在没有设备树之前我们是怎么来写一个驱动程序的。以字符设备驱动代码框架为例,我们一起来回顾下。

任何的设备驱动的编写,Linux已经为我们打好了框架,我们只要像做完形填空一样填写进去就可以了。

字符设备驱动框架如下图所示:

 

杂项设备驱动框架: 

通过这些框架,我们可以很容易编写驱动代码,但是,当我们用这个框架非常熟练的时候,我们就会发现虽然这个方法很简单,但是非常不容易扩展,当我们有很多很多相似设备的时候,如果我们都是按照这个框架来完成,那就要写很多遍这个流程,但是多个相似设备之间真正有差异的地方只有框架的初始化硬件的部分,其他步骤的代码基本都是一样的。这样就会造成大量的重复代码。但是,我们在编写驱动代码的时候,我们要尽量做到代码的复用,也就是一套驱动尽量可以兼任很多设备,如果我们还按照这个来编写就不太符合规则了。

为了实现这个目标,我们就要把通用的代码和有差异的代码分离出来,来增强我们驱动代码的可移植性。所以,设备驱动分离的思想也就应运而生了,在Linux中,我们是在写代码的时候进行分离,分离是把一些不相似的东西放到了device.c,把相似的东西放在了driver.c,如果我们有很多相似的设备或者平台,我们只要修改device.c就可以了,这样我们重复性的工作就大大的减少了。这个就是平台总线的由来。

平台总线这个方法有什么弊端呢?

当我们用这个方法用习惯以后就会发现,假如soc不变,我们每换一个平台,都要修改C文件,并且还要重新编译。而且会在arch/arm/plat-xxx和arch/arm/mach-xxx下面留下大量的关于板级细节的代码。并不是说这个方法不好,只是从Linux的发展来看,这些代码相对于Linux内核来说就是“垃圾代码”,而且这些“垃圾代码”非常多,于是就有了Linux Torvalds那句简单粗暴的话:

 

为了改变这个现状,设备树也就被引进到Linux上了,用来剔除相对内核来说的“垃圾代码”,即用设备树文件来描述这些设备信息,也就是代替device.c文件,platform匹配上基本不变,并且相比于之前的方法,使用设备树不仅可以去掉大量“垃圾代码”,并且采用文本格式,方便阅读和修改,如果需要修改部分资源,我们也不用在重新编译内核了,只需要把设备树源文件编译成二进制文件,在通过bootloader传递给内核就可以了。内核在对其进行解析和展开得到一个关于硬件的拓扑图。我们通过内核提供的接口获取设备树的节点和属性就可以了。即内核对于同一soc的不同主板,只需更换设备树文件dtb即可实现不同主板的无差异支持,而无需更换内核文件。 

50.2 什么是设备树?

Device Tree是一种描述硬件的数据结构,由一系列被命名的节点(node)和属性(property)组成,而节点本身可包含子节点。所谓属性,其实就是成对出现的name和value。

在Device Tree中,可描述的信息包括:CPU的数量和类别,内存基地址和大小,总线和桥,外设连接,中断控制器和中断使用情况,GPIO控制器和GPIO使用情况,Clock控制器和Clock使用情况。设备树基本上就是画一棵电路板上由CPU、总线、设备组成的树,Bootloader会将这棵树传递给内核,然后内核可以识别这棵树,并根据它展开出Linux内核中的platform_device、i2c_client、spi_device等设备,而这些设备用到的内存、IRQ等资源,也被传递给了内核,内核会将这些资源绑定给展开的相应的设备。

50.3 DTS 、DTC 和 DTB

文件.dts是一种ASCII文件格式设备树描述,在Linux中,一个.dts文件对应一个ARM设备,一般放置在arch/arm/boot/dts目录下。

dtb文件是dts文件被编译后生成的二进制文件,由Linux内核解析,有了设备树文件就可以在不改动Linux内核的情况下,对不同的平台实现无差异的支持,只需更换相应的dts文件,即可满足。

dtc是将dts编译为dtb的工具。在Linux内核下可以单独编译设备树文件,那么如何确定去编译我们自己的板子对应的dts文件? 以IMX8MM开发板为例,我们来看一下内核源码下的/home/topeet/linux/linux-imx/arch/arm64/boot/dts/freescale/Makefile这个文件的内容:

# SPDX-License-Identifier: GPL-2.0
dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1012a-frdm.dtb
dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1012a-qds.dtb
dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1012a-rdb.dtb
dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1043a-qds.dtb
dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1043a-rdb.dtb
dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-qds.dtb
dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-rdb.dtb
dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1088a-qds.dtb
dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1088a-rdb.dtb
dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls2080a-qds.dtb
dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls2080a-rdb.dtb
dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls2080a-simu.dtb
dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls2088a-qds.dtb
dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls2088a-rdb.dtb
 
dtb-$(CONFIG_ARCH_FSL_IMX8QM) += fsl-imx8qm-lpddr4-arm2.dtb \
				 fsl-imx8qm-lpddr4-arm2-dom0.dtb \
				 fsl-imx8qm-lpddr4-arm2-domu.dtb \
				 fsl-imx8qm-ddr4-arm2.dtb \
				 fsl-imx8qm-ddr4-arm2-hdmi.dtb \
				 fsl-imx8qm-lpddr4-arm2_ca53.dtb \
				 fsl-imx8qm-lpddr4-arm2_ca72.dtb \
				 fsl-imx8qm-mek.dtb \
				 fsl-imx8qm-mek-mipi-ov5640.dtb \
				 fsl-imx8qm-mek-mipi-two-ov5640.dtb \
				 fsl-imx8qm-mek-8cam.dtb \
				 fsl-imx8qm-mek_ca53.dtb \
				 fsl-imx8qm-mek_ca72.dtb \
				 fsl-imx8qm-mek-hdmi.dtb \
				 fsl-imx8qm-mek-hdmi-in.dtb \
				 fsl-imx8qm-mek-dsi-rm67191.dtb \
				 fsl-imx8qm-mek-enet2-tja1100.dtb \
				 fsl-imx8qm-mek-jdi-wuxga-lvds1-panel.dtb \
				 fsl-imx8qm-mek-dom0.dtb \
				 fsl-imx8qm-mek-dom0-dpu2.dtb \
				 fsl-imx8qm-mek-domu.dtb \
				 fsl-imx8qm-mek-domu-car.dtb \
				 fsl-imx8qm-mek-domu-dpu1.dtb \
				 fsl-imx8qm-mek-domu-dpu1-hdmi.dtb \
				 fsl-imx8qm-mek-root.dtb \
				 fsl-imx8qm-mek-inmate.dtb \
				 fsl-imx8qm-mek-m4.dtb \
				 fsl-imx8qm-lpddr4-arm2-dp.dtb \
				 fsl-imx8qm-lpddr4-arm2-hdmi.dtb \
				 fsl-imx8qm-lpddr4-arm2-hdmi-in.dtb \
				 fsl-imx8qm-lpddr4-arm2-8cam.dtb \
				 fsl-imx8qm-lpddr4-arm2-it6263-dual-channel.dtb \
				 fsl-imx8qm-lpddr4-arm2-jdi-wuxga-lvds1-panel.dtb \
				 fsl-imx8qm-lpddr4-arm2-lpspi.dtb \
				 fsl-imx8qm-lpddr4-arm2-spdif.dtb \
				 fsl-imx8qm-lpddr4-arm2-mqs.dtb \
				 fsl-imx8qm-lpddr4-arm2-usb3.dtb \
				 fsl-imx8qm-lpddr4-arm2-dsi-rm67191.dtb \
				 fsl-imx8qm-lpddr4-arm2-enet2-tja1100.dtb \
				 fsl-imx8qm-lpddr4-arm2-hsic.dtb \
				 fsl-imx8dm-lpddr4-arm2.dtb \
				 fsl-imx8qp-lpddr4-arm2.dtb \
				 fsl-imx8qm-lpddr4-arm2-dp-dig-pll.dtb
dtb-$(CONFIG_ARCH_FSL_IMX8QXP) += fsl-imx8qxp-lpddr4-arm2.dtb \
				  fsl-imx8qxp-mek.dtb \
				  fsl-imx8qxp-mek-dsp.dtb \
				  fsl-imx8qxp-mek-dom0.dtb \
				  fsl-imx8qxp-mek-csi.dtb \
				  fsl-imx8qxp-mek-mipi-ov5640.dtb \
				  fsl-imx8qxp-mek-ov5640.dtb \
				  fsl-imx8qxp-mek-enet2.dtb \
				  fsl-imx8qxp-mek-enet2-tja1100.dtb \
				  fsl-imx8qxp-mek-dsi-rm67191.dtb \
				  fsl-imx8qxp-mek-a0.dtb \
				  fsl-imx8qxp-mek-lcdif.dtb \
				  fsl-imx8qxp-mek-it6263-lvds0-dual-channel.dtb \
				  fsl-imx8qxp-mek-it6263-lvds1-dual-channel.dtb \
				  fsl-imx8qxp-mek-jdi-wuxga-lvds0-panel.dtb \
				  fsl-imx8qxp-mek-jdi-wuxga-lvds1-panel.dtb \
				  fsl-imx8qxp-mek-root.dtb \
				  fsl-imx8qxp-mek-m4.dtb \
				  fsl-imx8qxp-mek-inmate.dtb \
				  fsl-imx8qxp-lpddr4-arm2-enet2.dtb \
				  fsl-imx8qxp-lpddr4-arm2-enet2-tja1100.dtb \
				  fsl-imx8qxp-lpddr4-arm2-gpmi-nand.dtb \
				  fsl-imx8qxp-lpddr4-arm2-lpspi.dtb \
				  fsl-imx8qxp-lpddr4-arm2-spdif.dtb \
				  fsl-imx8qxp-lpddr4-arm2-mlb.dtb \
				  fsl-imx8qxp-lpddr4-arm2-mqs.dtb \
				  fsl-imx8qxp-lpddr4-arm2-wm8962.dtb \
				  fsl-imx8qxp-lpddr4-arm2-dsp.dtb \
				  fsl-imx8qxp-lpddr4-arm2-dsi-rm67191.dtb \
				  fsl-imx8qxp-lpddr4-arm2-a0.dtb \
				  fsl-imx8qxp-ddr3l-val.dtb \
				  fsl-imx8dx-lpddr4-arm2.dtb \
				  fsl-imx8dxp-lpddr4-arm2.dtb
dtb-$(CONFIG_ARCH_FSL_IMX8MQ) += fsl-imx8mq-ddr3l-arm2.dtb \
				 fsl-imx8mq-ddr4-arm2.dtb \
				 fsl-imx8mq-ddr4-arm2-gpmi-nand.dtb \
				 fsl-imx8mq-evk.dtb \
				 fsl-imx8mq-evk-b3.dtb \
				 fsl-imx8mq-evk-m4.dtb \
				 fsl-imx8mq-evk-pcie1-m2.dtb \
				 fsl-imx8mq-evk-lcdif-adv7535.dtb \
				 fsl-imx8mq-evk-lcdif-adv7535-b3.dtb \
				 fsl-imx8mq-evk-mipi-csi2.dtb \
				 fsl-imx8mq-evk-pdm.dtb \
				 fsl-imx8mq-evk-dcss-adv7535.dtb \
				 fsl-imx8mq-evk-dcss-adv7535-b3.dtb \
				 fsl-imx8mq-evk-dcss-rm67191.dtb \
				 fsl-imx8mq-evk-dcss-rm67191-b3.dtb \
				 fsl-imx8mq-evk-dual-display.dtb \
				 fsl-imx8mq-evk-dual-display-b3.dtb \
				 fsl-imx8mq-evk-ak4497.dtb \
				 fsl-imx8mq-evk-audio-tdm.dtb \
				 fsl-imx8mq-evk-drm.dtb \
				 fsl-imx8mq-evk-root.dtb \
				 fsl-imx8mq-evk-inmate.dtb \
				 fsl-imx8mq-evk-dp.dtb \
				 fsl-imx8mq-evk-edp.dtb
dtb-$(CONFIG_ARCH_FSL_IMX8MM) += fsl-imx8mm-evk.dtb \
				 itop8mm-evk.dtb \
				 itop8mm-evk-7.0.dtb \
				 itop8mm-evk-9.7.dtb \
				 itop8mm-evk-10.1.dtb \
				 itop8mm-evk-mipi.dtb \
				 fsl-imx8mm-evk-ak4497.dtb \
				 fsl-imx8mm-evk-m4.dtb \
				 fsl-imx8mm-evk-ak5558.dtb \
				 fsl-imx8mm-evk-audio-tdm.dtb \
				 fsl-imx8mm-ddr3l-val.dtb \
				 fsl-imx8mm-ddr4-evk.dtb \
				 fsl-imx8mm-ddr4-val.dtb \
				 fsl-imx8mm-evk-rm67191.dtb \
				 fsl-imx8mm-evk-root.dtb \
				 fsl-imx8mm-evk-revb.dtb \
				 fsl-imx8mm-evk-revb-rm67191.dtb \
				 fsl-imx8mm-ddr4-evk-rm67191.dtb
always		:= $(dtb-y)
subdir-y	:= $(dts-dirs)
clean-files	:= *.dtb

可以看出,当选中某一个选项时,所有使用到设备树文件这会被编译为.dtb。如果我们使用3399新做了一个板子,只需要新建一个此板子对应的.dts 文件,然后将对应的.dtb 文件名添加到 dtb- $(CONFIG_ARCH_ROCKCHIP)下,这样在编译设备树的时候就会将对应的.dts 编译为二进制的.dtb 文件。

其中,DTS,DTSI,DTB,DTC,他们之间的关系如下:

50.4 DTS 设备树语法结构

一般情况下,我们不会从头编写一个完整的dts文件,SOC厂商一般会直接提供一个有着基本框架的dts文件,当需要添加自己的板子设备树文件时,基于厂商提供的dts文件修改即可。所以我们要了解dts设备树文件的语法,这样我们才清楚如何添加我们自己的设备。

50.4.1 dtsi 头文件

由于一个 SOC 可能对应多个 ARM 设备,这些 dts 文件势必包含许多共同的部分,Linux 内核为了简化,把 SOC 公用的部分或者多个设备共同的部分提炼为.dtsi 文件,类似于 C 语言的头文件。device tree source include(dtsi)是更通用的设备树代码,也就是相同芯片但不同平台都可以使用的代码。

.dtsi 文件也可以包含其他的.dtsi。在/home/topeet/linux/linux-imx/arch/arm64/boot/dts/freescale/itop8mm-evk-9.7.dts文件中有如下内容:

#include "itop8mm-evk.dtsi"用“#include”关键字来引用了itop8mm-evk.dtsi文件,也可以像C语言那样来引用.h文件

一般.dtsi 文件用于描述 SOC 的内部外设信息,比如 CPU 架构、主频、外设寄存器地址范围,比如

UART、IIC 等等。比如fsl-imx8mm.dtsi就是描述iMX8MM这个 SOC 内部外设情况信息的,内容如下:

#include "fsl-imx8-ca53.dtsi"
#include <dt-bindings/clock/imx8mm-clock.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/pinctrl/pins-imx8mm.h>
#include <dt-bindings/thermal/thermal.h>

/ {
	compatible = "fsl,imx8mm";
	interrupt-parent = <&gpc>;
	#address-cells = <2>;
	#size-cells = <2>;

	aliases {
		ethernet0 = &fec1;
		i2c0 = &i2c1;
		i2c1 = &i2c2;
		i2c2 = &i2c3;
		i2c3 = &i2c4;
		serial0 = &uart1;
		serial1 = &uart2;
		serial2 = &uart3;
		gpio0 = &gpio1;
		gpio1 = &gpio2;
		gpio2 = &gpio3;
		gpio3 = &gpio4;
		gpio4 = &gpio5;
		spi0 = &flexspi0;
		usb0 = &usbotg1;
		usb1 = &usbotg2;
	};

	cpus {
		idle-states {
			entry-method = "psci";

			CPU_SLEEP: cpu-sleep {
				compatible = "arm,idle-state";
				arm,psci-suspend-param = <0x0010033>;
				local-timer-stop;
				entry-latency-us = <1000>;
				exit-latency-us = <700>;
				min-residency-us = <2700>;
				wakeup-latency-us = <1500>;
			};
		};
	};

	memory@40000000 {
		device_type = "memory";
		reg = <0x0 0x40000000 0 0x80000000>;
	};

	reserved-memory {
		#address-cells = <2>;
		#size-cells = <2>;
		ranges;

		/* global autoconfigured region for contiguous allocations */
		linux,cma {
			compatible = "shared-dma-pool";
			reusable;
			size = <0 0x28000000>;
			alloc-ranges = <0 0x40000000 0 0x80000000>;
			linux,cma-default;
		};
	};

	gic: interrupt-controller@38800000 {
		compatible = "arm,gic-v3";
		reg = <0x0 0x38800000 0 0x10000>, /* GIC Dist */
		      <0x0 0x38880000 0 0xC0000>; /* GICR (RD_base + SGI_base) */
		#interrupt-cells = <3>;
		interrupt-controller;
		interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;
		interrupt-parent = <&gic>;
	};

	timer {
		compatible = "arm,armv8-timer";
		interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(6) | IRQ_TYPE_LEVEL_LOW)>, /* Physical Secure */
			     <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(6) | IRQ_TYPE_LEVEL_LOW)>, /* Physical Non-Secure */
			     <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(6) | IRQ_TYPE_LEVEL_LOW)>, /* Virtual */
			     <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(6) | IRQ_TYPE_LEVEL_LOW)>; /* Hypervisor */
		clock-frequency = <8000000>;
		interrupt-parent = <&gic>;
	};

	clocks {
		#address-cells = <1>;
		#size-cells = <0>;

		osc_32k: clock@0 {
			compatible = "fixed-clock";
			reg = <0>;
			#clock-cells = <0>;
			clock-frequency = <32768>;
			clock-output-names = "osc_32k";
		};

		osc_24m: clock@1 {
			compatible = "fixed-clock";
			reg = <1>;
			#clock-cells = <0>;
			clock-frequency = <24000000>;
			clock-output-names = "osc_24m";
		};

		clk_ext1: clock@2 {
			compatible = "fixed-clock";
			reg = <3>;
			#clock-cells = <0>;
			clock-frequency = <133000000>;
			clock-output-names = "clk_ext1";
		};

		clk_ext2: clock@3 {
			compatible = "fixed-clock";
			reg = <4>;
			#clock-cells = <0>;
			clock-frequency = <133000000>;
			clock-output-names = "clk_ext2";
		};

		clk_ext3: clock@4 {
			compatible = "fixed-clock";
			reg = <5>;
			#clock-cells = <0>;
			clock-frequency = <133000000>;
			clock-output-names = "clk_ext3";
		};

		clk_ext4: clock@5 {
			compatible = "fixed-clock";
			reg = <6>;
			#clock-cells = <0>;
			clock-frequency= <133000000>;
			clock-output-names = "clk_ext4";
		};
	};

	mipi_pd: gpc_power_domain@0 {
		compatible = "fsl,imx8mm-pm-domain";
		#power-domain-cells = <0>;
		domain-id = <0>;
		domain-name = "MIPI_PD";
	};

	pcie0_pd: gpc_power_domain@1 {
		compatible = "fsl,imx8mm-pm-domain";
		#power-domain-cells = <0>;
		domain-id = <1>;
		domain-name = "PCIE0_PD";
	};

	usb_otg1_pd: gpc_power_domain@2 {
		compatible = "fsl,imx8mm-pm-domain";
		#power-domain-cells = <0>;
		domain-id = <2>;
		domain-name = "USB_OTG1_PD";
	};

	usb_otg2_pd: gpc_power_domain@3 {
		compatible = "fsl,imx8mm-pm-domain";
		#power-domain-cells = <0>;
		domain-id = <3>;
		domain-name = "USB_OTG2_PD";
	};

	gpu_2d_pd: gpc_power_domain@4 {
		compatible = "fsl,imx8mm-pm-domain";
		#power-domain-cells = <0>;
		domain-id = <4>;
		domain-name = "GPU_2D_PD";
	};

	gpu_mix_pd: gpc_power_domain@5 {
		compatible = "fsl,imx8mm-pm-domain";
		#power-domain-cells = <0>;
		domain-id = <5>;
		domain-name = "GPU_MIX_PD";
	};

	vpu_mix_pd: gpc_power_domain@6 {
		compatible = "fsl,imx8mm-pm-domain";
		#power-domain-cells = <0>;
		domain-id = <6>;
		domain-name = "VPU_MIX_PD";
	};

	disp_mix_pd: gpc_power_domain@7 {
		compatible = "fsl,imx8mm-pm-domain";
		#power-domain-cells = <0>;
		domain-id = <7>;
		domain-name = "DISP_MIX_PD";
	};

	vpu_g1_pd: gpc_power_domain@8 {
		compatible = "fsl,imx8mm-pm-domain";
		#power-domain-cells = <0>;
		domain-id = <8>;
		domain-name = "VPU_G1_PD";
	};

	vpu_g2_pd: gpc_power_domain@9 {
		compatible = "fsl,imx8mm-pm-domain";
		#power-domain-cells = <0>;
		domain-id = <9>;
		domain-name = "VPU_G2_PD";
	};

	vpu_h1_pd: gpc_power_domain@10 {
		compatible = "fsl,imx8mm-pm-domain";
		#power-domain-cells = <0>;
		domain-id = <10>;
		domain-name = "VPU_H1_PD";
	};

	gpio1: gpio@30200000 {
		compatible = "fsl,imx8mm-gpio", "fsl,imx35-gpio";
		reg = <0x0 0x30200000 0x0 0x10000>;
		interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>,
			     <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
		gpio-controller;
		#gpio-cells = <2>;
		interrupt-controller;
		#interrupt-cells = <2>;
	};

	gpio2: gpio@30210000 {
		compatible = "fsl,imx8mm-gpio", "fsl,imx35-gpio";
		reg = <0x0 0x30210000 0x0 0x10000>;
		interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
			     <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
		gpio-controller;
		#gpio-cells = <2>;
		interrupt-controller;
		#interrupt-cells = <2>;
	};

	gpio3: gpio@30220000 {
		compatible = "fsl,imx8mm-gpio", "fsl,imx35-gpio";
		reg = <0x0 0x30220000 0x0 0x10000>;
		interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>,
			     <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>;
		gpio-controller;
		#gpio-cells = <2>;
		interrupt-controller;
		#interrupt-cells = <2>;
	};
 
                ..................
50.4.2 设备节点信息

设备树从根节点开始,每个设备都是一个节点。根节点就相当于树根。节点和节点之间可以互相嵌套,形成父子关系。可以理解为树枝可以分成好几个小的树枝。设备的属性用key-value对(键值对)来描述,每个属性用分号结束。下面先来看一个设备树结构模板:

1   / {
2       node1 {
3               a-string-property = "A string";
4               a-string-list-property = "first string", "second string";
5               a-byte-data-property = [0x01 0x23 0x34 0x56];
6           child-node1 {
7               first-child-property;
8               second-child-property = <1>;
9               a-string-property = "Hello, world";
10                   };
11          child-node2 {
12                  };
13           };
14      node2 {
15              an-empty-property;
16              a-cell-property = <1 2 3 4>; 
17          child-node1 {
18                      };
19             };
20      }

上面的 dts 文件内容并没有实际的用途,只是基本表示了一个设备树源文件的结构。但是这里面体现了一些属性:

  • 一个单独的根节点:“/”
  • 两个子节点:“node1”和“node2”
  • 两个 node1 的子节点:“child-node1”和“child-node2”
  • 一些分散在树里的属性,属性是最简单的键-值对,它的值可以为空或者包含一个任意的字节流。

虽然数据类型并没有编码进数据结构,但是设备树源文件中仍有几个基本的数据表示形式:

1) 文本字符串(无结束符),可以用双引号表示:

a-string-property = "A string";

2) “cells”是 32 位无符号整数,用尖括号限定:

cell-property = <0xbeef 123 0xabcd1234>;

3) 二进制数据用方括号限定:

binary-property = [0x01 0x23 0x45 0x67];

4) 不同表示形式的数据可以用逗号连在一起:

mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;

5) 逗号也可以用于创建字符串列表:

string-list = "red fish", "blue fish";

下面我们看一下简化之后结构:

1   / {
2       aliases {
3               can0 = &flexcan1;
4               };
5
6       cpus {
7               #address-cells = <1>;
8               #size-cells = <0>;
9
10              cpu0: cpu@0 {
11                    compatible = "arm,cortex-a7";
12                    device_type = "cpu";
13                    reg = <0>;
14                 };
15           };
16
17      intc: interrupt-controller@00a01000 {
18              compatible = "arm,cortex-a7-gic";
19              #interrupt-cells = <3>;
20              interrupt-controller;
21              reg = <0x00a01000 0x1000>,
22              <0x00a02000 0x100>;
23               };
24     }

第 1 行,“/”是根节点。

第 2、6 和 17 行,aliases、cpus 和 intc 是三个子节点。

第 10 行,cpu0 是 cpus 的子节点。

简单来说,节点就好比一棵大树,从树的主干开始,然后有一节一节的树枝,这个就叫节点。在代码中的节点是什么样子的呢。我们把上面模板中的根节点摘出来,如下图所示,这个就是根节点,相当于大树的树干。

/{
};

而树枝就相当于设备树的子节点,同样我们把子节点摘出来就是根节点里面的node1和node2,如下图所示:

/{  //根节点
      node1//子节点node1
      { 
      };
      node2//子节点node2
      { 
      };
 };

一个树枝是不是也可以继续分成好几个树枝呢,也就是说子节点里面可以包含子子节点。所以child-node1和child-node2是node1的子节点,如下图所示:

/{  //根节点
      node1//子节点node1
      { 
          child-node1 //子子节点
          { 
          };
      };
      node2//子节点node2
      { 
          child-node2 //子子节点
          { 
          };
      };
 };
50.4.3 设备节点及lable命名

在前面的代码中,我们注意到节点和子节点之间的命名有所不同,它们都遵循了下面的命名格式:

格式:<名称>[@<设备地址>]

<名称>节点的名称也不是任意起的,一般要体现设备的类型而不是特点的型号,比如网口,应该命名为ethernet,而不是随意起一个,比如111。

<设备地址>就是用来访问该设备的基地址。但并不是说在操作过程中来描述一个地址,他主要用来区分用。

注意事项:

  • 同一级的节点只要地址不一样,名字是可以不唯一的。
  • 设备地址是一个可选选项,可以不写。但为了容易区分和理解,一般是都写的。

当我们找一个节点的时候,我们必须书写完整的节点路径,如果我们的节点名很长,那么我们在引用的时候就十分不方便,所以,设备树允许我们用下面的形式为节点标注引用(起别名)。比如一个动漫人物的名字是蒙其·D·路飞,他的小名是路飞,那是不是小名要比我们的全名更容易记忆了。这个就是别名。

举例:

 uart8: serial@02288000 

其中,uart8就是这个节点名称的别名,serial@02288000就是节点名称。

一般我往一个节点里面添加内容的时候,不会直接把添加的内容写到节点里面,而是通过节点的引用来添加。

举例

&uart8 {
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_uart8>;
        status = "okay";
};

&uart8表示引用节点别名为uart8的节点,并往这个节点里面添加以下内容:

 pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_uart8>;
        status = "okay";

注意事项:

编译设备树的时候,相同的节点的不同属性信息都会被合并,相同节点的相同的属性会被重写,使用引用可以避免四处找节点。如dts和dtsi里面都有根节点,但最终会合并成一个根节点。

50.4.4 标准属性

address-cells和size-cells属性

不同的平台,不同的总线,地址位长度可能不同,有 32 位地址,有 64 位地址,为了适应这个,规范规定一个 32 位的长度为一个 cell。

"#address-cells"属性用来表示总线地址需要几个 cell 表示,该属性本身是u32 类型的。

"#size-cells"属性用来表示子总线地址空间的长度需要几个cell 表示,属性本身的类型也是 u32。

可以这么理解父节点表示总线,总线上每个设备的地址长度以及地址范围是总线的一个特性,用

"#address-cells","#size-cells"属性表示,比如总线是 32 位,那么"#address-cells"设置成 1 就可以了。这两个属性不可以继承,就是说在未定义这两个属性的时候,不会继承更高一级父节点的设置,如果没有设置的话,内核默认认为"#address-cells"为 2,"#size-cells"为 1。举例来说,如下所示:

1 spi4 {
2       compatible = "spi-gpio";
3       #address-cells = <1>;
4       #size-cells = <0>;
5
6       gpio_spi: gpio_spi@0 {
7       compatible = "fairchild,74hc595";
8       reg = <0>;
9                            };
10 };
11
12 aips3: aips-bus@02200000 {
13      compatible = "fsl,aips-bus", "simple-bus";
14      #address-cells = <1>;
15      #size-cells = <1>;
16
17      dcp: dcp@02280000 {
18      compatible = "fsl,imx6sl-dcp";
19      reg = <0x02280000 0x4000>;
20                          };
21 };

第 3,4 行,节点 spi4 的#address-cells = <1>,#size-cells = <0>,说明 spi4 的子节点 reg 属性中起始

地址所占用的字长为 1,地址长度所占用的字长为 0。因此第 8 行 reg 属性值为 <0>,相当于设置了起始

地址,而没有设置地址长度。

第 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>,相当于设置了起始地址为 0x02280000,

地址长度为 0x40000。

reg属性

"reg"属性用来表示节点地址资源的,比如常见的就是寄存器的起始地址及大小。要想表示一块连续地

址,必须包含起始地址和空间大小两个参数,如果有多块地址,那么就需要多组这样的值表示。对于'reg'

属性,每个元素是一个二元组,包含起始地址和大小。还有另外一个问题,地址和大小用几个 u32 表示呢?

这个就由父节点的"#address-cells","#size-cells"属性确定。

例如:

323 uart1: serial@02020000 {
324                compatible = "fsl,imx6ul-uart",
325                 "fsl,imx6q-uart", "fsl,imx21-uart";
326                 reg = <0x02020000 0x4000>;
327                 interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
328                 clocks = <&clks IMX6UL_CLK_UART1_IPG>,
329                 <&clks IMX6UL_CLK_UART1_SERIAL>;
330                 clock-names = "ipg", "per";
331                 status = "disabled";
332 };

上述代码是节点 uart1,uart1 节点描述了 I.MX6ULL 的 UART1 相关信息,重点是第 326 行的 reg 属性。其中 uart1 的父节点 aips1: aips-bus@02000000 设置了#address-cells = <1>、#size-cells = <1>,因此 reg

属性中 address=0x02020000,length=0x4000。

compatible属性

设备树中的每个表示一个设备的节点都需要一个 compatible 属性,compatible 属性是操作系统用来决定设备和驱动绑定的关键因素。compatible 属性也叫做兼容性属性,属性的值是一个字符串列表,用于表示是何种设备,可以在代码中进行匹配。

举例:

compatible = "manufacturer,model"

第一个字符串表示厂商,后面的字符串表示确切的设备名称。比如在 topeet_emmc_4_3.dts 文件中 sound节点表示开发板的音频设备节点,i.MX6ULL终结者开发板上的音频芯片是欧胜(WOLFSON)出品的 WM8960,sound 节点的 compatible 属性值如下:

compatible = "fsl,imx6ul-evk-wm8960","fsl,imx-audio-wm8960";

属性值有两个,分别为“fsl,imx6ul-evk-wm8960”和“fsl,imx-audio-wm8960”,其中“fsl” 表示厂商是

飞思卡尔,“imx6ul-evk-wm8960”和“imx-audio-wm8960”表示设备驱动的名字。sound 这个设备首先使用第一个兼容值在 Linux 内核里面查找,看看能不能找到与之匹配的驱动文件,如果没有找到的话就使用第二个兼容值查找,直到找到或者查找完整个 Linux 内核也没有找到对应的驱动。

status属性

status 属性用来表示节点的状态,其实就是硬件的状态,用字符串表示。

  • “okay”表示硬件正常工作
  • “disable”表示当前硬件不可用
  • “fail”表示因为出错不可用
  • “fail-sss”表示某种原因出错不可用,sss 表示具体出错的原因。

实际中,基本只用“okay”和“disabl”。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1946431.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Gin】架构的精妙编织:Gin框架中组合模式的革新实践与技术深度解析(下)

【Gin】架构的精妙编织&#xff1a;Gin框架中组合模式的革新实践与技术深度解析(下) 大家好 我是寸铁&#x1f44a; 【Gin】架构的精妙编织&#xff1a;Gin框架中组合模式的革新实践与技术深度解析(下)✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 前言 本次文章分为上下两部分…

Typora 【最新1.8.6】版本安装下载教程 (轻量级 Markdown 编辑器),图文步骤详解,免费领取(软件可激活使用)

文章目录 软件介绍软件下载安装步骤激活步骤 软件介绍 Typora 是一款专为 Markdown 爱好者设计的文本编辑器&#xff0c;它结合了简洁的界面设计与强大的 Markdown 渲染能力&#xff0c;为用户提供了一个流畅、高效的写作环境。以下是对 Typora 更详细的介绍&#xff1a; 核心特…

[k8s源码]8.deltaFIFO

deltaFIFO DeltaFIFO: 这是一个特殊类型的队列&#xff0c;它结合了FIFO&#xff08;先进先出&#xff09;队列的特性和增量&#xff08;Delta&#xff09;处理的能力。DeltaFIFO 中是按顺序存储的&#xff0c;但它们不必严格按照发生的顺序逐个处理。这种设计提供了处理的灵…

iPhone 17系列取消17 Plus版本?新一代苹果手机迎来新变革

随着科技的飞速发展&#xff0c;苹果公司再次准备刷新我们的期待&#xff0c;即将推出的iPhone 17系列携带着一系列令人兴奋的升级。今年&#xff0c;苹果打破了常规&#xff0c;将四款新机型带入市场——iPhone 17、17 Pro、17 Pro Max&#xff0c;以及一款全新的成员&#xf…

QT串口和数据库通信

创建串口 串口连接客户端并向服务器发送消息 client.pro #------------------------------------------------- # # Project created by QtCreator 2024-07-02T14:11:20 # #-------------------------------------------------QT core gui network QT core gui…

Music Tag Editor Pro for Mac:强大的音频标签管理工具

Music Tag Editor Pro for Mac是一款专为Mac系统设计的音频标签管理工具&#xff0c;其简易直观的操作界面和强大的功能深受用户喜爱。 这款软件的核心功能在于它能够批量编辑各类音频文件的标签。无论是修改元数据、重命名文件&#xff0c;还是转换音乐标签的文本编码&#x…

(十三)Spring教程——依赖注入之工厂方法注入

1.工厂方法注入 工厂方法是在应用中被经常使用的设计模式&#xff0c;它也是控制反转和单例设计思想的主要实现方法。由于Spring IoC容器以框架的方式提供工厂方法的功能&#xff0c;并以透明的方式开放给开发者&#xff0c;所以很少需要手工编写基于工厂方法的类。正是因为工厂…

参加可观测性Observability Foundation认证培训,您有哪些收益?

一、可观测性认证培训的内容 作为SRE&#xff08;站点可靠性工程&#xff09;课程体系的最新发展&#xff0c;可观测性&#xff08;Observability&#xff09;认证课程介绍了一系列结合应用程序生命周期和复杂体系架构中推进可观测性的核心概念和实践。为关注全栈可观测性&…

Hyper-V 安装 CentOS 8.5

前言 Hyper-V安装文档:在 Windows 10 上安装 Hyper-VCentOS 系统下载:CentOS 国内镜像源 8.5.2111作者:易墨发布时间:2023.10.01原文地址:https://www.cnblogs.com/morang/p/devops-hyperv-centos-install.html使用命令安装 以管理员身份运行 PowerShell 命令: Enable-…

【React】全面解析:从基础知识到高级应用,掌握现代Web开发利器

文章目录 一、React 的基础知识1. 什么是 React&#xff1f;2. React 的基本概念3. 基本示例 二、React 的进阶概念1. 状态&#xff08;State&#xff09;和属性&#xff08;Props&#xff09;2. 生命周期方法&#xff08;Lifecycle Methods&#xff09;3. 钩子&#xff08;Hoo…

pikauchu之Unsafe Fileupload(不安全的文件上传)

Client check&#xff08;客户检查&#xff09; 第一步先新建一个一句话木马 <?php eval($_POST[1]);?> 然后上传文件 有限制&#xff0c;只能上传那几种类型 现在看看源代码 我们将一句话木马文件的后缀改为png 然后用burp抓包&#xff0c;将png改成php 就能上传成功 …

Android中Service学习记录

目录 一 概述二 生命周期2.1 启动服务startService()2.2 绑定服务bindService()2.3 先启动后绑定2.4 先绑定后启动 三 使用3.1 本地服务&#xff08;启动式&#xff09;3.2 可通信的服务&#xff08;绑定式&#xff09;3.3 前台服务3.4 IntentService 总结参考 一 概述 Servic…

无人机之起飞前准备

一、检查无人机状态 1、确保无人机的电池充满电或有足够的电量&#xff1b; 2、检查螺旋桨是否安装牢固&#xff0c;没有损坏&#xff1b; 3、确认无人机的固件是最新版本&#xff0c;以保证拥有最新的功能和修正。 二、选择合适的起飞地点 1、避免在人群密集或有障碍物的…

husky引发git commit报错的解决方案

在git commit的时候&#xff0c;有可能会遇到这样的报错&#xff0c;husky - pre-commit hook exited with code 1 (error) 出现这个问题的原因主要是&#xff0c;假如项目中采用 husky和lint-staged结合进行代码校验&#xff0c;那么&#xff0c;只要项目代码中有不规范的地方…

新手学习python 安装Anaconda

PyCharm选择社区版就够用了&#xff0c;专业版本太多花哨的东西没必要&#xff0c;环境可以用Anaconda&#xff0c;单纯的python环境需要下载很多包 一&#xff1a;安装Anaconda 下载打包坏境 Anaconda 地址 https://www.anaconda.com/&#xff0c;我本地是安装在D:\ProgramD…

CTF ssti零基础(一) 模块注入的payload

这里先推荐一个还不错的工具 git clone https://github.com/vladko312/SSTImap.gitcd SSTImappip install requirements.txt命令还是挺多的 有点像sqlmap但是这个暴力多了不用加那么多的参数 ./sstimap.py -u http://example.com ./sstimap.py -u http://example.com --os-sh…

Word 导入导出

在实际的开发过程中&#xff0c;也会遇到导入导出的功能&#xff0c;今天就简单的做一下总结。 1.需求&#xff1a;将下面word 数据导入到数据库并进行存储 在Controller中 RequestMapping(value "/ImportWord")public RawResponseBodyObject ImportWord(HttpServl…

怎么将jpg图片转换为pdf?将jpg图片转换为pdf的几种方法介绍

怎么将jpg图片转换为pdf&#xff1f;转换jpg图片为PDF是一项常见的任务&#xff0c;无论是个人用户还是商业环境中都经常会遇到这种需求。将多张jpg图像合并成一个单一的PDF文件可以带来诸多便利&#xff0c;例如便于存档、分享和打印。这种转换不仅简化了文件管理流程&#xf…

萝卜快跑:自动驾驶的先锋与挑战

萝卜快跑&#xff1a;自动驾驶的先锋与挑战 近段时间&#xff0c;由萝卜快跑引发的自动驾驶事件如火如荼&#xff0c;成为科技领域的热门话题。萝卜快跑作为自动驾驶领域的重要参与者&#xff0c;其最新事件引发了广泛的关注和讨论。 萝卜快跑是百度推出的自动驾驶出行服务平台…

计算机网络基础:4.HTTP与HTTPS

一、回顾设定 想象你在经营一家繁忙的餐厅&#xff0c;顾客们通过点餐系统&#xff08;网卡&#xff09;下单&#xff0c;订单被前台&#xff08;路由器&#xff09;接收并分发到各个厨房区域&#xff08;网络设备&#xff09;。光猫像是食材供应商&#xff0c;通过高效的物流系…