设备树的属性和绑定文件信息


前言

一、标准属性

标准的属性,就是设备的共有的属性。

1.compatible 属性-重要

compatible 属性也叫做“兼容性”属性,非常重要!compatible 属性的值是一个字符串列表,compatible 属性用于将设备和驱动绑定起来字符串列表用于选择设备所要使用的驱动程序,compatible 属性的值格式如下所示:

"manufacturer,model"

其中 manufacturer 表示厂商,model 一般是模块对应的驱动名字。比如 imx6ull-alientek-emmc.dts 中 sound 节点是 I.MX6U-ALPHA 开发板的音频设备节点,I.MX6U-ALPHA 开发板上的音频芯片采用的欧胜(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 内核里面查找,看看能不能找到与之匹配的驱动文件,也就是相应的.c文件如果没有找到的话就使用第二个兼容值查。
一般驱动程序文件都会有一个 OF 匹配表,此 OF 匹配表保存着一些 compatible 值,如果设备节点的 compatible 属性值和 OF 匹配表中的任何一个值相等,那么就表示设备可以使用这个驱动。
比如在文件 imx-wm8960.c 中有如下内容:==就像是去饭店去点菜,菜单上有的才可以点菜。==设备树通过此属性与对应驱动连接

632 static const struct of_device_id imx_wm8960_dt_ids[] = {
633 { .compatible = "fsl,imx-audio-wm8960", },
634 { /* sentinel */ }
635 };
636 MODULE_DEVICE_TABLE(of, imx_wm8960_dt_ids);
637
638 static struct platform_driver imx_wm8960_driver = {
639 .driver = {
640 .name = "imx-wm8960",
641 .pm = &snd_soc_pm_ops,
642 .of_match_table = imx_wm8960_dt_ids,
643 },
644 .probe = imx_wm8960_probe,
645 .remove = imx_wm8960_remove,
646 };

第 632~635 行的数组 imx_wm8960_dt_ids 就是 imx-wm8960.c 这个驱动文件的匹配表,此
匹配表只有一个匹配值“fsl,imx-audio-wm8960”。如果在设备树中有哪个节点的 compatible 属性值与此相等,那么这个节点就会使用此驱动文件。

2.model 属性

model 属性值也是一个字符串,一般 model 属性描述设备模块信息,比如名字什么的,比
如:

model = "wm8960-audio";

3.status 属性

status 属性看名字就知道是和设备状态有关的,status 属性值也是字符串,字符串是设备的
状态信息,用于控制外设的使能
在这里插入图片描述

4.#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 这个数据所占用的字长,比如:

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 行,子节点 gpio_spi: gpio_spi@0 的 reg 属性值为 <0>,因为父节点设置了#address-cells = <1>,#size-cells = <0>,因此 addres=0,没有 length 的值,相当于设置了起始地址,而没有设置地址长度。
第 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>,因为父节点设
置了#address-cells = <1>,#size-cells = <1>,address= 0x02280000,length= 0x4000,相当于设置了起始地址为 0x02280000,地址长度为 0x4000。

注意:如果子节点不管有没有#address-cells和#size-cells 属性信息,都以父节点的为准
上述就是常用的属性

二、Linux内核如何识别运行平台是否能运行呢

内核支持设备树的,也是通过compatible = “fsl,imx6ull-14x14-evk”, “fsl,imx6ull”;这个属性,来判断内核是否支持这个设备。也就是根目录的compatible属性。

三、绑定文件信息

设备树是用来描述板子上的设备信息的,不同的设备其信息不同,反映到设备树中就是属性不同。那么我们在设备树中添加一个硬件对应的节点的时候从哪里查阅相关的说明呢?在Linux 内核源码中有详细的.txt 文档描述了如何添加节点,这些.txt 文档叫做绑定文档,
路径为:Linux 源码目录/Documentation/devicetree/bindings
在这里插入图片描述
比如我们现在要想在 I.MX6ULL 这颗 SOC 的 I2C 下添加一个节点,那么就可以查看Documentation/devicetree/bindings/i2c/i2c-imx.txt,此文档详细的描述了 I.MX 系列的 SOC 如何在设备树中添加 I2C 设备节点,文档内容如下所示:
在这里插入图片描述
其他的在查看其他的文档内容。

四、设备树常用 OF 操作函数

驱动如何获取设备树中的节点信息呢,是通过OF函数来获取的。
设备树描述了设备的详细信息,这些信息包括数字类型的、字符串类型的、数组类型的,我们在编写驱动的时候需要获取到这些信息。
比如设备树使用 reg 属性描述了某个外设的寄存器地址为 0X02005482,长度为 0X400,我们在编写驱动的时候需要获取到 reg 属性的0X02005482 和 0X400 这两个值,然后初始化外设。
那该怎么获取呢,Linux 内核给我们提供了一系列的函数来获取设备树中的节点或者属性信息,这一系列的函数都有一个统一的前缀“of_”,所以被叫做 OF 函数。这些 OF 函数原型都定义在 include/linux/of.h 文件中。

1. 查找节点的 OF 函数

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

49 struct device_node {
50 const char *name; /* 节点名字 */
51 const char *type; /* 设备类型 */
52 phandle phandle;
53 const char *full_name; /* 节点全名 */
54 struct fwnode_handle fwnode;
55
56 struct property *properties; /* 属性 */
57 struct property *deadprops; /* removed 属性 */
58 struct device_node *parent; /* 父节点 */
59 struct device_node *child; /* 子节点 */
60 struct device_node *sibling;
61 struct kobject kobj;
62 unsigned long _flags;
63 void *data;
64 #if defined(CONFIG_SPARC)
65 const char *path_component_name;
66 unsigned int unique_id;
67 struct of_irq_controller *irq_trans;
68 #endif
69 };

of_find_node_by_name 函数

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属性以弃用

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 函数

of_find_compatible_node 函数根据 device_type 和 compatible 这两个属性查找指定的节点,函数原型如下:

struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compatible)

函数参数和返回值含义如下:
from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
type:要查找的节点对应的 type 字符串,也就是 device_type 属性值,可以为 NULL,表示忽略掉 device_type 属性。
compatible:要查找的节点所对应的 compatible 属性列表。
返回值:找到的节点,如果为 NULL 表示查找失败

of_find_matching_node_and_match 函数

of_find_matching_node_and_match 函数通过 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 函数-最常用

of_find_node_by_path 函数通过路径来查找指定的节点,函数原型如下:

inline struct device_node *of_find_node_by_path(const char *path)

函数参数和返回值含义如下:
path:带有全路径的节点名,可以使用节点的别名,比如“/backlight”就是 backlight 这个节点的全路径。
返回值:找到的节点,如果为 NULL 表示查找失败

2. 查找父节点和子节点的 OF 函数

of_get_parent 函数-很少使用

of_get_parent 函数用于获取指定节点的父节点(如果有父节点的话),函数原型如下:

struct device_node *of_get_parent(const struct device_node *node)

函数参数和返回值含义如下:
node:要查找的父节点的节点。
返回值:找到的父节点。

of_get_next_child 函数-很少使用

of_get_next_child 函数用迭代的方式查找子节点,函数原型如下:

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

函数参数和返回值含义如下:
node:父节点。
prev:前一个子节点,也就是从哪一个子节点开始迭代的查找下一个子节点。可以设置为NULL,表示从第一个子节点开始。
返回值:找到的下一个子节点。

3. 节点找了该获取其中的属性了

节点的属性信息里面保存了驱动所需要的内容,因此对于属性值的提取非常重要,Linux 内核中使用结构体 property 表示属性,此结构体同样定义在文件 include/linux/of.h 中,内容如下:

35 struct property {
36 char *name; /* 属性名字 */
37 int length; /* 属性长度 */
38 void *value; /* 属性值 */
39 struct property *next; /* 下一个属性 */
40 unsigned long _flags;
41 unsigned int unique_id;
42 struct bin_attribute attr;
43 };

of_find_property 函数

of_find_property 函数用于查找指定的属性,函数原型如下:

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

函数参数和返回值含义如下:
np:设备节点。
name: 属性名字。
lenp:属性值的字节数
返回值:找到的属性。

of_property_count_elems_of_size 函数

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 函数

of_property_read_u32_index 函数用于从属性中获取指定标号的 u32 类型数据值(无符号 32位),比如某个属性有多个 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_u8_array 函数

of_property_read_u16_array 函数

of_property_read_u32_array 函数

of_property_read_u64_array 函数

这 4 个函数分别是读取属性中 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,

函数参数和返回值含义如下:
np:设备节点。
proname: 要读取的属性名字。
out_value:读取到的数组值,分别为 u8、u16、u32 和 u64。
sz:要读取的数组元素数量。
返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。

of_property_read_u8函数

of_property_read_u16函数

of_property_read_u32函数

of_property_read_u64函数

有些属性只有一个整形值,这四个函数就是用于读取这种只有一个整形值的属性,分别用于读取 u8、u16、u32 和 u64 类型属性值,函数原型如下:

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 函数

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 函数

of_n_addr_cells 函数用于获取#address-cells 属性值,函数原型如下:

int of_n_addr_cells(struct device_node *np)

函数参数和返回值含义如下:
np:设备节点。
返回值:获取到的#address-cells 属性值。

of_n_size_cells 函数

of_size_cells 函数用于获取#size-cells 属性值,函数原型如下:

int of_n_size_cells(struct device_node *np)

函数参数和返回值含义如下:
np:设备节点。
返回值:获取到的#size-cells 属性值。

  • 21
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值