当前位置: 首页 > news >正文

linux 设备树详解

设备树

描述设备树的文件叫做 DTS(Device Tree Source),这个 DTS 文件采用树形结构描述板级设备,也就是开发板上的设备信息,比如CPU 数量、 内存基地址、IIC 接口上接了哪些设备、SPI 接口上接了哪些设备等等。

在这里插入图片描述

树的主干就是系统总线,IIC 控制器、GPIO 控制器、SPI 控制器等都是接到系统主线上的分支。DTS 文件的主要功能就是按照上图所示的结构来描述板子上的设备信息。SOC厂商有多种开发板,将这些共同的信息提取出来作为一个通用的文件,其他的.dts 文件直接引用这个通用文件即可,这个通用文件就是.dtsi 文件,类似于 C 语言中的头文件。一般.dts 描述板级信息(也就是开发板上有哪些 IIC 设备、SPI 设备等),.dtsi 描述 SOC 级信息(也就是 SOC 有几个 CPU、主频是多少、各个外设控制器信息等)。

DTS、DTB、DTC之间的关系

DTS 是设备树源码文件,DTB 是将DTS 编译以后得到的二进制文件。编译的工具就是DTC。该工具存放/scripts/dtc文件夹下。文件/scripts/dtc/Makefile下有:

# scripts/dtc makefilehostprogs-y	:= dtc
always		:= $(hostprogs-y)dtc-objs	:= dtc.o flattree.o fstree.o data.o livetree.o treesource.o \srcpos.o checks.o util.o
dtc-objs	+= dtc-lexer.lex.o dtc-parser.tab.o# Source files need to get at the userspace version of libfdt_env.h to compile

说明了DTC 工具依赖于 dtc.cflattree.cfstree.c 等文件,最终编译并链接出 DTC 这个主机文件

在文件arch/arm/boot/dts/Makefile中有IMX6ULLSOC所编译生成的.dtb文件,以后我们需要添加就在该文件中找到对用的芯片,加载在下方即可。

dtb-$(CONFIG_SOC_IMX6ULL) += \imx6ull-14x14-ddr3-arm2.dtb \imx6ull-14x14-ddr3-arm2-adc.dtb \imx6ull-14x14-ddr3-arm2-cs42888.dtb \imx6ull-14x14-ddr3-arm2-ecspi.dtb \imx6ull-14x14-ddr3-arm2-emmc.dtb \imx6ull-14x14-ddr3-arm2-epdc.dtb \imx6ull-14x14-ddr3-arm2-flexcan2.dtb \imx6ull-14x14-ddr3-arm2-gpmi-weim.dtb \imx6ull-14x14-ddr3-arm2-lcdif.dtb \imx6ull-14x14-ddr3-arm2-ldo.dtb \imx6ull-14x14-ddr3-arm2-qspi.dtb \imx6ull-14x14-ddr3-arm2-qspi-all.dtb \imx6ull-14x14-ddr3-arm2-tsc.dtb \imx6ull-14x14-ddr3-arm2-uart2.dtb \imx6ull-14x14-ddr3-arm2-usb.dtb \imx6ull-14x14-ddr3-arm2-wm8958.dtb \imx6ull-14x14-evk.dtb \imx6ull-14x14-evk-btwifi.dtb \imx6ull-14x14-evk-emmc.dtb \imx6ull-14x14-evk-gpmi-weim.dtb \imx6ull-14x14-evk-usb-certi.dtb \imx6ull-9x9-evk.dtb \imx6ull-9x9-evk-btwifi.dtb \imx6ull-9x9-evk-ldo.dtb

当选中 I.MX6ULL 这个 SOC 以后(CONFIG_SOC_IMX6ULL=y),所有使用到I.MX6ULL 这个 SOC 的板子对应的.dts 文件都会被编译为.dtb

DTS语法

我们基本上不会从头到尾重写一个.dts 文件,大多时候是直接在 SOC 厂商提供的.dts文件上进行修改。

dtsi头文件

和 C 语言一样,设备树也支持头文件,设备树的头文件扩展名为.dtsi。可以通过“#include”来引用.h.dtsi .dts 文件。只是,我们在编写设备树头文件的时候最好选择.dtsi 后缀。

#include <dt-bindings/input/input.h>
#include "imx6ull.dtsi"
#include "imx6ull-14x14-evk.dts"

一般.dtsi 文件用于描述 SOC 的内部外设信息,比如 CPU 架构、主频、外设寄存器地址范围,比如 UARTIIC 等等。在arch/arm/boot/dts/imx6ull.dtsi中描述cpu的信息:

cpus {#address-cells = <1>;#size-cells = <0>;cpu0: cpu@0 {compatible = "arm,cortex-a7";device_type = "cpu";reg = <0>;clock-latency = <61036>; /* two CLK32 periods */operating-points = </* kHz	uV */696000	1275000528000	1175000396000	1025000198000	950000>;fsl,soc-operating-points = </* KHz	uV */696000  1275000528000	1175000396000	1175000198000	1175000>;clocks = <&clks IMX6UL_CLK_ARM>,<&clks IMX6UL_CLK_PLL2_BUS>,<&clks IMX6UL_CLK_PLL2_PFD2>,<&clks IMX6UL_CA7_SECONDARY_SEL>,<&clks IMX6UL_CLK_STEP>,<&clks IMX6UL_CLK_PLL1_SW>,<&clks IMX6UL_CLK_PLL1_SYS>,<&clks IMX6UL_PLL1_BYPASS>,<&clks IMX6UL_CLK_PLL1>,<&clks IMX6UL_PLL1_BYPASS_SRC>,<&clks IMX6UL_CLK_OSC>;clock-names = "arm", "pll2_bus",  "pll2_pfd2_396m", "secondary_sel", "step","pll1_sw", "pll1_sys", "pll1_bypass", "pll1", "pll1_bypass_src", "osc";};};

imx6ull.dtsi 文件中不仅仅描述了 cpu0 这一个节点信息,I.MX6ULL 这颗 SOC 所有的外设都描述的清清楚楚,比如 ecspi1~4uart1~8usbphy1~2i2c1~4 等等。

设备节点

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

/ { 	// 根节点,include的文件中也有根节点,不会冲突,这两个“/”根节点的内容会合并成一个根节点。// aliases、cpus 和 intc 是三个子节点aliases {can0 = &flexcan1;};cpus {#address-cells = <1>;#size-cells = <0>;// cpu0 也是子节点,只是 cpu0 是 cpus 的子节点cpu0: cpu@0 {compatible = "arm,cortex-a7";device_type = "cpu";reg = <0>;};};intc: interrupt-controller@00a01000 {compatible = "arm,cortex-a7-gic";#interrupt-cells = <3>;interrupt-controller;reg = <0x00a01000 0x1000>,<0x00a02000 0x100>;};
  • 节点命名格式分析
1、node-name@unit-address:eg: interrupt-controller@00a01000“node-name”是节点名字,为 ASCII 字符串,节点名字应该能够清晰的描述出节点的功能。“unit-address”一般表示设备的地址或寄存器首地址,如果某个节点没有地址或者寄存器的话“unitaddress”可以不要。eg: aliases
2、label: node-name@unit-addresseg:cpu0: cpu@0引入 label 的目的就是为了方便访问节点,可以直接通过&label 来访问这个节点,比如通过&cpu0 就可以访问“cpu@0”这个节点,而不需要输入完整的节点名字。

每个节点都有不同属性,不同的属性又有不同的内容,属性都是键值对,值可以为空或任意的字节流。设备树源码中常用的几种数据形式如下所示

  • 字符串
ompatible = "arm,cortex-a7";
// 设置 compatible 属性的值为字符串“arm,cortex-a7”。
  • 32 位无符号整数
reg = <0>;
reg = <0 0x123456 100>;
  • 字符串列表
compatible = "fsl,imx6ull-gpmi-nand", "fsl, imx6ul-gpmi-nand";
// 字符串与字符串之间使用逗号隔开
// 设置属性 compatible 的值为“fsl,imx6ull-gpmi-nand”和“fsl, imx6ul-gpmi-nand”。

标准属性

节点是由一堆的属性组成,节点都是具体的设备,不同的设备需要的属性不同,用户可以自定义属性。除了用户自定义属性,有很多属性是标准属性。

compatible 属性

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

compatible = "manufacturer,model"
// manufacturer 表示厂商 model 一般是模块对应的驱动名字
compatible = "fsl,imx6ul-evk-wm8960","fsl,imx-audio-wm8960";
// “fsl”表示厂商是飞思卡尔,“imx6ul-evk-wm8960”和“imx-audio-wm8960”表示驱动模块名字。
// 设备首先使用第一个兼容值在 Linux 内核里面查找,看看能不能找到与之匹配的驱动文件。
// 如果没有找到的话就使用第二个兼容值查。

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

imx-wm8960.c文件中有:

/*
// Struct used for matching a device
struct of_device_id {char	name[32];char	type[32];char	compatible[128];const void *data;
};
*/
/*of_XXX其中的of表示open firmware 即开放固件*/
static const struct of_device_id imx_wm8960_dt_ids[] = {// 在设备树中是"fsl,imx-audio-wm8960"的就是用这个驱动{ .compatible = "fsl,imx-audio-wm8960", },{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_wm8960_dt_ids);static struct platform_driver imx_wm8960_driver = {.driver = {.name = "imx-wm8960",.pm = &snd_soc_pm_ops,// 设置这个 platform_driver 所使用的OF 匹配表。.of_match_table = imx_wm8960_dt_ids,  },.probe = imx_wm8960_probe,.remove = imx_wm8960_remove,
};
module_platform_driver(imx_wm8960_driver);

model 属性

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

status 属性

status 属性看名字就知道是和设备状态有关的status 属性值也是字符串,字符串是设备的状态信息。

描述
“okay”表明设备是可操作的
"disabled"设备是不可操作的,但可以改为可操作,比如,热插拔
"fail"设备不可操作,设备检测到了一系列的错误,而且设备也不大可能变得可操作
"fail-sss"含义和“fail”相同,后面的 sss 部分是检测到的错误内容。

#address-cells #size-cells 属性

这两个属性的值都是无符号 32 位整形
#address-cells#size-cells 这两个属性可以用在任何拥有子节点的设备中,用于描述子节点的地址信息。
#address-cells 属性值决定了子节点 reg 属性中地址信息所占用的字长(32 位)。
#size-cells 属性值决定了子节点 reg 属性中地址长度信息所占的字长(32 位)。
#address-cells#size-cells 表明了子节点应该如何编写 reg 属性值,
一般 reg 属性都是和地址有关的内容,由两种和地址相关的信息组成:起始地址和地址长度。

reg 格式:

reg = <address1 length1 address2 length2 address3 length3……>
// 每个“address length”组合表示一个地址范围,其中 address 是起始地址,length 是地址长度,
// #address-cells 表明 address 这个数据所占用的字长,
// #size-cells 表明 length 这个数据所占用的字长
spi4 {compatible = "spi-gpio";// 说明 spi4 的子节点 reg 属性中起始地址所占用的字长为 1,地址长度所占用的字长为 0。#address-cells = <1>;#size-cells = <0>;gpio_spi: gpio_spi@0 {compatible = "fairchild,74hc595";// addres=0,没有length的值,相当于设置了起始地址,而没有设置地址长度。reg = <0>;};
};aips3: aips-bus@02200000 {compatible = "fsl,aips-bus", "simple-bus";// 起始地址长度所占用的字长为 1,地址长度所占用的字长也为 1。#address-cells = <1>;#size-cells = <1>;dcp: dcp@02280000 {compatible = "fsl,imx6sl-dcp";// 相当于设置了起始地址为 0x02280000,地址长度为 0x40000。reg = <0x02280000 0x4000>;};
};

reg 属性

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

ranges 属性

ranges属性值可以为空或者按照(child-bus-address,parent-bus-address,length)格式编写的数字矩阵,ranges 是一个地址映射/转换表。
child-bus-address: 子总线地址空间的物理地址,由父节点的#address-cells 确定此物理地址所占用的字长。
parent-bus-address:父总线地址空间的物理地址,由父节点的#address-cells 确定此物理地址所占用的字长。
length:子地址空间的长度,由父节点的#size-cells 确定此地址长度所占用的字长。
如果ranges属性值为空值,说明子地址空间和父地址空间完全相同,不需要进行地址转换

soc {#address-cells = <1>;#size-cells = <1>;compatible = "simple-bus";interrupt-parent = <&gpc>;ranges;  // 子地址空间和父地址空间完全相同
}
soc {compatible = "simple-bus";#address-cells = <1>;#size-cells = <1>;// 指定了一个 1024KB(0x00100000)的地址范围,子地址空间的物理起始地址为 0x0// 父地址空间的物理起始地址为 0xe0000000。ranges = <0x0 0xe0000000 0x00100000>;serial {device_type = "serial";compatible = "ns16550";// 定义了 serial 设备寄存器的起始地址为 0x4600,寄存器长度为 0x100// 经过地址转换, serial 设备可以从 0xe0004600 开始进行读写操作	// 0xe0004600=0x4600 + 0xe0000000reg = <0x4600 0x100>;clock-frequency = <0>;interrupts = <0xA 0x8>;interrupt-parent = <&ipic>;};
};

name 属性

name 属性值为字符串,name 属性用于记录节点名字,name 属性已经被弃用,不推荐使用name 属性,一些老的设备树文件可能会使用此属性。

device_type 属性

用于描述设备的 FCode,过时了,建议不用。它的值是字符串,用来表示节点的类型。在跟platform_driver匹配时,优先级为中。compatible属性在匹配过程中,优先级最高。

根节点 compatible 属性

每个节点都有 compatible 属性,根节点“/”也不例外。

/ {model = "Freescale i.MX6 ULL 14x14 EVK Board";compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";
}

compatible 有两个值:“fsl,imx6ull-14x14-evk”“fsl,imx6ull”。设备节点的 compatible 属性值是为了匹配 Linux 内核中的驱动程序。根节点的 compatible 属性用于描述我们所使用的设备,一般第一个值描述了所使用的硬件设备名字,第二个值描述了设备所使用的 SOCLinux 内核会通过根节点的 compoatible 属性查看是否支持此设备,如果支持的话设备就会启动 Linux 内核。

使用设备树之前设备匹配方法

在没有使用设备树以前,uboot 会向 Linux 内核传递一个叫做 machine id 的值,machine id 设备 ID,告诉 Linux 内核自己是个什么设备。Linux 内核是支持很多设备的,针对每一个设备(板子),Linux内核都用MACHINE_STARTMACHINE_END来定义一个 machine_desc 结构体来描述这个设备。

// arch/arm/mach-imx/mach-mx35_3ds.c
// 定义了 Freescale MX35PDK 这个设备
MACHINE_START(MX35_3DS, "Freescale MX35PDK")/* Maintainer: Freescale Semiconductor, Inc */.atag_offset = 0x100,.map_io = mx35_map_io,.init_early = imx35_init_early,.init_irq = mx35_init_irq,.init_time	= mx35pdk_timer_init,.init_machine = mx35_3ds_init,.reserve = mx35_3ds_reserve,.restart	= mxc_restart,
MACHINE_END/** Set of macros to define architecture features.  This is built into* a table by the linker.*/
// arch/arm/include/asm/mach/arch.h
#define MACHINE_START(_type,_name)			\
static const struct machine_desc __mach_desc_##_type	\__used							\__attribute__((__section__(".arch.info.init"))) = {	\.nr		= MACH_TYPE_##_type,		\.name		= _name,#define MACHINE_END				\
};

将上面的代码展开后有:

static const struct machine_desc __mach_desc_MX35_3DS	__used	/* machine_desc结构体__mach_desc_MX35_3DS储存在.arch.info.init */__attribute__((__section__(".arch.info.init"))) = {	/*说明了machine id 为 MACH_TYPE_MX35_3DS 定义在 include/generated/mach-types.h 中*/.nr		= MACH_TYPE_MX35_3DS,/*板子的名字叫做 "Freescale MX35PDK" */.name		= "Freescale MX35PDK",.atag_offset = 0x100,.map_io = mx35_map_io,.init_early = imx35_init_early,.init_irq = mx35_init_irq,.init_time	= mx35pdk_timer_init,.init_machine = mx35_3ds_init,.reserve = mx35_3ds_reserve,.restart	= mxc_restart,
};

include/generated/mach-types.h中定义了各种machine iduboot 会给 Linux 内核传递 machine id 这个参数。linux将传入的machine id和这里定义的宏比较,如果有,那么linux内核就支持该设备,否则不支持。

/*其中的部分宏定义*/
#define MACH_TYPE_U300                 1627
#define MACH_TYPE_WRT350N_V2           1633
#define MACH_TYPE_OMAP_LDP             1639
#define MACH_TYPE_MX35_3DS             1645 /*Freescale MX35PDK 对应的machine id*/
#define MACH_TYPE_NEUROS_OSD2          1647
#define MACH_TYPE_TRIZEPS4WL           1649
#define MACH_TYPE_TS78XX               1652

使用设备树以后的设备匹配方法

使用设备树后,不在使用上述宏,而是使用宏定义DT_MACHINE_START

// arch/arm/include/asm/mach/arch.h
#define DT_MACHINE_START(_name, _namestr)		\
static const struct machine_desc __mach_desc_##_name	\__used							\__attribute__((__section__(".arch.info.init"))) = {	\/*.nr= ~0说明引入设备树以后不会再根据 machine id 来检查Linux 内核是否支持某个设备了*/.nr		= ~0,				\.name		= _namestr,#endif
// arch/arm/mach-imx/mach-imx6ul.c
static const char *imx6ul_dt_compat[] __initconst = {/*只要某个设备根节点“/”的 compatible 属性值与imx6ul_dt_compat 表中的任何一个值相等,那么就表示 Linux 内核支持此设备。*/"fsl,imx6ul","fsl,imx6ull",NULL,
};DT_MACHINE_START(IMX6UL, "Freescale i.MX6 Ultralite (Device Tree)").map_io		= imx6ul_map_io,.init_irq	= imx6ul_init_irq,.init_machine	= imx6ul_init_machine,.init_late	= imx6ul_init_late,/*.dt_compat 保存着本设备兼容属性*/.dt_compat	= imx6ul_dt_compat,
MACHINE_END
  • linux内核匹配过程

Linux 内核调用 start_kernel 函数来启动内核,start_kernel 函数会调用 setup_arch 函数来匹配 machine_descsetup_arch 调用 setup_machine_fdt 函数来获取匹配的 machine_descsetup_machine_fdt 函数通过调用函数 of_flat_dt_match_machine 来获取匹配的 machine_desc

在这里插入图片描述

向节点添加或修改内容

假如我们要在开发板上的i2c1上添加其他的硬件设备。就要修改设备树里面的内容。不能直接修改文件imx6ull.dtsi文件里面的i2c1节点的内容,这样的话,其他未使用该设备的板子也相应的添加了。我们直接在我们板子对应的.dts文件中修改。imx6ull_14x14_evk.dts

// 向 i2c1 节点添加/修改数据,
&i2c1 {// i2c1 时钟为 100KHz。clock-frequency = <100000>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_i2c1>;status = "okay";mag3110@0e {compatible = "fsl,mag3110";reg = <0x0e>;position = <2>;};fxls8471@1e {compatible = "fsl,fxls8471";reg = <0x1e>;position = <0>;interrupt-parent = <&gpio5>;interrupts = <0 8>;};
};

设备树在系统中的体现

Linux 内核启动的时候会解析设备树中各个节点的信息,并且在根文件系统的/proc/device-tree 目录下根据节点名字创建不同文件夹。文件夹显示的是根节点的各种属性和子节点。

在这里插入图片描述

特殊节点

在根节点“/”中有两个特殊的子节点:aliases chosen

  • aliases节点

其中aliases的意思是别名的意思。因此 aliases 节点的主要功能就是定义别名,定义别名的目的就是为了方便访问节点。不过我们一般会在节点命名的时候会加上 label,然后通过&label来访问节点。

  • chosen节点

chosen 并不是一个真实的设备,chosen 节点主要是为了 uboot 向 Linux 内核传递数据,重点是 bootargs 参数。一般.dts 文件中 chosen 节点通常为空或者内容很少。

chosen {stdout-path = &uart1;
};

属性“stdout-path”,表示标准输出使用 uart1。但是在/proc/device-tree/chosen 目录里面多了 bootargs 属性。uboot 中的 fdt_chosen 函数在设备树的 chosen 节点中加入了 bootargs属性,并且还设置了 bootargs 属性值。

以下是 fdt_chosen 函数调用过程。

在这里插入图片描述

Linux 内核解析 DTB 文件

Linux 内核在启动的时候会解析 DTB 文件,然后在/proc/device-tree 目录下生成相应的设备树节点文件。

在这里插入图片描述

start_kernel 函数中完成了设备树节点解析的工作,最终实际工作的函数为 unflatten_dt_node

绑定信息文档

设备树是用来描述板子上的设备信息的,不同的设备其信息不同,反映到设备树中就是属性不同。

在Linux 内核源码中有详细的.txt 文档描述了如何添加节点,这些.txt 文档叫做绑定文档,路径为:Linux 源码目录/Documentation/devicetree/bindings。在 I.MX6ULL 这颗 SOCI2C 下添加一个节点,那么就可以查看Documentation/devicetree/bindings/i2c/i2c-imx.txt

* Freescale Inter IC (I2C) and High Speed Inter IC (HS-I2C) for i.MXRequired properties:
- compatible :- "fsl,imx1-i2c" for I2C compatible with the one integrated on i.MX1 SoC- "fsl,imx21-i2c" for I2C compatible with the one integrated on i.MX21 SoC- "fsl,vf610-i2c" for I2C compatible with the one integrated on Vybrid vf610 SoC
- reg : Should contain I2C/HS-I2C registers location and length
- interrupts : Should contain I2C/HS-I2C interrupt
- clocks : Should contain the I2C/HS-I2C clock specifierOptional properties:
- clock-frequency : Constains desired I2C/HS-I2C bus clock frequency in Hz.The absence of the propoerty indicates the default frequency 100 kHz.
- dmas: A list of two dma specifiers, one for each entry in dma-names.
- dma-names: should contain "tx" and "rx".Examples:i2c@83fc4000 { /* I2C2 on i.MX51 */compatible = "fsl,imx51-i2c", "fsl,imx21-i2c";reg = <0x83fc4000 0x4000>;interrupts = <63>;
};i2c@70038000 { /* HS-I2C on i.MX51 */compatible = "fsl,imx51-i2c", "fsl,imx21-i2c";reg = <0x70038000 0x4000>;interrupts = <64>;clock-frequency = <400000>;
};i2c0: i2c@40066000 { /* i2c0 on vf610 */compatible = "fsl,vf610-i2c";reg = <0x40066000 0x1000>;interrupts =<0 71 0x04>;dmas = <&edma0 0 50>,<&edma0 0 51>;dma-names = "rx","tx";
};

设备树常用 OF 操作函数

Linux 内核给我们提供了一系列的函数来获取设备树中的节点或者属性信息,这一系列的函数都有一个统一的前缀“of_”,所以在很多资料里面也被叫做 OF 函数。这些 OF 函数原型都定义在 include/linux/of.h 文件中。

查找节点的 OF 函数

设备都是以节点的形式“挂”到设备树上的,因此要想获取这个设备的其他属性信息,必须先获取到这个设备的节点。Linux 内核使用 device_node 结构体来描述一个节点,此结构体定义在文件 include/linux/of.h 中。

struct device_node {const char *name;  	/* 节点名字 */const char *type;	/* 设备类型 */phandle phandle;	const char *full_name;	/* 节点全名 */struct fwnode_handle fwnode;struct	property *properties;	/* 属性 */struct	property *deadprops;	/* removed 属性 */struct	device_node *parent;	/* 父节点 */struct	device_node *child;		/* 子节点 */struct	device_node *sibling;struct	kobject kobj;unsigned long _flags;void	*data;
#if defined(CONFIG_SPARC)const char *path_component_name;unsigned int unique_id;struct of_irq_controller *irq_trans;
#endif
};
  • of_find_node_by_name 函数
// 通过节点名字查找指定的节点
struct device_node *of_find_node_by_name(struct device_node *from,const char *name);
from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树
name:要查找的节点名字。
返回值:找到的节点,如果为 NULL 表示查找失败。
  • of_find_node_by_type 函数
// 通过 device_type属性 查找指定的节点
struct device_node *of_find_node_by_type(struct device_node *from,const char *type);
from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树
type: 要查找的节点对应的 type 字符串,也就是 device_type 属性值
返回值:找到的节点,如果为 NULL 表示查找失败。
  • of_find_compatible_node 函数
// 根据 device_type 和 compatible 这两个属性查找指定的节点
struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compat);
from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树
type: 要查找的节点对应的 type 字符串,也就是 device_type 属性值,可以是NULL表是忽略
compat:要查找的节点所对应的 compatible 属性列表
返回值:找到的节点,如果为 NULL 表示查找失败。
  • of_find_matching_node_and_match 函数
struct of_device_id {char	name[32];char	type[32];char	compatible[128];const void *data;
};
// 通过 of_device_id 匹配表来查找指定的节点
struct device_node *of_find_matching_node_and_match(struct device_node *from,const struct of_device_id *matches,const struct of_device_id **match);
from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树
matches:of_device_id 匹配表,也就是在此匹配表里面查找节点
match:找到的匹配的 of_device_id
返回值:找到的节点,如果为 NULL 表示查找失败
  • of_find_node_by_path 函数
// 通过路径来查找指定的节点
struct device_node *of_find_node_opts_by_path(const char *path,const char **opts);
static inline struct device_node *of_find_node_by_path(const char *path)
{return of_find_node_opts_by_path(path, NULL);
}
path:带有全路径的节点名,可以使用节点的别名,比如“/backlight”就是 backlight 这个节点的全路径。
返回值:找到的节点,如果为 NULL 表示查找失败

查找父/子节点的 OF 函数

  • of_get_parent 函数
// 获取指定节点的父节点
struct device_node *of_get_parent(const struct device_node *node);
  • of_get_next_child 函数
// 用迭代的方式查找子节点
struct device_node *of_get_next_child(const struct device_node *node,struct device_node *prev);
node:父节点
prev:表从哪一个子节点开始迭代的查找下一个子节点。可以设置为NULL,表示从第一个子节点开始。
返回值:找到的下一个子节点

提取属性值的 OF 函数

Linux 内核中使用结构体 property 表示属性。

struct property {char	*name; 	/* 属性名字 */int	length;		/* 属性长度 */void	*value;	/* 属性值 */struct property *next;	/* 下一个属性 */unsigned long _flags;unsigned int unique_id;struct bin_attribute attr;
};
  • of_find_property 函数
// 用于查找指定的属性
struct property *of_find_property(const struct device_node *np,const char *name,int *lenp);
np:设备节点
name: 属性名字。
lenp:属性值的字节数
返回值:找到的属性。
  • of_property_count_elems_of_size 函数
// 用于获取属性中元素的数量
// 比如 reg 属性值是一个数组,那么使用此函数可以获取到这个数组的大小
int of_property_count_elems_of_size(const struct device_node *np,const char *propname, int elem_size);
np:设备节点。
proname: 需要统计元素数量的属性名字。
elem_size:元素长度。
返回值:得到的属性元素数量。
  • of_property_read_u32_index 函数
// 用于从属性中获取指定标号的 u32 类型数据值
// 比如某个属性有多个 u32 类型的值,那么就可以使用此函数来获取指定标号的数据值
int of_property_read_u32_index(const struct device_node *np,const char *propname,u32 index, u32 *out_value);
np:设备节点。
proname: 要读取的属性名字。
index:要读取的值标号。
out_value:读取到的值
返回值:0 读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。
  • of_property_read_uX_array 函数
// 分别是读取属性中 u8、u16、u32 和 u64 类型的数组数据
// 比如大多数的 reg 属性都是数组数据,可以使用这 4 个函数一次读取出 reg 属性中的所有数据。
int of_property_read_u8_array(const struct device_node *np,const char *propname, u8 *out_values, size_t sz);
int of_property_read_u16_array(const struct device_node *np,const char *propname, u16 *out_values, size_t sz);
int of_property_read_u32_array(const struct device_node *np,const char *propname,u32 *out_values,size_t sz);
int of_property_read_u64_array(const struct device_node *np,const char *propname,u64 *out_values,size_t sz);
np:设备节点。
proname: 要读取的属性名字。
out_value:读取到的数组值,分别为 u8、u16、u32 和 u64。
sz:要读取的数组元素数量。
返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。
  • of_property_read_uX 函数
// 有些属性只有一个整形值,这四个函数就是用于读取这种只有一个整形值的属性
int of_property_read_u8(const struct device_node *np,const char *propname, u8 *out_value);
int of_property_read_u16(const struct device_node *np,const char *propname, u16 *out_value);
int of_property_read_u32(const struct device_node *np,const char *propname, u32 *out_value);
int of_property_read_u64(const struct device_node *np,const char *propname, u64 *out_value);
np:设备节点。
proname: 要读取的属性名字。
out_value:读取到的数组值。
返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。
  • of_property_read_string 函数
// 用于读取属性中字符串值
int of_property_read_string(struct device_node *np,const char *propname,const char **out_string);
np:设备节点。
proname: 要读取的属性名字。
out_string:读取到的字符串值。
返回值:0,读取成功,负值,读取失败。
  • of_n_addr_cells 函数
// 用于获取#address-cells 属性值
int of_n_addr_cells(struct device_node *np);
np:设备节点。
返回值:获取到的#address-cells 属性值。
  • of_n_size_cells 函数
// 数用于获取#size-cells 属性值
int of_n_size_cells(struct device_node *np)
np:设备节点。
返回值:获取到的#size-cells 属性值。

其他常用 OF 函数

  • of_device_is_compatible 函数
// 用于查看节点的 compatible 属性是否有包含 compat 指定的字符串,也就是检查设备节点的兼容性
static inline int of_device_is_compatible(const struct device_node *device,const char *compat)
device:设备节点。
compat:要查看的字符串。
返回值:0,节点的 compatible 属性中不包含 compat 指定的字符串;正数,节点的 compatible属性中包含 compat 指定的字符串。
  • of_get_address 函数
// 数用于获取地址相关属性,主要是“reg”或者“assigned-addresses”属性值
static inline const __be32 *of_get_address(struct device_node *dev, int index,u64 *size, unsigned int *flags)
dev:设备节点。
index:要读取的地址标号。
size:地址长度。
flags:参数,比如 IORESOURCE_IO、IORESOURCE_MEM 等
返回值:读取到的地址数据首地址,为 NULL 的话表示读取失败。
  • of_translate_address 函数
// 负责将从设备树读取到的地址转换为物理地址
u64 of_translate_address(struct device_node *np, const __be32 *addr);
dev:设备节点。
in_addr:要转换的地址。
返回值:得到的物理地址,如果为 OF_BAD_ADDR 的话表示转换失败。
  • of_address_to_resource 函数

IIC、SPI、GPIO 等这些外设都有对应的寄存器,这些寄存器其实就是一组内存空间,Linux内核使用 resource 结构体来描述一段内存空间,“resource”翻译出来就是“资源”,因此用 resource结构体描述的都是设备资源信息。

// include/linux/ioport.h
struct resource {resource_size_t start;	/*  resource_size_t 其实就是u32类型 开始地址 */resource_size_t end;	/* 结束地址 */const char *name;		/* 资源的名字 */unsigned long flags;	/* 资源标志位,用于表示资源类型 *//* 常见的标志位 IORESOURCE_MEM 、 IORESOURCE_REG 、IORESOURCE_IRQ */struct resource *parent, *sibling, *child;
};
// 将 reg 属性值转换为 resource 结构体类型
int of_address_to_resource(struct device_node *dev, int index,struct resource *r);
dev:设备节点。
index:地址资源标号。
r:得到的 resource 类型的资源值。
返回值:0,成功;负值,失败。
  • of_iomap 函数
// 数用于直接内存映射,以前我们会通过 ioremap 函数来完成物理地址到虚拟地址的映射,
// 采用设备树以后就可以直接通过 of_iomap 函数来获取内存地址所对应的虚拟地址
void __iomem *of_iomap(struct device_node *np, int index)
np:设备节点。
index:reg 属性中要完成内存映射的段,如果 reg 属性只有一段的话 index 就设置为 0。
返回值:经过内存映射后的虚拟内存首地址,如果为 NULL 的话表示内存映射失败。

相关文章:

linux 设备树详解

设备树 描述设备树的文件叫做 DTS(Device Tree Source)&#xff0c;这个 DTS 文件采用树形结构描述板级设备&#xff0c;也就是开发板上的设备信息&#xff0c;比如CPU 数量、 内存基地址、IIC 接口上接了哪些设备、SPI 接口上接了哪些设备等等。 树的主干就是系统总线&#x…...

STM32 学习笔记_7 定时器中断:输出比较

输出比较 电机相关比较重要。 OC Output Compare&#xff08;IC 是输入捕获&#xff0c;CC代指这两个单元&#xff09;&#xff0c;用于输出一定频率和占空比的PWM波形。 右下角四个就是CCR。只有通用计时器和高级计时器有&#xff0c;共用一个cnt计数器&#xff0c;高级计数…...

HTML购物车示例(勾选、删除、添加和结算功能)

以下是一个简单的HTML购物车示例&#xff0c;包含勾选、删除、添加和结算功能。结算功能使用PHP实现&#xff0c;可以获取选中商品的ID。 以下是一个简单的HTML购物车示例&#xff0c;包含勾选、删除、添加和结算功能。结算功能使用PHP实现&#xff0c;可以获取选中商品的ID以下…...

MySQL原理(十):主从架构

前言 上一篇介绍了 MySQL 的表分区和分库分表&#xff0c;这一篇将介绍主从架构相关的内容。 主从架构 常见的主从架构模式有四种&#xff1a; 一主多从架构&#xff1a;适用于读大于写的场景&#xff0c;采用多个从库来分担数据库系统的读压力。多主架构&#xff1a;适用于…...

一文了解Moonbeam智能合约

智能合约&#xff1a;区块链交易的基石 20世纪90年代&#xff0c;Nick Szabo首次提出智能合约的概念&#xff0c;这是一个建立在自动化、加密安全世界之上的数字化市场。在这种数字化市场中&#xff0c;交易和业务可以在无需信任的情况下进行&#xff0c;无需中间人。 以太坊…...

【加解密篇】利用HashCat破解RAR压缩包加密文件详细教程

【加解密篇】利用HashCat解密RAR压缩包加密文件 在取证知识里挖呀挖呀挖—【蘇小沐】 文章目录 【加解密篇】利用HashCat解密RAR压缩包加密文件1.实验环境2.RAR加密压缩包 &#xff08;一&#xff09;john软件1.使用CMD命令&#xff1a; run\rar2john.exe &#xff08;二&…...

React面试题汇总1

1.React的严格模式如何使用&#xff0c;有什么用处&#xff1f; React中StrictMode严格模式_react.strictmode_前端精髓的博客-CSDN博客当我们使用 npx create-react-app my-app 创建一个项目的时候。项目中有一段如下所示的代码&#xff1a;ReactDOM.render( <React.Stric…...

Golang每日一练(leetDay0066) 有效电话号码、转置文件

目录 193. 有效电话号码 Valid Phone Numbers &#x1f31f; 194. 转置文件 Transpose File &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 193. 有效电话号…...

前端 之 FormData对象浅谈

一、简介 ​ 通常情况下&#xff0c;前端在使用post请求提交数据的时候&#xff0c;请求都是采用application/json 或 application/x-www-form-urlencoded编码类型&#xff0c;分别是借助JSON字符串来传递参数或者keyvalue格式字符串&#xff08;多参数通过&进行连接&…...

【分布式锁】Redisson分布式锁的使用(推荐使用)

文章目录 前言一、常见分布式锁方案对比二、分布式锁需满足四个条件三、什么是Redisson?官网和官方文档Redisson使用 四、Redisson 分布式重入锁用法Redisson 支持单点模式、主从模式、哨兵模式、集群模式自己先思考下,如果要手写一个分布式锁组件&#xff0c;怎么做&#xff…...

创建XML的三种方式(二)

文章目录 1 使用XmlDocument创建XML文档2 使用XmlTextWriter写XML文档3 使用LINQ to XML 的XDocument类4 小结 本文介绍了在winform中使用C#开发语言来创建XML文档的三种方式&#xff0c;并介绍了各自的优缺点。 方法1是使用 XmlDocument创建XML文档&#xff0c;方法2是使用 …...

十分钟教你搭建类似ChatGPT的安卓应用程序

大家好&#xff0c;我是易安&#xff01; Chat GPT 是当今著名的人工智能工具&#xff0c;就像聊天机器人一样。Chat GPT会回答发送给它的所有查询。今天&#xff0c;我将通过集成 OpenAI API (ChatGPT)构建一个简单的类似 ChatGPT 的 android 应用程序&#xff0c;我们可以在其…...

问题 E: 起止位置(C++)(二分查找)

目录 1.题目描述 2.AC 1.题目描述 问题 E: 起止位置 时间限制: 1.000 Sec 内存限制: 128 MB提交 状态 题目描述 有n位同学按照年龄从小到大排好队。 王老师想要查询&#xff0c;年龄为x的同学&#xff0c;在队伍中首次出现的位置和最后一次出现的位置&#xff1b;如果队…...

【sop】基于灵敏度分析的有源配电网智能软开关优化配置[升级1](Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

LeetCode周赛复盘(第345场周赛)

文章目录 1、找出转圈游戏输家1.1 题目链接1.2 题目描述1.3 解题代码1.4 解题思路 2、相邻值的按位异或2.1 题目链接2.2 题目描述2.3 解题代码2.4 解题思路 3、 矩阵中移动的最大次数3.1 题目链接3.2 题目描述3.3 解题代码3.4 解题思路 4、 统计完全连通分量的数量4.1 题目链接…...

Call for Papers丨第三届GLB@KDD‘23 Workshop

鉴于介绍新数据集和Benchmark研究往往需要不同于常规论文的评审标准&#xff0c;计算机视觉和自然语言处理领域&#xff0c;以及最近的NeurIPS会议&#xff0c;都有专门致力于建立新Benchmark数据集和任务的Conference Track。然而在图机器学习领域&#xff0c;我们还没有类似的…...

【多线程】单例模式

目录 饿汉模式 懒汉模式-单线程版 懒汉模式-多线程版 懒汉模式-多线程版(改进) 单例是一种设计模式。 啥是设计模式 ? 设计模式好比象棋中的 " 棋谱 ". 红方当头炮 , 黑方马来跳 . 针对红方的一些走法 , 黑方应招的时候有一些固定的套路. 按照套路来走局势…...

7搜索管理

7搜索管理 7.1 准备环境 7.1.1 创建映射 创建xc_course索引库。 创建如下映射 post&#xff1a;http://localhost:9200/xc_course/doc/_mapping 参考 “资料”–》搜索测试-初始化数据.txt { "properties": { "description": { "type": &…...

在Pytorch中使用Tensorboard

Tensorboard是一款深度学习可视化软件&#xff0c;目前主要使用了它的可视化模型, 可视化模型权重和可视化损失函数功能。 x.1 tensorboard初始化 tensorboard初始化需要导入SummaryWriter包并指定存储位置和开放端口号。 from torch.utils.tensorboard import SummaryWrite…...

[笔记]深入解析Windows操作系统《四》管理机制

文章目录 前言4.1注册表查看和修改注册表注册表用法注册表数据类型注册表逻辑结构HKEY_CURRENT_USERHKEY_USERS 实验&#xff1a;观察轮廓加载和卸载HKEY_CLASSES_ROOTHKEY_LOCAL_MACHINE 实验:离线方式或远程编辑BCDHKEY_CURRENT_CONFIGHKEY_PERFORMANCE_DATA 前言 本章讲述了…...

【小沐学Python】Python实现在线英语翻译功能

文章目录 1、简介2、在线翻译接口2.1 Google Translate API2.2 Microsoft Translator API2.2.1 开发简介2.2.2 开发费用2.2.3 开发API 2.3 百度翻译开放平台 API2.3.1 开发简介2.3.2 开发费用2.3.3 开发API 2.4 Tencent AI 开放平台的翻译 API2.4.1 开发简介2.4.2 开发API 2.5 …...

k8s中pod使用详解

一、前言 在之前k8s组件一篇中,我们谈到了pod这个组件,了解到pod是k8s中资源管理的最小单位,可以说Pod是整个k8s对外提供服务的最基础的个体,有必要对Pod做深入的学习和探究。 二、再看k8s架构图 为了加深对k8s中pod的理解,再来回顾下k8s的完整架构 三、pod特点 结合上面这…...

案例说明:vue中Element UI下拉列表el-option中的key、value、label含义各是什么

可以简单理解为&#xff1a;label 是给用户展示的东西&#xff0c;value是前端往后端传递的真实值 <template><div><el-page-header back"goBack" content"注册"></el-page-header><el-divider></el-divider><el-…...

idea创建javaweb项目步骤超详细(2022最新版本)

目录 前言必读 一、新建文件 1.在idea里面点击文件-新建-项目 2.新建项目-更改名称为自己想要的项目名称-创建 3.右键自己建立的项目-添加框架支持&#xff08;英文版是Add Framework Support...&#xff09; 4.勾选Web应用程序-确定 5.建立成功界面 二、配置tomcat 6.…...

「SAP ABAP」OPEN SQL(六)【DELETE语句 | MODIFY语句】

💂作者简介: THUNDER王,一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学本科在读,同时任汉硕云(广东)科技有限公司ABAP开发顾问。在学习工作中,我通常使用偏后端的开发语言ABAP,SQL进行任务的完成,对SAP企业管理系统,SAP ABAP开发和数据库具有较…...

SpringCloud --- Feign远程调用

一、RestTemplate问题 先来看我们以前利用RestTemplate发起远程调用的代码&#xff1a; 存在下面的问题&#xff1a; 代码可读性差&#xff0c;编程体验不统一参数复杂URL难以维护 Feign是一个声明式的http客户端&#xff0c;官方地址&#xff1a;GitHub - OpenFeign/feign:…...

基于单片机的数字频率计设计

数字频率计概述 数字频率计是计算机、通讯设备、音频视频等科研生产领域不可缺少的测量仪器。它是一种用十进制数字显示被测信号频率的数字测量仪器。它的基本功能是测量正弦信号&#xff0c;方波信号及其他各种单位时间内变化的物理量。在进行模拟、数字电路的设计、安装、调试…...

我看看哪个靓仔还没把Github Copilot用起来?

本人经常分享有价值的生产力工具、技术、好物与书籍&#xff0c;可关注同名公众&#x1f42d;并设为&#x1f31f;星标&#xff0c;第一时间获得更新 Github Copilot 是一个AI编程助手&#xff0c;其使用 OpenAI CodeX 在你的编辑器中实时建议代码或给你实现整个功能。 视频版介…...

C++系列一: C++简介

C入门简介 1. C语言的特点2. C编译器3. 第一个 C 程序4. 总结&#xff08;手稿版&#xff09; C 是一种高级编程语言&#xff0c;是C语言的扩展和改进版本&#xff0c;由Bjarne Stroustrup于1983年在贝尔实验室为了支持C语言中的面向对象编程而创建。C 既能够进行底层的系统编程…...

信通初试第一:无科研无竞赛一战上岸上海交大819学硕感悟

笔者来自通信考研小马哥23上交819全程班学员 信通初试第一&#xff1a;无科研无竞赛一战上岸上海交大819学硕感悟 原创2023-04-27 11:04通信考研小马哥 笔者来自通信考研小马哥23上交819全程班学员 本人情况&#xff1a; 本人是19届交本&#xff0c;本科成绩很差&#xff0c;…...