make xxxx.dts
由DTC工具,编译dts源文件,生成.dtb二进制文件。系统在启动时会根据dtb设备树文件。
(这个问题是面试着玩的时候被面试官问到的,当时没反应过来,看来板子还得多折腾,实在惭愧,板子一大堆,全部都在吃灰)
设备树是怎么体现实现?
在linux启动时,会加载解析.dtb文件。生成设备树文件目录
在根文件系统中体现,系统启动以后,可以在根文件系统里面看到设备树得节点信息,/proc/device_tree/里面体现
node节点都是以文件夹体现,节点下面得子节点、设备节点均是以文件夹存在。最终会包含其中得属性
属性是以文件形式体现(这应该是废话了,linux下一切皆文件)
设备树常用语法
设备树可以包含头文件,来包含一些原厂做的宏定义,设备树基础信息等。通常我们在开发时,通过参考dtsi文件和相关的头文件,来对xxx.dts(自己的设备树)进行响应的修改,内容追加等。
相同得节点由追加、继承的作用。如果由相同的属性名则是会最后的为准,覆盖前面的。
#include "xxx.dtsi"
#include "input.h"
compatible 属性
compatible 属性也叫做“兼容性”属性,这是非常重要的一个属性!compatible 属性的值是一个字符串列表,compatible 属性用于将设备和驱动绑定起来。字符串列表用于选择设备所要使用的驱动程序。
例如:欧胜(WOLFSON)出品的 WM8960的属性值如下
compatible = "fsl,imx6ul-evk-wm8960","fsl,imx-audio-wm8960";
model 属性
model 属性值也是一个字符串,一般 model 属性描述设备模块信息,比如名字什么的,比
如:
model = "wm8960-audio";
status 属性
status 属性是和设备状态有关的,status 属性值也是字符串,字符串是设备的状态信息。
#address-cells 和#size-cells 属性
#address-cells 和#size-cells 表明了子节点应该如何编写 reg 属性值
reg = <address1 length1 address2 length2 address3 length3……>
ranges 属性
该属性的格式如下,可以为空也可以子地址、父地址、长度表示。
ranges = <child-bus-address, parent-bus-address, length>;
ranges ;
soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0x0 0xe0000000 0x00100000>;
serial {
device_type = "serial";
compatible = "ns16550";
reg = <0x4600 0x100>;
clock-frequency = <0>;
interrupts = <0xA 0x8>;
interrupt-parent = <&ipic>;
};
};
soc 定义的 ranges 属性,值为<0x0 0xe0000000 0x00100000>,此属性值指定
了一个 1024KB(0x00100000)的地址范围,子地址空间的物理起始地址为 0x0,父地址空间的物
理起始地址为 0xe0000000。
serial 是串口设备节点,reg 属性定义了 serial 设备寄存器的起始地址为 0x4600,
寄存器长度为 0x100。经过地址转换,serial 设备可以从 0xe0004600 开始进行读写操作,
0xe0004600=0x4600+0xe0000000。
OF函数
of函数可以简单理解为获取设备树的相关资源信息。这样就实现了设备资源通过修改设备树,进行通过驱动获取对应的资源的方式。
大部分函数原型可以在"include/linux/of.h"文件中找到对应的API
查找获取节点
此处用的最多的应该是:inline struct device_node *of_find_node_by_path(const char *path),设备树的枝干改变了,此处的路径也需要进行响应的修改。
/*
path:带有全路径的节点名,可以使用节点的别名,比如“/backlight”就是 backlight 这个
节点的全路径。
返回值:找到的节点,如果为 NULL 表示查找失败
*/
inline struct device_node *of_find_node_by_path(const char *path)
/* of_device_id 匹配表来查找指定的节点 */
struct device_node *of_find_matching_node_and_match(struct device_node *from,
const struct of_device_id *matches,
const struct of_device_id **match)
/* 通过节点名字查找指定的节点*/
struct device_node *of_find_node_by_name(struct device_node *from,
const char *name);
/* 根据 device_type 和 compatible 这两个属性查找指定的节点 */
struct device_node *of_find_compatible_node(struct device_node *from,
const char *type,
const char *compatible)
提取节点属性
函数并未列举完全,只是列出几个常见的。
/* 查找指定的属性 */
property *of_find_property(const struct device_node *np,
const char *name,
int *lenp)
/* 读取属性中 u8、u16、u32 和 u64 类型的数组数据,比如大多数的 reg 属性都是数组数据,可以使用这 4 个函数一次读取出 reg 属性中的所有数据 */
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)
/* #address-cells 属性值 #size-cells 属性值 */
int of_n_addr_cells(struct device_node *np)
int of_n_size_cells(struct device_node *np)
of_iomap
of_iomap 函数用于直接内存映射
void __iomem *of_iomap(struct device_node *np, int index)
/*
my_led {
#address-cells = <1>;
#size-cells = <1>;
compatible = "my-led";
status = "okay";
reg = < 0X020C406C 0X04 /* CCM_CCGR1_BAE */
0X020E0068 0X04 /* SW_MUX_GPIO1_IO03_BASE */
0X020E02F4 0X04 /* SW_PAD_GPIO1_IO03_BASE */
0X0209C000 0X04 /* GPIO1_DR_BASE */
0X0209C004 0X04>; /* GPIO1_GDIR_BASE */
};
*/
IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0);
SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd, 1);
SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd, 2);
GPIO1_DR = of_iomap(dtsled.nd, 3);
GPIO1_GDIR = of_iomap(dtsled.nd, 4);