Linux驱动开发——设备树

文章目录

  • 1 什么是设备树?
  • 2 DTS、DTB和DTC
  • 3 DTS语法
    • 3.1 dtsi头文件
    • 3.2 设备节点
    • 3.3 标准属性
    • 3.4 根节点compatible属性
    • 3.5 向节点追加或修改内容
  • 4 创建小型模板设备树
  • 5 设备树在系统中的体现
  • 6 绑定信息文档
  • 7 设备树常用OF操作函数
    • 7.1 查找节点的OF函数
    • 7.2 查找父/子节点的OF函数
    • 7.3 提取属性值的OF函数
    • 7.4 其他常用的OF函数

系列文章:
Linux驱动开发——字符设备驱动开发
Linux驱动开发——LED驱动开发
Linux驱动开发——新字符设备驱动开发

1 什么是设备树?

设备树(Device Tree),将这个词分开就是“设备”和“树”,描述设备树的文件叫做 DTS(Device Tree Source),这个 DTS 文件采用树形结构描述板级设备,也就是开发板上的设备信息,比如CPU 数量、 内存基地址、IIC 接口上接了哪些设备、SPI 接口上接了哪些设备等等,如下图所示:
在这里插入图片描述
在以前的内核中,还没有采用设备树,内核源码中有大量的arch/arm/mach-xxx和arch/arm/plat-xxx文件夹,用于存储不同平台的板级信息,但随着芯片产业的发展,加入内核中的板级信息日益庞大,导致内核“虚胖”,所以引入了PowerPC等架构已经采用的设备树。将这些描述板级硬件信息的内容都从Linux中分离,用一个专属的文件格式类描述,这个专属的文件就叫做设备树,文件扩展名为.dts。一个SOC可以出很多不同的班子,不同的板子肯定有相同的部分信息,将这些相同信息提取到一个文件中,其他dts文件引用这个文件,避免重复定义,这个通用文件就是dtsi文件,有点类似于C语言中的头文件。

2 DTS、DTB和DTC

设备树源文件为dts文件,该文件编译之后,成为dtb文件,而编译dts文件的工具是DTC工具。
该工具是以源码的形式存在于内核源码中的,位于kernel/scripts/dtc目录下
可以使用dtc工具编译dts文件:
这里是Android系统内核,首先需要执行defconfig配置:

make ARCH=arm64 rockchip_defconfig android-11.config

然后编译

make ARCH=arm64 dtbs

输出如下:

CALL    scripts/checksyscalls.sh
DTC     arch/arm64/boot/dts/rockchip/px30-ad-d6-anx6345.dtb
DTC     arch/arm64/boot/dts/rockchip/px30-ad-r35-mb-rk618-dual-lvds.dtb
DTC     arch/arm64/boot/dts/rockchip/px30-ad-r35-mb-rk618-hdmi.dtb
DTC     arch/arm64/boot/dts/rockchip/px30-ad-r35-mb-rk618-hdmi-lvds.dtb
DTC     arch/arm64/boot/dts/rockchip/px30-ad-r35-mb-rk618-lvds.dtb
DTC     arch/arm64/boot/dts/rockchip/px30-evb-ddr3-lvds-v10.dtb
DTC     arch/arm64/boot/dts/rockchip/px30-evb-ddr3-v10.dtb
DTC     arch/arm64/boot/dts/rockchip/px30-evb-ddr3-v10-avb.dtb
...

也可以单独编译某一个dts文件

make ARCH=arm64 rockchip/rk3568-atk-evb1-ddr4-v10.dtb

默认内核编译的脚本中会包含源码树文件的编译。

3 DTS语法

3.1 dtsi头文件

dtsi头文件类似于C语言中的头文件,包含一些公共的信息,可以在其他dts文件中导入

#include "rk3568-atk-evb1-ddr4-v10.dtsi"
#include "rk3568-linux.dtsi"

dts文件中除了可以引用dtsi文件,还可以引用.h头文件,甚至也可以引用dts文件。但是遵从编写习惯,还是将dtsi文件作为设备树的头文件,而不使用.h头文件。
一般.dtsi文件中描述的是SOC的内部外设信息,比如cpu架构、主频、外设寄存器地址范围等。

#include <dt-bindings/clock/rk3568-cru.h>
...
#include <dt-bindings/thermal/thermal.h>
#include "rk3568-dram-default-timing.dtsi"/ {compatible = "rockchip,rk3568";interrupt-parent = <&gic>;#address-cells = <2>;#size-cells = <2>;aliases {csi2dphy0 = &csi2_dphy0;csi2dphy1 = &csi2_dphy1;csi2dphy2 = &csi2_dphy2;dsi0 = &dsi0;dsi1 = &dsi1;ethernet0 = &gmac0;ethernet1 = &gmac1;gpio0 = &gpio0;gpio1 = &gpio1;gpio2 = &gpio2;gpio3 = &gpio3;gpio4 = &gpio4;i2c0 = &i2c0;...mmc0 = &sdhci;...serial0 = &uart0;...spi0 = &spi0;...};cpus {#address-cells = <2>;#size-cells = <0>;cpu0: cpu@0 {device_type = "cpu";compatible = "arm,cortex-a55";reg = <0x0 0x0>;enable-method = "psci";clocks = <&scmi_clk 0>;operating-points-v2 = <&cpu0_opp_table>;cpu-idle-states = <&CPU_SLEEP>;#cooling-cells = <2>;dynamic-power-coefficient = <187>;};cpu1: cpu@100 {device_type = "cpu";compatible = "arm,cortex-a55";reg = <0x0 0x100>;enable-method = "psci";clocks = <&scmi_clk 0>;operating-points-v2 = <&cpu0_opp_table>;cpu-idle-states = <&CPU_SLEEP>;};cpu2: cpu@200 {device_type = "cpu";compatible = "arm,cortex-a55";reg = <0x0 0x200>;enable-method = "psci";clocks = <&scmi_clk 0>;operating-points-v2 = <&cpu0_opp_table>;cpu-idle-states = <&CPU_SLEEP>;};cpu3: cpu@300 {device_type = "cpu";compatible = "arm,cortex-a55";reg = <0x0 0x300>;enable-method = "psci";clocks = <&scmi_clk 0>;operating-points-v2 = <&cpu0_opp_table>;cpu-idle-states = <&CPU_SLEEP>;};idle-states {entry-method = "psci";CPU_SLEEP: cpu-sleep {compatible = "arm,idle-state";local-timer-stop;arm,psci-suspend-param = <0x0010000>;entry-latency-us = <100>;exit-latency-us = <120>;min-residency-us = <1000>;};};};
}

以上是rk3568.dtsi文件的节选,包含了cpu、uart、I2c相关的一些设备信息。

3.2 设备节点

  1. 根节点
    设备树采用树形结构描述板子上的设备信息,每个设备都是一个节点,叫做设备节点,每个节点通过一些属性信息来描述节点信息,属性就是键-值对。
/ {...cpus {...};
}

这里的“/”是根节点,每个设备树文件只有一个根节点。如果引用的两个文件都有根节点,那么两个根节点会进行合并,合并成一个根节点。

  1. 子节点
aliases {...
}
cpus {...
}

以上位于根节点之下的为根节点的子节点,设备树中节点命名格式为:

node-name@unit-address

其中node-name是节点的名字,节点的名字能够清晰的描述节点的功能,比如“uart1”表示UART1这个外设。
unit-address一般表示设备的地址或者寄存器首地址,如果没有,则可以不要
查看cpu节点,显示如下:

cpu0:cpu@0

这里用:分为了两部分,前面是节点的标签,后面是节点定义

  1. 节点属性
    一个节点会包含不同的属性,不同的属性保存不同的内容,属性都是键值对,值可以为空或者任意的字节流,以下是常见的属性数据形式:
  2. 字符串
compatible="rockchip,rk3568";
  1. 32位无符号整数
reg=<0>;
  1. 字符串列表
compatible="rockchip,rk3568-evb", "rockchip,rk3568";

3.3 标准属性

以下是节点中常用的一些属性

  • 1、compatible属性
    compatible属性叫做“兼容性”属性,是一个字符串列表,用于将设备和驱动绑定起来。字符串列表用于选择设备所要使用的驱动程序,格式如下:
"manufacturer,model"

其中manufacture表示厂商,model一般是模块对应的驱动名字。比如rk3568-atk-evb1-v10.dtsi中有一个MIPI摄像头节点,芯片采用SNOY公司的IMX415,属性值如下:

compatible = "sony,imx415";

compatible可以有多个属性值,比如:

compatible = "arm,cortex-a55-pmu", "arm,armv8-pmuv3";

在这样的配置中,在查找设备对应的驱动时,先将第一个兼容值在Linux内核中查找,如果能够找到则使用该驱动文件,如果没有,则查看第二个兼容值,以此类推,直到查找完所有的兼容值。
一般驱动程序文件都会有一个OF匹配列表,用于保存compatible值,如果设备节点的compatible值和OF匹配列表中的任何一个值相等,则表示设备可以使用该驱动。比如文件buildroot系统源码中的imx415.c文件中:

static const struct of_device_id imx415_of_match[] = {{ .compatible = "sony,imx415" },{},
};

该数组中只有一个compatible值,当设备树中的哪个节点与该compatible值匹配的时候,那么这个节点就会使用此驱动文件

  • 2、model属性
    model属性值是一个字符串,一般model描述开发板的名字或者设备模块信息,比如:
model = "Rockchip rk3568 EVB DDR4 V10 Board";
  • 3、status属性
    表示设备状态,属性值是字符串
描述
“okay”表示设备可操作
“disabled”表示设备不可操作,但是未来可以变为可操作的,比如热插拔设备插入后
“fail”表示设备不可操作,设备检测到了一系列错误,设备也不太可能变成可操作
“fail-sss”和“fail”相同,后面的sss表示检测到的内容
  • 4、#address-cells和#size-cells属性
    这两个属性都是无符号32位整数,这两个属性可以用在任何拥有子节点的设备中,用于描述子节点的地址信息。#address-cells 属性值决定了子节点 reg 属性中地址信息所占用的字长(32 位),#size-cells 属性值决定了子节点 reg 属性中长度信息所占的字长(32 位)。
    这两个属性表明子节点如何编写reg属性值,reg属性和地址相关,这两个就是描述起始地址和地址长度。reg属性格式为:
reg = <address1 length1 address2 length2 address3 length3……>

每个“address length”组合表示一个地址范围,其中address是起始地址,length是地址长度,#address-cells 表明 address 这个数据所占用的字长,#size-cells 表明 length 这个数据所占用的字长。

  • 5、reg属性
    (address,length)对,描述设备地址空间资源信息或者设备地址信息,比如某个外设寄存器地址范围信息,或者IIC设备地址等。
uart5: serial@fe690000 {compatible = "rockchip,rk3568-uart", "snps,dw-apb-uart";reg = <0x0 0xfe690000 0x0 0x100>;...
}

这里0xfe690000描述的是uart5寄存器首地址,长度为0x100

  • 6、ranges属性
    ranges属性值可以为空或者按照(child-bus-address,parent-bus-address,length)格式编写的数字矩阵,ranges 是一个地址映射/转换表,ranges 属性每个项目由子地址、父地址和地址空间长度
    这三部分组成:
    child-bus-address:子总线地址空间的物理地址,由父节点的#address-cells 确定此物理地址所占用的字长。
    parent-bus-address :父总线地址空间的物理地址,同样由父节点的#address-cells 确定此物理地址所占用的字长。
    length :子地址空间的长度,由父节点的#size-cells 确定此地址长度所占用的字长。
    如果 ranges 属性值为空值,说明子地址空间和父地址空间完全相同,不需要进行地址转换,对于我们所使用的 RK3568 来说,子地址空间和父地址空间完全相同,因此会在 rk3568.dtsi 中找到大量的值为空的 ranges 属性
  • 7、name属性
    记录节点名字,已被弃用
  • 8、device_type属性
    用于描述设备的FCode,但是设备树没有FCode,所以此属性不会使用。此属性只能用于cpu节点或者memory节点。

3.4 根节点compatible属性

每个节点都有compatible属性,根节点也是,根节点的属性内容如下:

/ {model = "Rockchip RK3568 EVB1 DDR4 V10 Board";compatible = "rockchip,rk3568-evb1-ddr4-v10", "rockchip,rk3568";
};

rk3568-atk-evb1-ddr4-v10.dtsi中定义的根节点中的compatible包含两个值。设备节点中的compatible用于匹配驱动程序,根节点中的有什么作用呢?这里根节点第一个通常描述设备名称,这里使用的就是“rk3568-evb1-ddr4-v10”这个设备。第二个值描述了设备使用的SOC,这里使用的是rk3568。
Linux内核会根据根节点的compatible属性查看是否支持此设备,如果支持的话就会启动Linux内核。
include/linux/rockchip/cpu.h

static inline bool cpu_is_rk3568(void)
{if (rockchip_soc_id)return (rockchip_soc_id & ROCKCHIP_CPU_MASK) == ROCKCHIP_CPU_RK3568;return of_machine_is_compatible("rockchip,rk3568");
}

这里会判断soc是否为rk3568,根据根节点的compatible属性进行判断。

3.5 向节点追加或修改内容

假设要将一个fxls8471的设备连接到rk3568的i2c5这个端口上,那么就需要向i25节点中添加一个fxls8471子节点

i2c5: i2c@fe5e0000 {compatible = "rockchip,rk3399-i2c";reg = <0x0 0xfe5e0000 0x0 0x1000>;clocks = <&cru CLK_I2C5>, <&cru PCLK_I2C5>;clock-names = "i2c", "pclk";interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;pinctrl-names = "default";pinctrl-0 = <&i2c5m0_xfer>;#address-cells = <1>;#size-cells = <0>;status = "disabled";fxls8471: fxls8471@1e {compatible = "fsl,fxls8471";reg = <0x1e>;};
};

这样就添加了一个节点,但是有个问题是,这里添加的文件是rk3568.dtsi文件,而该文件是设备树头文件,所有使用SOC的版本都会引用这个文件,那么对于特定设备的修改缺影响了所有的该SOC的板子,这是不合理的。所以需要向设备追加数据,目前该板子使用的设备树文件为rk3568-atk-evb1-ddr4-v10.dsi,因此可以在这个文件中追加

&i2c5 {status = "okay";clock-frequency = <400000>;fxls8471: fxls8471@1e {compatible = "fsl,fxls8471";reg = <0x1e>;};
}

&i2c5表示访问i2c5这个label所对应的节点,label是通过aliases设置的,这样就完成了子节点的追加。同时修改了i2c5这个设备的状态,从disabled改为okay。

4 创建小型模板设备树

/ {compatible = "rockchip,rk3568-evb1-ddr4-v10","rockchip,rk3568";cpus {#address-cells = <2>;#size-cells = <0>;cpu0: cpu@0 {device_type = "cpu";compatible = "arm,cortex-a55";reg = <0x0 0x0>;};cpu1: cpu@100 {device_type = "cpu";compatible = "arm,cortex-a55";reg = <0x0 0x100>;};cpu2: cpu@200 {device_type = "cpu";compatible = "arm,cortex-a55";reg = <0x0 0x200>;};cpu3: cpu@300 {device_type = "cpu";compatible = "arm,cortex-a55";reg = <0x0 0x300>;};}uart2: serial@fe660000 {compatible = "rockchip,rk3568-uart", "snps,dw-apb-uart";reg = <0x0 0xfe660000 0x0 0x100>;};spi0: spi@fe610000 {compatible = "rockchip,rk3568-spi","rockchip,rk3066-spi";reg = <0x0 0xfe610000 0x0 0x1000>;};i2c5: i2c@fe5e0000 {compatible = "rockchip,rk3568-i2c","rockchip,rk3399-i2c";reg = <0x0 0xfe5e0000 0x0 0x1000>;};
};

这里创建了一个小型设备树,描述rk3568的cpu、uart2、spi0和i2c5这几个节点

5 设备树在系统中的体现

可以通过根文件系统的/proc/device-tree目录查看设备树,每一个节点都会在这个目录下生成一个文件夹
在这里插入图片描述
查看根节点属性
在这里插入图片描述
以上为model属性
在这里插入图片描述
以上是compatible属性
在这里插入图片描述
以上是cpus节点,其下的子节点也会创建目录
在这里插入图片描述
以上是aliases节点,目录下有很多文件,每个文件内容就是其节点的真实名字
在这里插入图片描述
以上是aliases中的i2c0文件内容
在这里插入图片描述
以上是chosen子节点,重点是bootargs参数,是为了uboot向Linux内核传递数据的。

Kernel command line: storagemedia=emmc androidboot.storagemedia=emmc androidboot.mode=normal  androidboot.dtb_idx=0 androidboot.dtbo_idx=0  androidboot.verifiedbootstate=orange androidboot.serialno=daf9b9fe169b1e37 console=ttyFIQ0 androidboot.baseband=N/A androidboot.wificountrycode=CN androidboot.veritymode=enforcing androidboot.hardware=rk30board androidboot.console=ttyFIQ0 androidboot.verifiedbootstate=orange firmware_class.path=/vendor/etc/firmware init=/init rootwait ro loop.max_part=7 buildvariant=eng earlycon=uart8250,mmio32,0xfe660000 androidboot.boot_devices=fe310000.sdhci,fe330000.nandc

从Android的内核日志中可以看到kernel command line就是chosen中bootargs中的值。这个是Android中的,这里会比设备树中的chosen定义多一些数据,多出来的这部分是uboot传递给Linux内核的,boot会对将其与设备树中的定义进行组合,并传递给kernel。

6 绑定信息文档

设备树描述设备信息,不同的设备信息不同,反映到设备树中就是属性不同。如果需要添加一个硬件对应的节点,可以通过Linux内核源码中的Documentation/devicetree/bindings目录下的文档进行配置,这些文档称为绑定文档。
在这里插入图片描述
比如添加一个i2c文档,可以查阅Documentation/devicetree/bindings/i2c/i2c-rk3x.txt文件:

* Rockchip RK3xxx I2C controllerThis driver interfaces with the native I2C controller present in Rockchip
RK3xxx SoCs.Required properties :- reg : Offset and length of the register set for the device- compatible: should be one of the following:- "rockchip,rv1108-i2c": for rv1108- "rockchip,rv1126-i2c": for rv1126- "rockchip,rk3066-i2c": for rk3066- "rockchip,rk3188-i2c": for rk3188- "rockchip,rk3228-i2c": for rk3228- "rockchip,rk3288-i2c": for rk3288- "rockchip,rk3328-i2c", "rockchip,rk3399-i2c": for rk3328- "rockchip,rk3399-i2c": for rk3399- interrupts : interrupt number- clocks: See ../clock/clock-bindings.txt- For older hardware (rk3066, rk3188, rk3228, rk3288):- There is one clock that's used both to derive the functional clockfor the device and as the bus clock.- For newer hardware (rk3399): specified by name- "i2c": This is used to derive the functional clock.- "pclk": This is the bus clock.Required on RK3066, RK3188 :- rockchip,grf : the phandle of the syscon node for the general registerfile (GRF)- on those SoCs an alias with the correct I2C bus ID (bit offset in the GRF)is also required.Optional properties :- clock-frequency : SCL frequency to use (in Hz). If omitted, 100kHz is used.- i2c-scl-rising-time-ns : Number of nanoseconds the SCL signal takes to rise(t(r) in I2C specification). If not specified this is assumed to bethe maximum the specification allows(1000 ns for Standard-mode,300 ns for Fast-mode) which might cause slightly slower communication.- i2c-scl-falling-time-ns : Number of nanoseconds the SCL signal takes to fall(t(f) in the I2C specification). If not specified this is assumed tobe the maximum the specification allows (300 ns) which might causeslightly slower communication.- i2c-sda-falling-time-ns : Number of nanoseconds the SDA signal takes to fall(t(f) in the I2C specification). If not specified we'll use the SCLvalue since they are the same in nearly all cases.Example:aliases {i2c0 = &i2c0;
}i2c0: i2c@2002d000 {compatible = "rockchip,rk3188-i2c";reg = <0x2002d000 0x1000>;interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;#address-cells = <1>;#size-cells = <0>;rockchip,grf = <&grf>;clock-names = "i2c";clocks = <&cru PCLK_I2C0>;i2c-scl-rising-time-ns = <800>;i2c-scl-falling-time-ns = <100>;
};

这里描述了i2c设备节点配置包含的属性,如reg、compatible等,还有一个示例。

7 设备树常用OF操作函数

设备树描述了设备的详细信息,这些信息通过Linux内核提供了一系列的函数来获取,这一系列的函数都有一个统一的前缀“of_”,这些of函数定义在include/linux/of.h文件中

7.1 查找节点的OF函数

Linux内核使用device_node结构体来描述一个节点

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 properties */struct	device_node *parent;struct	device_node *child;struct	device_node *sibling;
#if defined(CONFIG_OF_KOBJ)struct	kobject kobj;
#endifunsigned 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函数有5个:

  1. of_find_node_by_name
extern struct device_node *of_find_node_by_name(struct device_node *from, const char *name);

通过节点名称查找,from表示开始节点,如果为NULL,表示从根节点开始查找整个设备树,name表示要查找的节点名字。

  1. of_find_node_by_type
extern struct device_node *of_find_node_by_type(struct device_node *from, const char *type);

通过device_type属性来查找指定的节点,from和第一个一样,type表示device_type

  1. of_find_compatible_node
extern struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compat);

通过device_type和compatible这两个属性查找节点,device_type可以为NULL,表示忽略掉device_type属性。

  1. of_find_matching_node_and_match
struct of_device_id {char	name[32];char	type[32];char	compatible[128];const void *data;
};extern 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);

通过of_device_id匹配表来查找节点,match表示找到的匹配的of_device_id

  1. of_find_node_by_path
static inline struct device_node *of_find_node_by_path(const char *path)
{return of_find_node_opts_by_path(path, NULL);
}

通过路径来查找指定的节点

7.2 查找父/子节点的OF函数

  1. of_get_parent
extern struct device_node *of_get_parent(const struct device_node *node);

用于获取指定节点的父节点
3. of_get_next_child

extern struct device_node *of_get_next_child(const struct device_node *node,struct device_node *prev);

node表示父节点,prev表示前一个子节点,也就是从哪一个子节点开始遍历查找对应的子节点,设置为NULL表示从node父节点的第一个子节点开始查找。

7.3 提取属性值的OF函数

Linux内核中使用property表示设备树中的属性

struct property {char	*name;int	length;void	*value;struct property *next;
#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)unsigned long _flags;
#endif
#if defined(CONFIG_OF_PROMTREE)unsigned int unique_id;
#endif
#if defined(CONFIG_OF_KOBJ)struct bin_attribute attr;
#endif
};

Linux提供了读取该属性值的OF函数
4. of_find_property

extern struct property *of_find_property(const struct device_node *np,const char *name,int *lenp);

np表示设备节点,name表示属性的名字,lenp表示属性值的字节数,返回找到的property
5. of_property_count_elems_of_size

extern int of_property_count_elems_of_size(const struct device_node *np,const char *propname, int elem_size);

该函数用于获取属性中元素的数量,np为设备节点,propname表示属性的名字,elem_size表示元素长度。
6. of_property_read_u32_index

extern int of_property_read_u32_index(const struct device_node *np,const char *propname,u32 index, u32 *out_value);

该函数用于从属性中获取指定标号的u32类型数据值。
np表示设备节点,propname表示属性名称,index表示读取的值的标号,out_value表示读取到的值。
返回值0表示成功,负值表示读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小
7. of_property_read_u8_array、of_property_read_u16_array、of_property_read_u32_array 、of_property_read_u64_array

static inline int of_property_read_u8_array(const struct device_node *np,const char *propname, u8 *out_values, size_t sz)
static inline int of_property_read_u16_array(const struct device_node *np,const char *propname, u16 *out_values, size_t sz)
static inline int of_property_read_u32_array(const struct device_node *np,const char *propname,u32 *out_values, size_t sz)
static inline int of_property_read_u64_array(const struct device_node *np,const char *propname,u64 *out_values, size_t sz)

分别读取属性的u8、u16、u32、u64类型的数组,sz表示要读取数组元素数量。
8. of_property_read_u8、of_property_read_u16、of_property_read_u32、of_property_read_u64

static inline int of_property_read_u8(const struct device_node *np,const char *propname,u8 *out_value)
static inline int of_property_read_u16(const struct device_node *np,const char *propname,u16 *out_value)
static inline int of_property_read_u32(const struct device_node *np,const char *propname,u32 *out_value)
static inline int of_property_read_u64(const struct device_node *np,const char *propname,u64 *out_value)

分别读取属性的u8、u16、u32、u64类型的数组
9. of_property_read_string

extern int of_property_read_string(const struct device_node *np,const char *propname,const char **out_string);

读取字符串类型的属性值
10. of_n_addr_cells

extern int of_n_addr_cells(struct device_node *np);

用于获取#address-cells属性值
11. of_n_size_cells

extern int of_n_size_cells(struct device_node *np);

用于获取#size-cells属性值

7.4 其他常用的OF函数

  1. of_device_is_compatible
static inline int of_device_is_compatible(const struct device_node *device,const char *name)

用于查看节点的compatible属性是否包含name指定的字符串,也就是检查节点的兼容性。
2. of_get_address

static inline const __be32 *of_get_address(struct device_node *dev, int index,u64 *size, unsigned int *flags)

用于获取地址相关的属性,主要是“reg”或者“assigned-addresses”属性。dev是设备节点,index表示读取的地址标号,如reg中的地址index,size表示地址长度。flags表示IORESOURCE_IO、IORESOURCE_MEM。
3. of_translate_address

extern const __be32 *of_get_address(struct device_node *dev, int index,u64 *size, unsigned int *flags);

用于将从设备树读取到的物理地址转换为虚拟地址
4. of_address_to_resource
IIC、SPI、GPIO 等这些外设都有对应的寄存器,这些寄存器其实就是一组内存空间,Linux内核使用 resource 结构体来描述一段内存空间, “resource”翻译出来就是“资源”,因此用 resource结构体描述的都是设备资源信息,resource 结构体定义在文件 include/linux/ioport.h 中,定义如下:

struct resource {resource_size_t start;resource_size_t end;const char *name;unsigned long flags;unsigned long desc;struct resource *parent, *sibling, *child;ANDROID_KABI_RESERVE(1);ANDROID_KABI_RESERVE(2);ANDROID_KABI_RESERVE(3);ANDROID_KABI_RESERVE(4);
};

对于32位的SOC,resource_size_t是u32类型,其中start表示起始地址,end表示结束地址,name是这个资源的名字,flags是自愿标志位,一般表示资源类型,可选的资源标志定义在include/linux/ioport.h文件中:

#define IORESOURCE_BITS		0x000000ff	/* Bus-specific bits */#define IORESOURCE_TYPE_BITS	0x00001f00	/* Resource type */
#define IORESOURCE_IO		0x00000100	/* PCI/ISA I/O ports */
#define IORESOURCE_MEM		0x00000200
#define IORESOURCE_REG		0x00000300	/* Register offsets */
#define IORESOURCE_IRQ		0x00000400
#define IORESOURCE_DMA		0x00000800
#define IORESOURCE_BUS		0x00001000#define IORESOURCE_PREFETCH	0x00002000	/* No side effects */
#define IORESOURCE_READONLY	0x00004000
#define IORESOURCE_CACHEABLE	0x00008000
#define IORESOURCE_RANGELENGTH	0x00010000
#define IORESOURCE_SHADOWABLE	0x00020000#define IORESOURCE_SIZEALIGN	0x00040000	/* size indicates alignment */
#define IORESOURCE_STARTALIGN	0x00080000	/* start field is alignment */#define IORESOURCE_MEM_64	0x00100000
#define IORESOURCE_WINDOW	0x00200000	/* forwarded by bridge */
#define IORESOURCE_MUXED	0x00400000	/* Resource is software muxed */#define IORESOURCE_EXT_TYPE_BITS 0x01000000	/* Resource extended types */
#define IORESOURCE_SYSRAM	0x01000000	/* System RAM (modifier) */#define IORESOURCE_EXCLUSIVE	0x08000000	/* Userland may not map this resource */#define IORESOURCE_DISABLED	0x10000000
#define IORESOURCE_UNSET	0x20000000	/* No address assigned yet */
#define IORESOURCE_AUTO		0x40000000
#define IORESOURCE_BUSY		0x80000000	/* Driver has marked this resource busy */
...

一般常见的资源标志就是 IORESOURCE_MEM 、 IORESOURCE_REG 和IORESOURCE_IRQ 等。
of_address_to_resource函数就是提取reg属性值,然后将其转换成resource结构体类型:

extern int of_address_to_resource(struct device_node *dev, int index,struct resource *r);
  1. of_iomap
extern void __iomem *of_iomap(struct device_node *device, int index);

该函数用于直接内存映射,之前通过ioremap函数完成物理地址到虚拟地址的映射,采用设备树开发之后通过of_iomap函数来获取内存地址所对应的虚拟地址。of_iomap本质是将reg属性中的地址信息转换为虚拟地址,如果reg属性有多段的话,可以通过index参数来指定映射哪一段内存

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

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

相关文章

Unity Meta Quest 开发调试工具 Immersive Debugger

XR 开发者社区链接&#xff1a; 赠送原版GPT、完整课程、项目下载、项目孵化宣发、答疑、投融资&#xff08;YY&#xff09; 此工具可以在头显当中对 Unity 脚本的参数进行调整&#xff08;相当于在编辑器 Inspector 里调整脚本参数&#xff09;&#xff0c;并且还可以查看 Un…

免费送源码:Java+B/S+MySQL springboot电影推荐系统 计算机毕业设计原创定制

摘 要 随着互联网与移动互联网迅速普及&#xff0c;网络上的电影娱乐信息数量相当庞大&#xff0c;人们对获取感兴趣的电影娱乐信息的需求越来越大,个性化的电影推荐系统成为一个热门。然而电影信息的表示相当复杂&#xff0c;己有的相似度计算方法与推荐算法都各有优势&#…

赋能特大城市水务数据安全高速运算,深圳计算科学研究院YashanDB数据库系统斩获“鼎新杯”二等奖

第三届“鼎新杯”数字化转型应用优秀案例评选结果日前正式公布&#xff0c;深圳计算科学研究院联合深圳市环境水务集团有限公司申报的《深圳环境水务国产数据库YashanDB&#xff0c;赋能特大城市水务数据安全高速运转》案例&#xff0c;经过5个多月的评审&#xff0c;从4000申报…

Docker搭建Cisco AnyConnect 教程

本章教程搭建一个Cisco AnyConnect 连接教程。 一、下载文件 因为是基于Docker方式进行搭建的,所以需要提前安装好Docker,本章不介绍如何安装Docker,可以自行百度解决。 通过网盘分享的文件:ocserv-docker 链接: https://pan.baidu.com/s/14-2p9jenqE0KWzMilVzV-A?pwd=4yd…

穷举vs暴搜vs深搜vs回溯vs剪枝(一)

文章目录 全排列子集找出所有子集的异或总和再求和全排列 II电话号码的字母组合 全排列 题目&#xff1a;全排列 思路 通过深度优先搜索的方式&#xff0c;不断枚举每个数在当前位置的可能性&#xff0c;然后回溯到上一个状态&#xff0c;直到枚举完所有可能性得到正确的结果 r…

幂律分布笔记

一、幂律分布的数据拟合 数据分箱&#xff1a; 所谓分箱就是对原始数据进行分组&#xff0c;然后对每一组内的数据进行平滑处理。常见的分箱方式主要有等深分箱、等宽分箱、用户自定义等 对数分箱&#xff1a; 对原数据进行分箱&#xff0c;第i个箱的宽度为bi&#xff0c;b…

客户案例 | Ansys与台积电和微软合作加速光子仿真

Ansys与台积电和微软展开合作&#xff0c;将硅光子器件的仿真和分析速度提高10倍以上 主要亮点 借助使用NVIDIA图形处理单元&#xff08;GPU&#xff09;的Microsoft Azure虚拟机&#xff0c;Ansys Lumerical™ FDTD 3D电磁仿真的光子器件仿真速度实现了10倍提升 凭借Azure云…

CRMEB标准版Mysql修改sql_mode

数据库配置 1.宝塔控制面板-软件商店-MySql-设置 2.点击配置修改&#xff0c;查找sql-mode或sql_mode &#xff08;可使用CtrlF快捷查找&#xff09; 3.复制 NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 然后替换粘贴&#xff0c;保存 注&#xff1a;MySQL8.0版本的 第三步用…

Vulkan 开发(三):Vulkan 物理设备

Vulkan 物理设备 图片来自《 Vulkan 应用开发指南》 上一节了解了 Vulkan 实例&#xff0c;一旦有了实例&#xff0c;就可以查找系统里安装的与 Vulkan 兼容的物理设备。 Vulkan 物理设备&#xff08;PhysicalDevice&#xff09;一般是指支持 Vulkan 的物理硬件&#xff0c;通…

基于模型设计的智能平衡移动机器人-基础实验eCAP

目录 eCAP基本介绍 捕捉模式或者是APWM模式 捕获模块功能 CCS中打开模型 eCAP基本介绍 TMS320F28069的捕获单元模块能够捕获外部输入引脚的逻辑状态&#xff08;电平的高或低、电平翻转时的上升沿或下降沿&#xff09;&#xff0c;并利用内部定时器对外部事件或者引脚状态变…

关于网络接口监测工具ifstat命令的功能详解以及Linux下lsof命令的使用详解

一、关于网络接口监测工具ifstat命令的应用 ifstat工具是个网络接口监测工具,比较简单看网络流量&#xff0c;像VMSTAT那样一行行显示着浏览信息&#xff0c;可以设置显示某个或所有网卡流量数据。ifstat默认不监控回环接口&#xff0c;显示的流量单位是KB。系统默认未安装&…

【目标检测---旋转框标注】roLabelImg安装与使用

在目标检测领域&#xff0c;数据标注是至关重要的一环。为了提升模型的准确率和泛化能力&#xff0c;高质量的标注数据集是必不可少的。而roLabelImg作为一款专门用于标注旋转框的工具&#xff0c;为处理复杂场景下的目标检测提供了极大的便利。本文将详细介绍roLabelImg的安装…

电力变压器故障诊断数据集(猫脸码客 第219期)

电力变压器故障诊断数据集 电力变压器作为电力系统中不可或缺的重要设备&#xff0c;其稳定性和可靠性直接关系到整个电网的安全运行。然而&#xff0c;由于运行环境复杂、负载多变以及设备老化等因素&#xff0c;变压器在运行过程中难免会出现各种故障。这些故障若不能及时发…

【解决Docker无剩余存储磁盘空间问题】

【解决Docker无剩余存储磁盘空间问题】 目录 【解决Docker无剩余存储磁盘空间问题】一、问题概述二、问题原因三、解决方案1、方案一&#xff1a;清除Docker磁盘空间2、方案二&#xff1a;更换Docker磁盘存储目录 一、问题概述 执行Docker build -t [镜像名] [源目录] 命令报错…

基于Neo4j的推理知识图谱展示:智能系统与图谱可视化

还在找毕业设计项目吗&#xff1f;试试我们基于Neo4j打造的推理知识图谱展示系统&#xff01;这是一个兼具前沿技术与实战经验积累的项目&#xff0c;完美适合作为你的毕业设计。 &#x1f449; 什么是知识图谱&#xff1f; 简单来说&#xff0c;它是通过连接的节点&#xff0…

线性代数基础02

目录 1.向量 1.1向量的定义 1.2向量的运算 1.2.1向量加法 1.2.2向量数乘 1.2.3向量点积 1.3矩阵的特征值和特征向量 1.4向量的模 1.4.1向量的模的定义 1.4.2向量的模的几何解释 1.4.3向量的模的性质 1.5向量的内积 1.5.1向量的内积的定义 1.5.2向量的内积的几何解…

STMicroelectronics 意法半导体芯片选型表

意法半导体作为全球知名的半导体厂商&#xff0c;其产品广泛应用于各个领域&#xff0c;从消费电子到工业控制&#xff0c;从汽车电子到通信设备&#xff0c;都能看到意法半导体芯片的身影。在电子硬件设计领域&#xff0c;芯片的选型至关重要。亿配芯城&#xff08;ICgoodFind…

WPF常见容器全方位介绍

Windows Presentation Foundation (WPF) 是微软的一种用于构建Windows桌面应用程序的UI框架。WPF的布局系统基于容器&#xff0c;帮助开发者以灵活、响应的方式组织用户界面 (UI) 元素。本篇文章将详细介绍WPF中几种常见的容器&#xff0c;包括Grid、StackPanel、WrapPanel、Do…

Aspose.PDF功能演示:使用 JavaScript 从 PDF 中提取文本

在数据提取、业务文档自动化和文本挖掘方面&#xff0c;使用 JavaScript 从PDF中提取文本非常有用。它允许开发人员自动执行从 PDF 收集信息的过程&#xff0c;从而显著提高处理大量文档的生产力和效率。在这篇博文中&#xff0c;我们将学习如何使用 JavaScript 从 PDF 中提取文…

python-django-mysql原生sql增删改查搭建搭建web项目

先看我本地的项目结构 1 设置虚拟环境 python -m venv venv .\venv\Scripts\activate 2 在虚拟环境中安装Django 执行 pip install -r requirements.txt asgiref3.8.1 backports.zoneinfo0.2.1 Django3.2 mysqlclient2.2.4 pytz2024.2 sqlparse0.5.1 typing-extensions4.1…