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

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.dtbdtb-$(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.xdnf.cn/news/1487872.html

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

相关文章

儿童洗衣机什么牌子质量好?五款质量上乘机型推荐

在当今繁忙的生活中&#xff0c;儿童洗衣机已成为我们日常生活中不可或缺的家电。但是&#xff0c;面对市场上众多品牌的儿童洗衣机&#xff0c;那么&#xff0c;到底儿童洗衣机哪个牌子好&#xff1f;本次我将在这篇文章中探讨儿童洗衣机的选购策略&#xff0c;以帮助大家找到…

Logitech Media Server已更名为Lyrion Music Server

还以为是个新软件&#xff0c;折腾之后来才发现 &#xff0c;Lyrion Music Server 原名叫 Logitech Media Server&#xff0c;只是现在它已不再由 Logitech 拥有和维护&#xff0c;已完全移交给社区&#xff0c;因此更名&#xff0c;但简称依然还是 LMS。镜像目前还是延续了 lo…

Python Flask入门到精通:详细教程和实战案例

前言 Flask是一个轻量级的Web框架&#xff0c;用于快速开发Web应用程序。它的设计理念是简洁、灵活和易于扩展&#xff0c;非常适合于从简单的单页应用到复杂的大型项目。通过Flask&#xff0c;可以创建各种Web应用程序&#xff0c;比如博客、电子商务网站、RESTful API等。 …

在 LCD 上显示 png 图片-I.MX6U嵌入式Linux C应用编程学习笔记基于正点原子阿尔法开发板

在 LCD 上显示 png 图片 PNG 简介 无损压缩&#xff1a;PNG 使用 LZ77 派生算法进行无损压缩&#xff0c;确保图像质量不受损&#xff0c;且压缩比高 体积小&#xff1a;通过高压缩比&#xff0c;PNG 文件体积小&#xff0c;适合网络传输 索引彩色模式&#xff1a;PNG-8 格式…

结合el-upload上传组件,验证文件格式及大小

结合el-upload上传组件&#xff0c;验证文件格式及大小 效果如下&#xff1a; 代码如下&#xff1a; upgradeFirmwareInfo.vue页面 <template><div><el-dialog title"新增固件升级包" :visible.sync"dialogFormVisible"top"7vh&qu…

一年八百倍!刘邦不装杯!——早读(逆天打工人爬取热门微信文章解读)

台风格美&#xff0c;是比较倾向内陆的&#xff0c;大家注意安全 引言Python 代码第一篇 洞见 读懂了刘邦的人性哲学&#xff0c;你迟早脱离底层第二篇 看看新闻了 不小心越写越俗结尾 引言 这行情我TM真的越来越看不懂 现在的互联网太恐怖了 以前还好 很多散户都是懵懂懂的 现…

Java语言程序设计基础篇_编程练习题**15.28(显示一个转动的风扇)

**15.28(显示一个转动的风扇) 编写一个程序显示一个转动的风扇&#xff0c;如图15-33c所示。Pause、Resume和Reverse按钮用于暂停、继续和反转风扇的转动 可修改编程练习题14_9的代码 习题思路&#xff1a; 新建一个BorderPane&#xff0c;一个Pane和一个HBox&#xff0c;P…

javafx的ListView代入项目的使用

目录 1. 创建一个可观察的列表&#xff0c;用于存储ListView中的数据,这里的User是包装了用户的相关信息。 2.通过本人id获取friendid&#xff0c;及好友的id&#xff0c;然后用集合接送&#xff0c;更方便直观一点。 3.用for遍历集合&#xff0c;逐个添加。 4.渲染器&…

Python+Pytest+Allure+Yaml+Pymysql+Jenkins+GitLab运行原理

PythonPytestAllureYamlPymysqlJenkinsGitLab运行原理逻辑及调用关系 GitLab代码仓&#xff1a; Jenkins工作空间&#xff1a; 代码&#xff1a; 测试报告展示&#xff1a;

CTF ssrf 基础入门 (一)

0x01 引言 我发现我其实并不是很明白这个东西&#xff0c;有些微妙&#xff0c;而且记忆中也就记得Gopherus这个工具了&#xff0c;所以重新学习了一下&#xff0c;顺便记录一下吧 0x02 辨别 我们拿到一个题目&#xff0c;他的名字可能就是题目类型&#xff0c;但是也有可能…

Python3网络爬虫开发实战(1)爬虫基础

一、URL 基础 URL也就是网络资源地址&#xff0c;其满足如下格式规范 scheme://[username:password]hostname[:port][/path][;parameters][?query][#fragment] scheme&#xff1a;协议&#xff0c;常用的协议有 Http&#xff0c;https&#xff0c;ftp等等&#xff1b;usern…

数据库(MySQL)-DQL数据查询语言

DQL(Data Query Language 数据查询语言)的用途是查询数据库数据&#xff0c;如select语句。其中&#xff0c;可以根据表的结构和关系分为单表查询和多表联查。 单表查询 单表查询&#xff1a;针对数据库中的一张数据表进行查询 全字段查询 语法&#xff1a;select 字段名 fro…

C# 实现条件变量

C# 进程通信系列 第一章 共享内存 第二章 条件变量&#xff08;本章&#xff09; 第三章 消息队列 文章目录 C# 进程通信系列前言一、关键实现1、用到的主要对象2、初始化区分创建和打开3、变量放到共享内存4、等待和释放逻辑 二、完整代码三、使用示例1、线程同步控制2、进程…

Maven概述

目录 1.Maven简介 2.Maven开发环境搭建 2.1下载Maven服务器 2.2安装&#xff0c;配置Maven 1.配置本地仓库地址 2.配置阿里云镜像地址 2.3在idea中配置maven 2.4在idea中创建maven项目 3.pom.xml配置 1.项目基本信息 2.依赖信息 3.构建信息 4.Maven命令 5.打包Jav…

【PyTorch】图像多分类项目

【PyTorch】图像二分类项目 【PyTorch】图像二分类项目-部署 【PyTorch】图像多分类项目 【PyTorch】图像多分类项目部署 多类图像分类的目标是为一组固定类别中的图像分配标签。 目录 加载和处理数据 搭建模型 定义损失函数 定义优化器 训练和迁移学习 用随机权重进行训…

【HTML — 构建网络】HTML 入门

在本文中,我们将介绍 HTML 的绝对基础知识。为了帮助您入门,本文定义了元素、属性以及您可能听说过的所有其他重要术语。它还解释了这些在 HTML 中的位置。您将学习 HTML 元素的结构、典型的 HTML 页面的结构以及其他重要的基本语言功能。在此过程中,也将有机会玩转 HTML! …

SpringBoot 项目配置文件注释乱码的问题解决方案

一、问题描述 在项目的配置文件中&#xff0c;我们写了一些注释&#xff0c;如下所示&#xff1a; 但是再次打开注释会变成乱码&#xff0c;如下所示&#xff1a; 那么如何解决呢&#xff1f; 二、解决方案 1. 点击” File→Setting" 2. 搜索“File Encodings”, 将框…

Mac安装Hoomebrew与升级Python版本

参考 mac 安装HomeBrew(100%成功)_mac安装homebrew-CSDN博客 /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" 安装了Python 3.x版本&#xff0c;你可以使用以下命令来设置默认的Python版本&#xff1a; # 首先找到新安…

图片格式转换工具哪个好?一键转换图片格式就用这4个

在贵州的街头&#xff0c;福建的游神活动吸引了众多目光&#xff0c;人们纷纷拿出手机记录下这难得一见的盛况。然而&#xff0c;在分享这些精彩瞬间时&#xff0c;我们往往会遇到格式不兼容的问题。 想象一下&#xff0c;如果你能一键将手机拍摄的HEIC格式转换为更通用的JPG或…

2024 微信小程序 学习笔记 第二天

1. WXML 模板语法 数据绑定 事件绑定 条件渲染 列表渲染 2. WXSS 模板样式 rpx 样式导入 全局和局部样式 3. 全局配置 window tabBar 配置tabBar案例 4. 网络数据请求 Get请求 Post 请求 加载时请求 5. 案例 -本地生活&#xff08;首页&#xff09; 导航栏 轮播图 九宫格效果…