【北京迅为】《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.rhkb.cn/news/383017.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Springboot 开发之 RestTemplate 简介

一、什么是RestTemplate RestTemplate 是Spring框架提供的一个用于应用中调用REST服务的类。它简化了与HTTP服务的通信&#xff0c;统一了RESTFul的标准&#xff0c;并封装了HTTP连接&#xff0c;我们只需要传入URL及其返回值类型即可。RestTemplate的设计原则与许多其他Sprin…

spring boot(学习笔记第十四课)

spring boot(学习笔记第十四课) Spring Security的密码加密&#xff0c;基于数据库认证 学习内容&#xff1a; Spring Security的密码加密基于数据库认证 1. Spring Security的密码加密 如果用户的密码保存在数据库中是以明文保存&#xff0c;对于公司的安全将是灾难性的&…

【PyTorch】图像多分类项目部署

【PyTorch】图像多分类项目 【PyTorch】图像多分类项目部署 如果需要在独立于训练脚本的新脚本中部署模型&#xff0c;这种情况模型和权重在内存中不存在&#xff0c;因此需要构造一个模型类的对象&#xff0c;然后将存储的权重加载到模型中。 加载模型参数&#xff0c;验证模型…

NFTScan 浏览器现已支持 .mint 域名搜索功能!

近日&#xff0c;NFT 数据基础设施 NFTScan 浏览器现已支持用户输入 .mint 域名进行 Mint Blockchain 网络钱包地址的搜索查询&#xff0c; NFTScan 用户能够轻松地使用域名追踪 NFT 交易&#xff0c;为 NFT 钱包地址相关的搜索查询功能增加透明度和便利性。 NFTScan explorer…

goenv丝滑控制多版本go

安装 先装下goenv brew install goenv去 ~/.bash_profile 添加一下 export GOENV_ROOT"$HOME/.goenv" export PATH"$GOENV_ROOT/bin:$PATH" eval "$(goenv init -)"执行一下让配置生效 source ~/.bash_profile插一嘴&#xff0c;如果之前是在…

VScode 批量操作

VScode 批量操作 批量修改 按住 alt/option 键&#xff0c; 选择需要批量操作的位置 如果是多行&#xff0c;则按住 altshift 键 可以直接操作 但是有时候比如变量命名&#xff0c;可能需要递增操作的命名 需要下载插件 Increment Selection 按照1的方法多选光标之后&am…

Java | 自制AWT单词猜一猜小游戏(测试版)

目录 游戏标题 开发过程 开发想法 技术栈 代码呈现 导包 核心代码 游戏标题 探索知识的迷宫&#xff0c;体验自制AWT单词猜一猜小游戏 在数字时代&#xff0c;学习可以是多彩的&#xff0c;游戏可以是智慧的。我们自豪地推出“单词猜猜猜”是一款结合了教育与娱乐的自制…

MQTTX连接华为云IoTDA

目录 华为IoTDA平台 MQTTX连接参数的设置 物模型的构建 属性上报 基本数据格式 时戳 我以前上课都是用巴法云服务器来演示MQTT的&#xff0c;前几天因为测试工业互联网关使用了华为的IoTDA&#xff0c;觉得也不算太复杂&#xff0c;今天尝试用MQTTX连接华为云&#xff0c…

Python读取grib数据获取变量推荐姿势

前情提要 最近使用的EC和GFS预报数据给的都是grib2格式的&#xff0c;之前用惯nc格式的&#xff0c;python读取grib2数据的时候还走了些弯路&#xff0c;看到很多博客上给的教程其实不能满足我的需求&#xff0c;现在搞明白了分享一下 pygrib安装 第一个问题就是我电脑上pyg…

HTTPS 的加密过程 详解

HTTP 由于是明文传输&#xff0c;所以安全上存在以下三个风险&#xff1a; 窃听风险&#xff0c;比如通信链路上可以获取通信内容。篡改风险&#xff0c;比如通信内容被篡改。冒充风险&#xff0c;比如冒充网站。 HTTPS 在 HTTP 与 TCP 层之间加入了 SSL/TLS 协议&#xff0c…

1 go语言环境的搭建

本专栏将从基础开始&#xff0c;循序渐进&#xff0c;由浅入深讲解Go语言&#xff0c;希望大家都能够从中有所收获&#xff0c;也请大家多多支持。 查看相关资料与知识库 专栏地址:Go专栏 如果文章知识点有错误的地方&#xff0c;请指正&#xff01;大家一起学习&#xff0c;…

腾讯云k8s相关

1.某个服务腾讯云内网地址&#xff1f; 比如&#xff1a;spiderflow-web正式环境&#xff1a;http://spiderflow-web.sd-backend:30001 试一试&#xff1a;

Linux shell实现执行任务进度条动态显示(回旋镖)-3

process_bar.sh #!/bin/bash #display boomerangwhile : doclearfor i in {1..20}doecho -e "\033[2;${i}H*"sleep 0.1doneclearfor i in {20..1}doecho -e "\033[2;${i}H*"sleep 0.1doneclear done验证&#xff1a;

机器学习笔记-01-初识基础(问题-解答自查版)

前言 以下问题以Q&A形式记录&#xff0c;基本上都是笔者在初学一轮后&#xff0c;掌握不牢或者频繁忘记的点 Q&A的形式有助于学习过程中时刻关注自己的输入与输出关系&#xff0c;也适合做查漏补缺和复盘。 本文对读者可以用作自查&#xff0c;答案在后面&#xff0…

(35)远程识别(又称无人机识别)(二)

文章目录 前言 4 ArduRemoteID 5 终端用户数据的设置和使用 6 测试 7 为OEMs添加远程ID到ArduPilot系统的视频教程 前言 在一些国家&#xff0c;远程 ID 正在成为一项法律要求。以下是与 ArduPilot 兼容的设备列表。这里(here)有一个关于远程 ID 的很好解释和常见问题列表…

ubuntu那些ppa源在哪

Ubuntu中的 PPA 终极指南 - UBUNTU粉丝之家 什么是PPA PPA 代表个人包存档。 PPA 允许应用程序开发人员和 Linux 用户创建自己的存储库来分发软件。 使用 PPA&#xff0c;您可以轻松获取较新的软件版本或官方 Ubuntu 存储库无法提供的软件。 为什么使用PPA&#xff1f; 正如…

VLC输出NDI媒体流

目录 1. 下载安装VLC Play 2. 首先在电脑上安装NDI Tools 3. 运行VLC进行输出配置 4. 播放视频 5. 验证 (1)用Studio Monitor验证 (2)用OBS验证 NDI(Network Device Interface)即网络设备接口,是由美国 NewTek 公司开发的免费标准,它可使兼容的视频产品以高质量…

视图、存储过程、触发器

一、视图 视图是从一个或者几个基本表&#xff08;或视图&#xff09;导出的表。它与基 本表不同&#xff0c;是一个虚表&#xff0c;视图只能用来从查询&#xff0c;不能做增删改(虚拟的表) 1.创建视图 创建视图的语法&#xff1a; create view 视图名【view_xxx / v_xxx】 a…

基于CALMET诊断模型的高时空分辨率精细化风场模拟

原文链接&#xff1a;基于CALMET诊断模型的高时空分辨率精细化风场模拟https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247610033&idx7&sn999fb0fa3a0e57acebdfe209587ce7f3&chksmfa826f56cdf5e640f7dba429a9213a38d1222415eccd8660f4cf9fb46fa1a5ab3c5…

Netty:基于NIO的 Java 网络应用编程框架

Netty 是一个被广泛使用的&#xff0c;基于NIO的 Java 网络应用编程框架&#xff0c;Netty框架可以帮助开发者快速、简单的实现客户端和服务端的网络应用程序。“快速”和“简单”并不用产生维护性或性能上的问题。Netty 利用 Java 语言的NIO网络编程的能力&#xff0c;并隐藏其…