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