下面是自己关于设备树的一些理解,如果有错误,后面在进行修改。
一、操作系统在启动器件对设备树的处理
设备树分为两层,pinctl端和client端,在uboot引导kernel启动之后,kernel启动的时候就会去去读设备树,获取其中的相关信息储存起来,实际的情况如下所示。
上面的两个图是衔接的,只是图太大,所以分开进行截图了。
上面的两个图也是衔接的,图太大了,就分开截取。
二、开发板目录查看对应的pinctl、group、pin、复用情况等。
如上图目录所示,在“/sys/kernel/debug/pinctrl”目录下可以看到生成的连个pinctl:“20e0000.iomuxc”、“2290000.iomuxc-snvs”,绿色框里的设备使用如下图所示:
pins | 单个引脚信息 |
pingroups | 引脚的组信息 |
pinmux-pins | 单个引脚的复用信息 |
Pinctrl的虚拟文件 | 作用 |
pinmux-functions | function下的group(支持该function的group) |
pinconf-pins | 单个引脚的配置 |
pinconf-groups | 引脚组的配置 |
pinconf-config | 可以通过写它修改指定设备、指定状态下、指定(组)引脚的config值 |
使用上面的三个文件,就可以查看到不同的设备信息。
(1)进入“20e0000.iomuxc”或者“2290000.iomuxc-snvs”目录,可以看到下图所示的目录,可以使用“cat”命令打印其中的某些目录,就可以看到group、pin等信息。
(2)使用“”命令打印引脚的复用信息
从上面的图中就可以看到设备树中设置的pin引脚是否复用。
(3)下面三个文件是查看对应的pin状态的指令,具体的参考韦东山老师笔记。
(4)对设备树的信息和结构体的pin配置信息的关系
利用设备树中的信息,生成pinctl需要的结构体配置信息的方法有两种。
第一种:
获取设备树中的信息,对应pinctl中pin配置的结构体信息在内核驱动中已经写死,只是根绝设备树中的配置进行选择,然后根据在设备树中获取pin引脚配置的参数,对相应的引脚设置。
第二种:
通过在获取设备树中pin的配置信息,然后根据获取的pin信息,对pin信息存储的结构体进行创建,然后根绝设备树中对应的pin引脚配置参数,进行引脚的配置。
三、设备树编辑注意事项
如下图所示,“pinctrl_hog_1”名称与“:”冒号之间是不能有空格的,如果有空格就会报错。
四、设备树的基础知识
(1)设备树文件可以包含“NAME.dtsi”头文件,也可以包含其他c文件的头文件“dt-bindings/input/input.h”,这种c文件的头文件包含可能是因为要用里面的宏定义,设备树头文件也就是后缀为“.dtsi”的文件的包含作用,一般里面都是关于芯片的统一的内容,因为同一家芯片厂商的芯片的参数内容都是一样的,只是不同的芯片参数的数值不同而已,所以将这些相同的芯片参数封装为一个“.dtsi”的头文件。
(2)设备树结点定义,“标签: 结点名@寄存器首地址{}”,在设备树头文件“imx6ull.dtsi”里面定义的结点,可以继续进行内容的追加,格式如下图所示,自己添加的结点一般放在设备树的最后面。
五、设备树中两个特殊结点
两个特殊结点分别为“chosen”和“aliases”。
(1)aliases(翻译:别名)结点
aliases 节点的主要功能就是定义别名,定义别名的目的就是为了方便访问节点。不过我们一般会在节点命名的时候会加上 label,然后通过&label来访问节点,这样也很方便,而且设备树里面大量的使用&label 的形式来访问节点。
(2)chosen结点
详细细节参考正点原子imx6ull指南手册。
六、结点的特殊属性
(1)地址格式指定,手地址的格式,地址长度格式,这里都是指定一个参数。
address-cells = <1>;
size-cells = <1>;
(2)地址参数指定属性,指定了首地址和地址的长度。注意他的属性是由父节点里面设置的指定的,子节点中指定的影响他的子节点。
reg = <0x02008000 0x4000>;
其他属性参考正点手册
七、设备结点的of获得函数
这个函数在写驱动的时候,使用的还是比较多的,of函数的头文件在自己的乌班图中的路径为:“./Linux-4.9.88/include/linux/of.h”。下面将介绍一下常用的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 };
(1)查找设设备树中结点的函数
(2)查找父/子类结点的OF函数
(3)提取属性值property结构体的of函数
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 };
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,
size_t sz)
/**
* of_get_parent - Get a node's parent if any
* @node: Node to get parent
*
* Returns a node pointer with refcount incremented, use
* of_node_put() on it when done.
*/
struct device_node *of_get_parent(const struct device_node *node)
{
struct device_node *np;
unsigned long flags;
if (!node)
return NULL;
raw_spin_lock_irqsave(&devtree_lock, flags);
np = of_node_get(node->parent);
raw_spin_unlock_irqrestore(&devtree_lock, flags);
return np;
}
EXPORT_SYMBOL(of_get_parent);
//得到设备树中的gpio结点,通过设备结点中的name
static inline int of_get_named_gpio(struct device_node *np,
const char *propname, int index)
{
return of_get_named_gpio_flags(np, propname, index, NULL);
}