【北京迅为】《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.mzph.cn/diannao/49986.shtml

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

相关文章

代理IP在数据采集中具体有哪些作用

在当今信息化高速发展的时代&#xff0c;从市场趋势分析到消费者行为预测&#xff0c;从产品优化到服务改进&#xff0c;大数据都在其中扮演着不可或缺的角色。但数据的采集、整理和分析并非易事&#xff0c;特别是在面对海量的网络数据时&#xff0c;我们往往需要借助一些技术…

前端 SSE 长连接

使用 const options {withCredentials: true, // 默认 false}const eventSource new EventSource(/api, options);eventSource.addEventListener(open, () > {});eventSource.onmessage (event) > {}; // 或addEventListener(message,callback)eventSource.addEvent…

程序员拔火罐技能的分享

一.背景 之前为了考“中医康复理疗师”的证书&#xff0c;自学了拔火罐。自学主要是在自己大腿上练习&#xff0c;然后拿家人做小白鼠。后来考试没有那么严格也就顺利通过了。操作过程中&#xff0c;积累的一些小知识&#xff0c;分享给大家&#xff0c;有空在家里给家人服务体…

CTF ssrf 基础入门 (一)

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

FastAPI(七十三)实战开发《在线课程学习系统》接口开发-- 回复留言

源码见&#xff1a;"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 之前文章分享FastAPI&#xff08;七十二&#xff09;实战开发《在线课程学习系统》接口开发-- 留言列表开发&#xff0c;这次我们分享如何回复留言 按…

Jenkins+Gitlab持续集成综合实战

一、持续集成应用背景&#xff1a; DevOps&#xff1a;&#xff08;英文Development&#xff08;开发&#xff09;和Operations&#xff08;技术运营&#xff09;的组合&#xff09;是一组过程、方法与系统的统称&#xff0c;用于促进开发&#xff08;应用程序/软件工程&#…

NCRE3 2-1 网络总体设计基本方法

这部分记忆的比较多 概览 设计网络建设总体目标确定网络系统方案设计原则网络系统总体设计设计网络拓扑结构进行网络设备选型网络系统安全设计 设计网络建设总体目标 这部分视频没说到 确定网络系统方案设计原则 这部分视频没说到 网络系统总体设计 核心层网络结构设计 …

嵌入式Linux八股(四)——MCURTOS

嵌入式Linux八股完整文章目录 嵌入式Linux八股&#xff08;一&#xff09;——语言篇_嵌入式软件八股-CSDN博客 嵌入式Linux八股&#xff08;二&#xff09;——Linux_linux嵌入式的八股文有哪些-CSDN博客 嵌入式Linux八股&#xff08;三&#xff09;——计算机基础_嵌入式哪…

【Vue实战教程】之Vue工程化项目详解

Vue工程化项目 随着多年的发展&#xff0c;前端越来越模块化、组件化、工程化&#xff0c;这是前端发展的大趋势。webpack是目前用于构建前端工程化项目的主流工具之一&#xff0c;也正变得越来越重要。本章节我们来详细讲解一下如何使用webpack搭建Vue工程化项目。 1 使用we…

基于Python的哔哩哔哩国产动画排行数据分析系统

需要本项目的可以私信博主&#xff0c;提供完整的部署、讲解、文档、代码服务 随着经济社会的快速发展&#xff0c;中国影视产业迎来了蓬勃发展的契机&#xff0c;其中动漫产业发展尤为突出。中国拥有古老而又璀璨的文明&#xff0c;仅仅从中提取一部分就足以催生出大量精彩的…

源码拆解SpringBoot的自动配置机制

SpringBoot相比于Spring系列的前作&#xff0c;很大的一个亮点就是将配置进行了简化&#xff0c;引入了自动化配置&#xff0c;仅靠几个注解和yml文件就取代了之前XML的繁琐配置机制&#xff0c;这也是SpringBoot的独有特点&#xff0c;下面我们从源码角度&#xff0c;一点点拆…

Golang | Leetcode Golang题解之第273题整数转换英文表示

题目&#xff1a; 题解&#xff1a; var (singles []string{"", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine"}teens []string{&…

IEC61850 协议解读

1. IEC61850 协议介绍 IEC 61850 是定义 变电站 自动化系统 中 设备 及设备之间相互交互的 国际标准。 给出英文定义&#xff1a;IEC 61850 is the international standard for defining devices within substation automation systems and how they interact with one anoth…

Java基础-Java多线程机制

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 一、引言 二、多线程的基本概念 1. 线程与进程 2. 多线程与并发 3. 多线程的优势 三、Java多线程的实…

git命令使用详细介绍

1 环境配置 设置的信息会保存在~/.gitconfig文件中 查看配置信息 git config --list git config user.name设置用户信息 git config --global user.name "有勇气的牛排" git config --global user.email “1809296387qq.com”2 获取Git仓库 2.1 本地初始化一个仓…

Android Studio入门级教程(二)——项目开发基础(Java新手向))持续更新ing

目录 前言&#xff1a; 一.使用Log工具打印日志 常见语法&#xff1a; 如何使用&#xff1f; 二.工程目录结构 三.编译配置文件build.gradle 四.运行配置文件AndroidManifest.xml 五.界面显示与逻辑处理 六.创建新的app页面 1.包含的步骤 在layout目录下创建XML文件…

uniapp集成安卓原生录屏插件以及使用

概述 我们知道UniApp的出现简化了开发者的工作流程&#xff0c;并减少了代码的重复编写。开发者可以使用一套代码编译到iOS、Android、以及各种小程序的应用&#xff0c;节省了人力和时间成本&#xff0c;但是涉及到与系统交互的时候&#xff0c;比如录屏、录音、录像、文件操…

Java 每日一题: for 与 foreach 的区别 ?

for 循环&#xff1a;是最基本的循环结构&#xff0c;可以通过初始化语句、循环条件和迭代语句来控制循环的执行。 foreach 循环&#xff08;也称为增强型 for 循环&#xff09;&#xff1a;用于遍历集合或数组中的元素&#xff0c;简化了遍历过程&#xff0c;没有显式地控制索…

vercel 如何部署 express 项目

注意&#xff1a;如果你是用 express-generator 生成的 express 项目&#xff0c;请检查是否有依赖 jade &#xff0c;如果有的话删除目录下的 views 文件夹&#xff0c;并把所有渲染页面的方法改成 res.send() !!!!!! 然后在项目根目录创建文件 vercel.json {&qu…

获取后端返回的图形验证码

如果后端返回的直接就是一个图形&#xff0c;有以下几种方式展示 一、直接在img标签里面的src里面调用接口 <img :src"dialogSrc" class"photo" alt"验证码图片" click"changeDialog">let orgUrl "/api/captcha" …