设备树知识小全(十一):OF是什么?

简介: 设备树知识小全(十一):OF是什么?

除了前文介绍的of_machine_is_compatible()、of_device_is_compatible()等常用函数以外,在Linux的BSP和驱动代码中,经常会使用到一些Linux中其他设备树的API,这些API通常被冠以of_前缀,它们的实现代码位于内核的drivers/of目录下。这些常用的API包括下面内容。

1.寻找节点

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

根据兼容属性,获得设备节点。

遍历设备树中的设备节点,看看哪个节点的类型、兼容属性与本函数的输入参数匹配,在大多数情况下,from、type为NULL,则表示遍历了所有节点。

2.读取属性

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(const struct device_node *np, const char
*propname, u64 *out_value);

读取设备节点np的属性名,为propname,属性类型为8、16、32、64位整型数组。

对于32位处理器来讲,最常用的是of_property_read_u32_array()。

如在arch/arm/mm/cache-l2x0.c中,通过如下语句可读取L2cache的"arm,data-latency"属性:

of_property_read_u32_array(np, "arm,data-latency",data, ARRAY_SIZE(data));

在arch/arm/boot/dts/vexpress-v2p-ca9.dts中,对应的含有"arm,data-latency"属性的L2cache节点如下:

L2: cache-controller@1e00a000 {
compatible = "arm,pl310-cache";
reg = <0x1e00a000 0x1000>;
interrupts = <0 43 4>;
cache-level = <2>;
arm,data-latency = <1 1 1>;
arm,tag-latency = <1 1 1>;
}

在有些情况下,整型属性的长度可能为1,于是内核为了方便调用者,又在上述API的基础上封装出了更加简单的读单一整形属性的API,它们为int of_property_read_u8()、of_property_read_u16()等,实现于include/linux/of.h中,如代码清单18.19所示。

1 static inline int of_property_read_u8(const struct device_node *np,
 2                                        const char *propname,
 3                                        u8 *out_value)
 4 {
 5         return of_property_read_u8_array(np, propname, out_value, 1);
 6 }
 7
 8 static inline int of_property_read_u16(const struct device_node *np,
 9                                        const char *propname,
10                                        u16 *out_value)
11{
12         return of_property_read_u16_array(np, propname, out_value, 1);
13}
14
15 static inline int of_property_read_u32(const struct device_node *np,
16                                        const char *propname,
17                                        u32 *out_value)
18 {
19         return of_property_read_u32_array(np, propname, out_value, 1);
20 }

除了整型属性外,字符串属性也比较常用,其对应的API包括:

int of_property_read_string(struct device_node *np, const char *propname,const char **out_string);
int of_property_read_string_index(struct device_node *np, const char      *propname,int index, const char **output);

前者读取字符串属性后者读取字符串数组属性中的第index个字符串。

如drivers/clk/clk.c中的of_clk_get_parent_name()函数就通过of_property_read_string_index()遍历clkspec节点的所有"clock-output-names"字符串数组属性。

1 const char *of_clk_get_parent_name(struct device_node *np, int index)
 2 {
 3          struct of_phandle_args clkspec;
 4          const char *clk_name;
 5          int rc;
 6
 7          if (index < 0)
 8                  return NULL;
 9
10          rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index,
11                                         &clkspec);
12          if (rc)
13                  return NULL;
14
15          if (of_property_read_string_index(clkspec.np, "clock-output-names",
16                                    clkspec.args_count  clkspec.args[0] : 0,
17                                            &clk_name) < 0)
18                  clk_name = clkspec.np->name;
19
20          of_node_put(clkspec.np);
21          return clk_name;
22 }
23 EXPORT_SYMBOL_GPL(of_clk_get_parent_name);

除整型、字符串以外的最常用属性类型就是布尔型,其对应的API很简单,具体如下

static inline bool of_property_read_bool(const struct device_node *np,
const char *propname);

如果设备节点np含有propname属性,则返回true,否则返回false。一般用于检查空属性是否存在。

3.内存映射

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

上述API可以直接通过设备节点进行设备内存区间的ioremap(),index是内存段的索引。

若设备节点的reg属性有多段,可通过index标示要ioremap()的是哪一段,在只有1段的情况,index为0。

采用设备树后,一些设备驱动通过of_iomap()而不再通过传统的ioremap()进行映射,当然,传统的ioremap()的用户也不少。

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

上述API通过设备节点获取与它对应的内存资源的resource结构体。

其本质是分析reg属性以获取内存基地址、大小等信息并填充到struct resource*r参数指向的结构体中。

4.解析中断

unsigned int irq_of_parse_and_map(struct device_node *dev, int index);

通过设备树获得设备的中断号,实际上是从.dts中的interrupts属性里解析出中断号。若设备使用了多个中断,index指定中断的索引号。

5.获取与节点对应的platform_device

struct platform_device *of_find_device_by_node(struct device_node *np);

在可以拿到device_node的情况下,如果想反向获取对应的platform_device,可使用上述API。

当然,在已知platform_device的情况下,想获取device_node则易如反掌,例如:

static int sirfsoc_dma_probe(struct platform_device *op)
{
    struct device_node *dn = op->dev.of_node;
}

[一个现实的Linux设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCI、USB、I2C、SPI等的设备而言,这自然不是问题,但是在嵌入式系统里面,**在SoC系统中集成的独立外设控制器、挂接在SoC内存空间的外设等却不依附于此类总线。**基于这一背景,Linux发明了一种虚拟的总线,称为platform总线,相应的设备称为platform_device,而驱动成为platform_driver。]

6、打个总结

充斥着ARM社区的大量垃圾代码导致Linus盛怒,因此该社区在2011~2012年进行了大量的修整工作。

ARM Linux开始围绕设备树展开,设备树有自己的独立语法,它的源文件为.dts,编译后得到.dtb,Bootloader在引导Linux内核的时候会将.dtb地址告知内核。(通过dtc)

(这也是有的会有个dtb镜像)

之后内核会展开设备树并创建和注册相关的设备,因此arch/arm/mach-xxx和arch/arm/plat-xxx中的大量用于注册platform、I2C、SPI等板级信息的代码被删除,而驱动也以新的方式与在.dts中定义的设备节点进行匹配。

设备树就到这里,其实很难都一次性掌握好,需要不断地实践应用才会变成长时记忆,学习的路还很长。

到这里就差不多关于设备树的东西,没想到写了这么多,感恩宋老师的书籍。

参考书籍:

《Linux设备驱动开发详解》

目录
相关文章
|
7月前
|
Linux
设备树知识小全(一)
设备树知识小全(一)
73 0
|
4月前
|
存储 缓存 算法
USB3.2 摘录(一)(下)
USB3.2 摘录(一)
51 12
|
4月前
USB3.2 摘录(五)(下)
USB3.2 摘录(五)
43 1
|
4月前
|
机器学习/深度学习 流计算
USB3.2 摘录(五)(上)
USB3.2 摘录(五)
67 1
|
4月前
|
缓存
USB3.2 摘录(一)(上)
USB3.2 摘录(一)
58 0
USB3.2 摘录(一)(上)
|
4月前
|
数据格式 数据库管理
USB3.2 摘录(三)(上)
USB3.2 摘录(三)
43 0
|
4月前
USB3.2 摘录(三)(下)
USB3.2 摘录(三)
54 0
|
7月前
|
Ubuntu Linux 程序员
设备树知识小全(三)
设备树知识小全(三)
136 0
|
7月前
|
Linux C语言 SoC
设备树知识小全(二)
设备树知识小全(二)
110 0
|
7月前
|
Linux
设备树知识小全(四)
设备树知识小全(四)
62 0