热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

i.MX6ULL驱动开发|04Linux设备树基本语法与实例解析

文章目录一、设备树简介1.设备树在ARM架构的引入2.什么是设备树3.DTS、DTSI、DTB4.设备树编译二、设备树语法1.设备树版本2.设备树节点3.设备树节点标准属性4.特殊


文章目录

  • 一、设备树简介
    • 1. 设备树在ARM架构的引入
    • 2. 什么是设备树
    • 3. DTS、DTSI、DTB
    • 4. 设备树编译
  • 二、设备树语法
    • 1. 设备树版本
    • 2. 设备树节点
    • 3. 设备树节点标准属性
    • 4. 特殊节点
    • 5. 向节点追加内容
  • 三、设备树实例
    • i.MX6ULL内部框图
    • 如何寻找开发板对应的设备树文件
    • 1. skeleton描述文件
    • 2. imx6ull芯片级描述文件(通用)
      • 2.1. 根节点的补充
      • 2.2. aips2总线节点分析
      • 2.3. i2c控制器节点分析
    • 3. imx6ull ATK开发板描述文件
      • 3.1. 版本
      • 3.2. 根节点的补充
      • 3.3. 子节点的补充
      • 3.4. 磁力计mag3110节点分析
  • 四、设备树在系统中的体现
  • 五、设备树绑定信息文档
  • 六、Linux内核的OF操作函数
    • 1. 内核对于设备树节点的描述
    • 2. 查找节点
    • 3. 获取父子节点
    • 4. 提取属性值
    • 6. 地址相关操作
    • 7. 其它常用OF函数
  • 参考资料


一、设备树简介


1. 设备树在ARM架构的引入

在之前使用S3C2440开发板移植Linux 3.4.2内核时,修改了很多关于c文件去适配开发板,和开发板相关的文件放在arch/arm/mxch-xxx目录下,因此linux内核arm架构下添加了很多开发板的适配文件:

这些c文件仅仅用来适配某款开发板,对于Linux内核来说并没有提交什么新功能,但是每适配一款新的开发板就需要一堆文件,导致Linux内核越来越臃肿:

终于Linus忍不住天天merge这些鬼东西,向arm社区发出了一封邮件,第一句话就足矣表现不满:“This whole ARM thing is a f*cking pain in the ass”。

因此,Arm社区开始引入之前powerPC架构就采用的设备树,将描述这些板级信息的文件与Linux内核代码分离,Linux 4.x版本几乎都支持设备树,所有开发板的设备树文件统一放在arch/arm/boot/dts目录中


2. 什么是设备树

设备树全称Device Tree,是一种数据结构,用来描述板级设备信息,比如CPU数量、外设基地址、总线设备等,如图:


3. DTS、DTSI、DTB

(1)DTS:设备树描述文件为.dts格式,这个也是我们重点需要掌握编写的。
(2)DTSI

为了减少冗余,设备树头文件格式为.dtsi文件,可以被不同的.dts文件引用。

比如imx6ull有野火、正点原子、米尔、百问网等很多款开发板,这些开发板肯定需要一个dts文件来描述,但是关于imx6ull芯片级别的描述,就不需要每个文件都去描述一下,而是大家都引用NXP官方提供的.dtsi描述文件即可。

这样既最大化的降低了设备描述文件的冗余程序,也极大的降低了开发者适配新开发板的工作量

(3)DTC

编写.dtc文件使用设备树语法,则需要一个特定的编译器来编译,称为dtc工具,源码在Linux内核的scripts/dtc目录下。

(4)DTB:设备树源码.dts.dtsi文件最终经过dtc编译器,会生成.dtb文件。


4. 设备树编译

(1)简单粗暴,编译内核

make

(2)编译全部设备树文件

make dtbs

(3)编译指定的设备树文件

make <xxx.dtb>

二、设备树语法


1. 设备树版本

/dts-v1/

2. 设备树节点

设备树是由一个个节点组成的&#xff0c;每个节点相当于树上的一片叶子&#xff0c;节点的结构和约定如下。

&#xff08;1&#xff09;节点名称

node-name&#64;unit-address

node-name指明了节点的名称&#xff0c;长度应该为1-31个字符&#xff0c;命名应该以小写或者大写字母开头&#xff0c;支持的字符如下表&#xff1a;

节点名称的unit-address表示设备地址或者寄存器首地址&#xff08;具体节点具体分析&#xff0c;不一定是绝对地址&#xff09;&#xff0c;必须与节点reg属性中指定的首地址匹配

eg. imx6ull.dtsi中描述的uart1控制器节点&#xff1a;

该节点label为uart1&#xff0c;节点名称为serial&#xff0c;设备地址&#xff08;寄存器首地址&#xff09;为02020000&#xff0c;正是imx6ull uart1外设寄存器的首地址

&#xff08;2&#xff09;路径名称

通过指定从根节点到所有子节点到所需节点的完整路径&#xff0c;可以唯一地标识设备树中的节点。

指定设备路径的约定如下&#xff1a;

/node-name-1/node-name-2/node-name-N

&#xff08;3&#xff09;属性

设备树中的每个节点都有用来描述节点信息的属性。属性由名称和值两部分组成&#xff0c;属性名称的可用字符如下表&#xff1a;

属性值是一个由零个或多个字节组成的数组&#xff0c;其中包含与属性相关联的信息&#xff0c;支持的数据类型如下&#xff1a;


&#xff08;4&#xff09;节点标签

节点标签用&#xff1a;隔开&#xff0c;为了方便访问节点&#xff0c;可用直接通过&node-lable来访问节点&#xff0c;示例如下&#xff1a;

node-label: node-name&#64;unit-address {};

3. 设备树节点标准属性

DTSpec为设备节点指定一组标准属性&#xff0c;如下。

&#xff08;1&#xff09;compatible

compatible属性值由string list组成&#xff0c;定义了设备的兼容性&#xff0c;推荐格式为manufacturer,model&#xff0c;manufacturer描述了生产商&#xff0c;model描述了型号。

compatible &#61; "fsl,imx6ull-14x14-evk", "fsl,imx6ull";

开发板上的音频芯片采用的欧胜WM8960&#xff0c;sound节点的compatible属性值如下&#xff1a;

compatible &#61; "fsl,imx6ul-evk-wm8960","fsl,imx-audio-wm8960"

使用的时候&#xff0c;sound这个设备首先使用第一个兼容值在Linux内核中查找&#xff0c;看看能不能找到对应的驱动文件&#xff1b;如果没有找到的话&#xff0c;就使用第二个兼容值查找。

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

比如在文件imx-wm8960.c文件中&#xff1a;

static const struct of_device_id imx_wm8960_dt_ids[] &#61; {{ .compatible &#61; "fsl,imx-audio-wm8960", },{ /* sentinel */ }
};

这个数组就是imx-wm8960.c这个驱动文件的匹配表。

&#xff08;2&#xff09; model

model属性值是一个string&#xff0c;指明了设备的厂商和型号&#xff0c;推荐格式为manufacturer,model

model &#61; "Freescale i.MX6 ULL 14x14 EVK Board";

&#xff08;3&#xff09; phandle

phandle属性值是一个u32&#xff0c;为设备树中唯一的节点指定一个数字标识符&#xff0c;用于其它节点指明关系。

&#xff08;4&#xff09; status

status属性值是一个string&#xff0c;表示设备的运行状态&#xff0c;可用值如下表&#xff1a;

&#xff08;5&#xff09;#address-cells 和 #size-cells

#address-cells and #size-cells属性值是一个u32&#xff0c;可以用在任何拥有子节点的设备中&#xff0c;并描述子设备节点应该如何寻址

#address-cells属性定义子节点reg属性中地址字段所占用的字长&#xff0c;也就是占用u32 单元格的数量。

#size-cells属性定义子节点reg属性值的长度所占用的 u32 单元格的数量。

&#xff08;6&#xff09;reg

reg属性值是一个 prop-encoded-array&#xff0c;用来描述设备地址空间资源信息&#xff0c;一般是某个外设的寄存器地址范围信息&#xff0c;包括起始地址和地址长度。

reg &#61;

其中address是起始地址&#xff0c;length是地址长度。#address-cells表明address这个数据所占用的字长&#xff0c;#size-cells表示length这个数据所占用的字长。

比如&#xff1a;

spi4 {compatible &#61; "spi-gpio";pinctrl-names &#61; "default";pinctrl-0 &#61; <&pinctrl_spi4>;status &#61; "okay";gpio-sck &#61; <&gpio5 11 0>;gpio-mosi &#61; <&gpio5 10 0>;num-chipselects &#61; <1>;#address-cells &#61; <1>;#size-cells &#61; <0>;gpio_spi: gpio_spi&#64;0 {compatible &#61; "fairchild,74hc595";gpio-controller;#gpio-cells &#61; <2>;reg &#61; <0>;registers-number &#61; <1>;registers-default &#61; /bits/ 8 <0x57>;spi-max-frequency &#61; <100000>;};};

#address-cells &#61; <1>表示子节点中reg属性的address占用1个u32数据&#xff0c;#size-cells表示子节点中reg属性的length不占用空间&#xff0c;没有。

在子节点gpio_spi中&#xff1a;reg属性值设置为<0>&#xff0c;相当于设置了起始地址&#xff0c;而没有设置地址长度。

&#xff08;7&#xff09;virtual-reg

&#xff08;8&#xff09;ranges

&#xff08;9&#xff09;dma-ranges


4. 特殊节点

&#xff08;1&#xff09;根节点

树是由树根开始的&#xff0c;在设备树中称之为根节点&#xff0c;路径为/&#xff0c;根节点不需要节点名称&#xff0c;所有子节点都是挂在根节点上的&#xff0c;可以看到最简单的根节点如下&#xff1a;

/ {};

根节点的属性有&#xff1a;

&#xff08;2&#xff09;aliases

aliases节点用来定义别名&#xff0c;为了内核方便访问节点。

&#xff08;3&#xff09;chosen

chosen节点是为了uboot向Linux内核传递数据&#xff0c;重点是bootargs参数&#xff0c;一般.dts文件中chosen节点通常为空或者内容很少。


此处关于uboot如何通过设备树传参给kernel&#xff0c;可以单独写篇文章&#xff0c;待补充…



5. 向节点追加内容

&#xff08;1&#xff09;向根节点追加内容

/ {//要补充的内容
};

&#xff08;2&#xff09;向子节点追加内容

&node-label {//追加内容
};

三、设备树实例


i.MX6ULL内部框图


如何寻找开发板对应的设备树文件

直接查看arch/arm/boot/dts/Makefile文件&#xff0c;在该文件中查找即可&#xff0c;比如imx6ull相关部分如下&#xff1a;

dtb-$(CONFIG_SOC_IMX6ULL) &#43;&#61; \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-atk-emmc.dtb \imx6ull-9x9-evk-btwifi.dtb \imx6ull-9x9-evk-ldo.dtb

可以看到所有imx6ull的开发板&#xff0c;其中我们移植的开发板为imx6ull-atk-emmc.dtb&#xff0c;本文就以该设备树文件为例&#xff0c;讲述设备树语法。


1. skeleton描述文件

查看文件arch/arm/boot/dts/skeleton.dtsi&#xff0c;内容非常简洁&#xff0c;只定义了根节点&#xff1a;


2. imx6ull芯片级描述文件&#xff08;通用&#xff09;

不同的imx6ull开发板都是使用imx6ull这颗处理器芯片&#xff0c;而imx6ull soc芯片级的描述是固定的&#xff0c;通常这个也是由芯片厂商提供

查看imx6ull.dtsi文件&#xff0c;整体框架如下&#xff1a;

接下来我们逐个分析。


2.1. 根节点的补充

该文件引用的skeleton.dtsi文件中&#xff0c;已经定义了根节点&#xff0c;如果再次定义根节点&#xff0c;其中的内容将作为对根节点的补充

在该描述文件中&#xff0c;挂在根节点上的子节点有&#xff1a;aliases、cpus、intc、clocks、soc。

&#xff08;1&#xff09;aliases节点

aliases节点用来定义一个或多个别名属性&#xff0c;按照约定&#xff0c;该节点应该在根节点上。

&#xff08;2&#xff09;cpus节点
所有的设备树都需要cpus节点&#xff0c;用来描述系统的CPU信息。

i.MX6ULL是单核处理器&#xff0c;因此只有一个/cpus/cpu*子节点&#xff0c;用来表示某一个具体CPU核的信息&#xff0c;其中有以下属性&#xff1a;


  • compatible&#xff1a;
  • device_type&#xff1a;描述设备类型
  • reg
  • clock-latency
  • operating-points
  • fsl,soc-operating-points
  • fsl,low-power-run
  • clocks
  • clock-names


&#xff08;3&#xff09;intc节点

&#xff08;4&#xff09;clocks节点

&#xff08;5&#xff09;soc节点

soc节点中&#xff0c;描述了i.MX6ULL片上的总线和全部外设&#xff1a;


2.2. aips2总线节点分析

aips2: aips-bus&#64;02100000 {compatible &#61; "fsl,aips-bus", "simple-bus";#address-cells &#61; <1>;#size-cells &#61; <1>;reg &#61; <0x02100000 0x100000>;ranges;//一堆外设子节点&#xff0c;省略...
};

aips2节点的属性有&#xff1a;


  • compatible&#xff1a;兼容性
  • #address-cells&#xff1a;子节点reg属性中地址字段所占用的单元格数量&#xff0c;占用1个u32
  • size-cells&#xff1a;子节点reg属性值的长度所占用的单元格的数量&#xff0c;占用1个u32
  • reg&#xff1a;寄存器起始地址0x02100000&#xff0c;长度0x100000
  • ranges&#xff1a;空

2.3. i2c控制器节点分析

i2c控制器是挂在aips2总线上的&#xff0c;对应到设备树中&#xff0c;i2c控制器节点挂在aips2节点上&#xff0c;描述代码如下&#xff1a;

以i2c1节点为例&#xff0c;标签是i2c1&#xff0c;节点名称是i2c&#xff0c;寄存器起始地址是0x021a0000&#xff0c;有如下属性&#xff1a;


  • #address-cells&#xff1a;子节点reg属性中地址字段所占用的单元格数量&#xff0c;占用1个u32
  • size-cells&#xff1a;子节点reg属性值的长度所占用的单元格的数量&#xff0c;占用0个u32
  • compatible&#xff1a;兼容性&#xff0c;fsl,imx6ul-i2c和fsl,imx21-i2c
  • reg&#xff1a;寄存器&#xff0c;起始地址是0x021a0000&#xff0c;长度是0x4000
  • interrupts&#xff1a;中断&#xff0c;不了解A7的中断控制器&#xff0c;看不懂
  • clocks&#xff1a;时钟源&#xff0c;clks节点的IMX6UL_CLK_I2C1这个时钟
  • status&#xff1a;节点状态&#xff0c;禁用


3. imx6ull ATK开发板描述文件

imx6ull-atk-emmc.dts这个文件的大概框架如下。


3.1. 版本

/dts-v1/;

3.2. 根节点的补充

框架如下&#xff1a;

其中 chosen 节点是uboot用来向内核传递参数&#xff0c;内容如下&#xff1a;


3.3. 子节点的补充

根节点之后&#xff0c;使用引用符&来对imx6ull.dtsi文件中定义的子节点进行补充&#xff0c;用来描述开发板的具体配置&#xff0c;这个也是主要需要适配修改的文件。


3.4. 磁力计mag3110节点分析

在NXP官方开发板上&#xff0c;磁力计mag3110是接在i2c1总线控制器上的&#xff0c;对应到设备树中&#xff0c;磁力计节点挂在i2c1控制器节点上&#xff0c;如下。

可以看到&#xff0c;i2c1节点的补充描述中&#xff0c;就描述了i2c1控制器上所连接的设备。

i2c1节点的补充属性有&#xff1a;


  • clock-frequency&#xff1a;i2c控制器时钟频率&#xff0c;100khz
  • pinctrl-names&#xff1a;
  • pinctrl-0&#xff1a;
  • status&#xff1a;设备状态&#xff0c;就绪

i2c控制器上接了两个设备&#xff0c;一个是mag3110磁力计&#xff0c;一个是fxls8471加速度计。注意&#xff0c;在描述节点时&#xff0c;&#64;后面的地址变为了i2c总线的设备地址&#xff0c;mag3110的i2c从机地址是0e&#xff0c;fxls8471的i2c从机地址是1e


至此&#xff0c;imx6ull设备树分析完成&#xff0c;完整的思维导图文档在这里&#xff1a;【腾讯文档】imx6ull设备树。



四、设备树在系统中的体现

Linux内核启动的时候会解析设备树dtb文件&#xff0c;所以启动以后可以在根文件系统中看到设备树的节点信息&#xff0c;在/proc/device-tree目录中&#xff1a;

这里 device-tree目录是一个软链接&#xff0c;实际指向/sys/firmware/devicetree/base目录。

在device-tree目录中&#xff0c;首先可以看到设备树根节点下的所有一级子节点。

&#xff08;1&#xff09;属性是以文件的方式给出&#xff0c;可以直接查看。

比如查看根节点的model属性&#xff1a;

&#xff08;2&#xff09;节点以目录的方式给出

比如soc子节点的内容如下&#xff1a;


五、设备树绑定信息文档

在设备树中添加一个新的节点时&#xff0c;添加的格式在Linux内核源码中有详细的.txt文档描述&#xff0c;这些txt文档就称为绑定文档。

绑定文档在/Documentation/devicetree/bindings路径中&#xff1a;

比如我们在开发板的i2c上新添加了一个设备&#xff0c;需要在设备树的i2c节点下新添加一个节点&#xff0c;就可以查看i2c/i2c-imx.txt文档&#xff1a;


六、Linux内核的OF操作函数

Linux内核提供了一系列的函数来获取设备树中的节点或者属性信息&#xff0c;这一系列的函数都有一个统一的前缀of_&#xff0c;所以也称为OF函数&#xff0c;声明在文件include/linux/of.h文件中。


1. 内核对于设备树节点的描述

Linux内核使用device_node结构体来描述一个设备树节点&#xff0c;定义在文件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 properties */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
};

2. 查找节点

&#xff08;1&#xff09;通过节点名字查找节点

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

参数意义如下&#xff1a;


  • from&#xff1a;开始查找的节点&#xff0c;NULL表示根节点
  • name&#xff1a;要查找的节点名称

返回值为找到的节点&#xff0c;NULL为查找失败。

&#xff08;2&#xff09;通过节点类型查找节点

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

type参数指定要查看节点对应的type字符串&#xff0c;也就是device_type属性值。

&#xff08;3&#xff09;通过device_type和 compatible查找节点

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

&#xff08;4&#xff09;通过of_device_id匹配表来查找节点

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);

&#xff08;5&#xff09;通过路径来查找节点

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

这里的path必须要是绝对路径。


3. 获取父子节点

&#xff08;1&#xff09;获取父节点

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

&#xff08;2&#xff09;迭代查找子节点

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

prev参数是前一个子节点&#xff0c;如果为NULL表示从第一个子节点开始。


4. 提取属性值

在节点描述类型device_node中&#xff0c;有这样一项用来描述属性值&#xff1a;

struct property *properties;

property结构体类型定义如下&#xff1a;

struct property {char *name;int length;void *value;struct property *next;unsigned long _flags;unsigned int unique_id;struct bin_attribute attr;
};

&#xff08;1&#xff09;查找指定节点的属性

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

参数name指属性名字&#xff0c;lenp指属性值的字节数。

&#xff08;2&#xff09;获取属性中元素的数量

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

参数propname是需要统计元素数量的属性名字&#xff0c;参数elem_size是元素的长度。

返回值是获取到的属性元素数量。

eg. reg属性的值通常是一个数组&#xff0c;使用此函数可以获取的数组的大小。

&#xff08;3&#xff09;从属性中获取指定索引的u32类型数据值

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

参数out_value用来返回获取到的值。

返回值用来表示是否获取成功。

&#xff08;4&#xff09;从属性中获取数组值

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

eg. reg属性的值通常是一个数组&#xff0c;使用这个函数可以一次读取出一个数组&#xff0c;也就是reg属性的全部值。

&#xff08;5&#xff09;从属性中获取布尔值/整形值

/*** of_property_read_bool - Findfrom a property* &#64;np: device node from which the property value is to be read.* &#64;propname: name of the property to be searched.** Search for a property in a device node.* Returns true if the property exist false otherwise.*/
static inline bool of_property_read_bool(const struct device_node *np,const char *propname)
{struct property *prop &#61; of_find_property(np, propname, NULL);return prop ? true : false;
}static inline int of_property_read_u8(const struct device_node *np,const char *propname,u8 *out_value)
{return of_property_read_u8_array(np, propname, out_value, 1);
}static inline int of_property_read_u16(const struct device_node *np,const char *propname,u16 *out_value)
{return of_property_read_u16_array(np, propname, out_value, 1);
}static inline int of_property_read_u32(const struct device_node *np,const char *propname,u32 *out_value)
{return of_property_read_u32_array(np, propname, out_value, 1);
}static inline int of_property_read_s32(const struct device_node *np,const char *propname,s32 *out_value)
{return of_property_read_u32(np, propname, (u32*) out_value);
}

&#xff08;6&#xff09;从属性中获取字符串

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

&#xff08;7&#xff09;获取#address-cells和#size-cells属性值

extern int of_n_addr_cells(struct device_node *np);
extern int of_n_size_cells(struct device_node *np);

6. 地址相关操作

地址相关操作的函数定义在include/linux/of_address.h文件中。

&#xff08;1&#xff09;获取地址相关属性

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

参数index是要读取的地址标号&#xff0c;size是地址长度&#xff0c;flag是是参数&#xff0c;比如 IORESOURCE_IO、IORESOURCE_MEM等。

返回值是读取到的数据首地址&#xff0c;为NULL则表示读取失败。

&#xff08;2&#xff09;将从设备树读取到的地址转换为物理地址

extern u64 of_translate_address(struct device_node *np, const __be32 *addr);

返回值为转换得到的地址&#xff0c;如果为 OF_BAD_ADDR 的话表示转换失败。

&#xff08;3&#xff09;将地址转换为resources资源

GPIO、IIC、SPI这些外设都有对应的寄存器&#xff0c;这些寄存器就是一段内存空间&#xff0c;Linux内核使用 resource 结构体来描述一段内存空间&#xff0c;定义在文件include/linux/ioport.h中。

/** Resources are tree-like, allowing* nesting etc..*/
struct resource {resource_size_t start;resource_size_t end;const char *name;unsigned long flags;struct resource *parent, *sibling, *child;
};

其中&#xff1a;


  • start&#xff1a;资源起始地址
  • end&#xff1a;资源结束地址
  • name&#xff1a;资源名称
  • flags&#xff1a;资源类型
  • 链表节点&#xff0c;用于嵌套

资源类型同样定义在文件include/linux/ioport.h中&#xff0c;如下&#xff1a;

/** IO resources have these defined flags.*/
#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_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函数中&#xff0c;可以将reg属性给出的地址转换为resource资源&#xff1a;

extern int of_address_to_resource(struct device_node *dev, int index,struct resource *r);

&#xff08;4&#xff09;虚拟地址映射

之前使用ioremap函数来完成物理地址到虚拟地址的映射&#xff0c;采用设备树以后&#xff0c;可以直接通过of_iomap来完成物理地址到虚拟地址的映射。

void __iomem *of_iomap(struct device_node *node, int index);

参数index是reg属性中要完成内存映射的段&#xff0c;如果reg属性只有一段的话&#xff0c;index就设置为0。


7. 其它常用OF函数

&#xff08;1&#xff09;检查设备兼容性

extern int of_device_is_compatible(const struct device_node *device,const char *);

第二个参数用来指定要查看的字符串&#xff0c;该函数会检查指定的字符串是否在节点的compatible属性中。

至此&#xff0c;设备树基本知识学习完毕&#xff0c;又是一篇万字长文。


参考资料


  • 《I.MX6U嵌入式Linux驱动开发指南》&#xff0c;正点原子

推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Webpack5内置处理图片资源的配置方法
    本文介绍了在Webpack5中处理图片资源的配置方法。在Webpack4中,我们需要使用file-loader和url-loader来处理图片资源,但是在Webpack5中,这两个Loader的功能已经被内置到Webpack中,我们只需要简单配置即可实现图片资源的处理。本文还介绍了一些常用的配置方法,如匹配不同类型的图片文件、设置输出路径等。通过本文的学习,读者可以快速掌握Webpack5处理图片资源的方法。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • C语言注释工具及快捷键,删除C语言注释工具的实现思路
    本文介绍了C语言中注释的两种方式以及注释的作用,提供了删除C语言注释的工具实现思路,并分享了C语言中注释的快捷键操作方法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
author-avatar
豪哥帅366
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有