前言
设备树起源?
device tree 来源于openfirmware,内核中关于设备树的函数都是以of开头。
为什么要用设备树?
- 减少储存空间,减少驱动的冗余编码。
- 实现一套内核对应多套板级设备。
环境:设备树增加usb3503A、网卡、nfs的支持
编译:设备树被编译后的二进制文件传到内核后,内核有专门的函数解析,处理。
语法
遇到括号就是节点,括号里面就是节点的属性
节点和根节点
{...} 框起来的结构,称为节点;
/{...} 在dts中最开头的,称为根节点。
节点的标准结构是xxx@yyy{…},
xxx是节点的名字,yyy则不是必须的,其值为节点的地址(寄存器地址或其他地址)。
节点可以包含属性和子节点,其中子节点也是用{}包围起来的。设备树的节点,可以包含,互相也可以引用--看到“&”,表示做了引用(也可以理解面向对象编程的重写);
属性
设备树学习的主要部分:设备树文件中属性的配置,驱动文件中调用设备树中的属性。
compatible--类似设备名称,
reg,label,gpios,pwms,status等等
设备树套路
带大家看下官方的设备树介绍文档
(Documentation/devicetree/usage-model.txt 106行)
设备树的三个主要目标:
1) platform identification,
2) runtime configuration, and
3) device population.
platform identification--确认平台
通过根节点的compatible属性
例如:
compatible = "ti,omap3-beagleboard", "ti,omap3450", "ti,omap3";
compatible = "ti,omap3-beagleboard-xm", "ti,omap3450", "ti,omap3";
4412的例子:
compatible = "topeet,itop4412-elite", "samsung,exynos4412", "samsung,exynos4";
runtime configuration--运行环境配置
大多数通过chosen节点
例如:
chosen {
bootargs = "console=ttyS0,115200 loglevel=8";
initrd-start = <0xc8000000>;
initrd-end = <0xc8200000>;
};
4412的例子:
chosen {
bootargs = "root=/dev/mmcblk0p2 rw rootfstype=ext4 rootdelay=1 rootwait";
stdout-path = "serial2:115200n8";
};
开发板启动之后,通过proc和sys文件系统可以查询到设备树的信息。在/proc/device-tree目录下有所有的node的信息;这个目录下能看到表明有相应的节点。在/sys/devices/platform/目录下有所有设备node的信息。在这个目录下能看到表明设备注册了。这里的信息很真要,因为有设备注册,最后驱动才能进入probe()函数!
增加节点和测试代码
在设备树中增加节点信息:
leds_test_node:leds_test_node {
compatible = "leds_test";
status = "disabled";
// status = "okay";
};
根节点之后 &adc之前增加
&leds_test_node {
status = "okay"; //可以理解为状态使能
};
在驱动中增加代码:
#define DRIVER_NAME "leds_test"
...
static const struct of_device_id of_leds_dt_match[] = {
{.compatible = DRIVER_NAME},
{},
};
MODULE_DEVICE_TABLE(of,of_leds_dt_match);
static struct platform_driver leds_driver = {
.probe = leds_probe,
.remove = leds_remove,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = of_leds_dt_match,
},
};
...
匹配的关键是of_leds_dt_match中的compatible和设备树中的compatible相同,
如果相同驱动就会进入probe。
获取属性
Linux引入设备树,把硬件分到设备树文件中,驱动中仍然需要硬件信息。
所以必须将设备树中的硬件信息传到驱动中,
设备树最重要的结构体
设备树属性获取函数头文件: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;
...
};
设备树中使用 device_node 结构体描述节点
驱动中获取device_node方法: struct device_node *node = pdev->dev.of_node;
根据属性名称,提取属性值,如果不需要lenp可以赋值为NULL
struct property *of_find_property(const struct device_node *np, const char *name, int *lenp);