linux设备驱动(21)设备树详解5-dts的应用

对于任何的知识来说,了解了理论的知识,知道了设备树怎么解析用以代替传统的范式之后,我们需要知道怎么使用设备树。对于使用我们分两部分,一部分是它有哪些接口,能做些什么,至于怎么编写dts文件本章不讨论。主要包括两部分:

(1)对于设备树,编译和设备启动后,怎么来查看设备树的信息,怎么用来debug

(2)设备树的操作函数提供了哪些接口,基本的方法有哪些

1 文件系统下设备树

一部分是出现问题后,怎么用来debug,对于内核来说一切皆是文件的思想,设备树与文件系统的关系,在Linux系统起来后,会将解析完成的设备树导出到用户空间。
kernel启动在of_init()函数中在sys/firmware/devicetree/base目录下面为设备树展开成sysfs的目录和二进制属性文件,所有的node节点就是一个目录,所有的property属性就是一个二进制属性文件。

 1 static int __init of_init(void)
 2 {
 3     struct device_node *np;
 4 
 5     /* Create the kset, and register existing nodes */
 6     mutex_lock(&of_mutex);
 7     of_kset = kset_create_and_add("devicetree", NULL, firmware_kobj);
 8     if (!of_kset) {
 9         mutex_unlock(&of_mutex);
10         return -ENOMEM;
11     }
12     for_each_of_allnodes(np)
13         __of_attach_node_sysfs(np);
14     mutex_unlock(&of_mutex);
15 
16     /* Symlink in /proc as required by userspace ABI */
17     if (of_allnodes)
18         proc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base");
19 
20     return 0;
21 }
22 core_initcall(of_init);

该函数主要完成下面三件事情:

(1)创建/sys/firmware/devicetree/目录
(2)遍历所有的设备树节点,并且在./sys/firmware/devicetree/目录下创建对应的sysfs,以目录结构程现的dtb文件, 根节点对应base目录, 每一个节点对应一个目录, 每一个属性对应一个文件
(3)在/proc/device-tree文件是软连接,软连接到/sys/firmware/devicetree/base 。

通过kernel_path/drivers/of/Kconfig 中的config:PROC_DEVICETREE  ,重新编译内核,系统就会生成文件 /proc/device-tree 。

2 设备树的操作函数

学习完了设备树的文件系统查看方法,那么对于在源码中去拿到设备树的节点或者属性信息,内核提供了那些操作函数呢?内核中开放出来的接口函数的声明大多在include/linux/下面,关于设备树的都是以of形式开头的命名:

 

对应各个文件主要完成什么功能如下:

文件名 功能描述
of.h :提供设备树的一般处理函数,大部分的功能函数都在该文件中
of_address.h :地址相关的函数, 比如 of_get_address(获得reg属性中的addr, size值)
of_device.h :设备相关的函数, 比如 of_match_device、of_device_register
of_dma.h :设备树中DMA相关属性的函数
of_fdt.h dtb:文件的相关操作函数,我们一般不使用
of_gpio.h :GPIO相关的函数
of_graph.h: GPU相关驱动中用到的函数, 从设备树中获得GPU信息
of_iommu.h: IOMMU相关函数
of_irq.h :中断相关的函数
of_mdio.h: mdio相关的函数
of_mtd.h: mtd相关的函数
of_net.h: net相关的函数
of_pci.h :pci相关的函数
of_platform.h :把device_node转换为platform_device时用到的函数
of_reserved_mem.h :reserved_mem的相关函数

定义位于:drivers/of目录下。
2.1 查找节点

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

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

使用方法可以如下

np = of_find_compatible_node(NULL, NULL, “fsl,imx23-digctl”);
digctrl = of_iomap(np, 0);

 dts中对应如下:

1 digctl: digctl@8001c000 {
2     compatible = "fsl,imx28-digctl", "fsl,imx23-digctl";
3     reg = <0x8001c000 0x2000>;
4     interrupts = <89>;
5     status = "disabled";
6 };

通过节点名查找指定节点

 1 static const struct of_device_id exynos_dt_mcpm_match[] = {
 2     { .compatible = "samsung,exynos5420" },
 3     { .compatible = "samsung,exynos5800" },
 4     {},
 5 };
 6 
 7     node = of_find_matching_node(NULL, exynos_dt_mcpm_match);
 8     if (!node)
 9         return -ENODEV;
10     of_node_put(node);

通过路径查找指定节点

struct device_node *of_find_node_by_path(const char *path);

/* 参数:    const char *path - 带全路径的节点名,也可以是节点的别名 */

data->current_node = of_find_node_by_path("/");

通过节点名查找指定节点

 1 sscg_np = of_find_node_by_name(NULL, "sscg");
 2     if (sscg_np == NULL) {
 3         pr_err("cannot get SSCG register node\n");
 4         return system_clk;
 5     }
 6 
 7     sscg_map = of_iomap(sscg_np, 0);
 8     if (sscg_map == NULL) {
 9         pr_err("cannot map SSCG register\n");
10         goto out;
11     }

 2.2 读取属性

1 int of_property_read_u8_array(const struct device_node *np,
2 const char *propname, u8 *out_values, size_t sz);
3 int of_property_read_u16_array(const struct device_node *np,
4 const char *propname, u16 *out_values, size_t sz);
5 int of_property_read_u32_array(const struct device_node *np,
6 const char *propname, u32 *out_values, size_t sz);
7 int of_property_read_u64(const struct device_node *np,
8                               const char*propname, u64 *out_value);

读取设备节点np的属性名,为propname,属性类型为8、16、32、64位整型数组。对于32位处理器来讲,最常用的是of_property_read_u32_array()。

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

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

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

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

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

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

2.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参数指向的结构体中。

2.4 解析中断

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

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

2.5 获取与节点对应的platform device

拿到device_node的情况下,使用以下API获取对应的platform_device

struct platform_device *of_find_device_by_node(struct device_node *np);

 当然,在已知platform_device的情况下,也可获取device_node

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

参考博文:

https://blog.csdn.net/u012489236/article/details/97395748

https://blog.csdn.net/baidu_38661691/article/details/97013406

posted @ 2020-05-28 19:28  Action_er  阅读(1622)  评论(0编辑  收藏  举报