一、DTS的来源
在linux内核源码的3.1版本之前,linux内核都是通过大量的platfrom-device文件来描述板级配置信息,这使得内核人员维护很困难,因此设备树(Device Tree)被采用
二、DTS的介绍
DTS可以看成一个描述板级硬件信息的树状结构,这棵树上有多个枝干,每一个树干都可以表示一个板级子设备信息
设备树文件目录一般位于kernel/arch/arm64/boot/dts/下,该目录含有多个厂商的DTS信息
DTS:DTS即Device Tree Source,是一个文本形式的文件,用于描述硬件信息。一般都是硬件的固定信息部分,不需要修改
DTSI:SoC公用的部分或者多个设备共同的部分一般提炼为.dtsi,类似于C语言头文件
DTB:dts经过dtc编译之后会得到dtb文件,dtb通过Bootloader引导程序加载到内核
三、DTS的介绍
下面是一个设备树文件的基本结构
/{ #表示设备树根节点
node1@address{ #node1表述节点名,address表示节点对应的寄存器地址
byte-property=<0xFF>;
string-property = "string";
string-list-property="string one","string two";
child-node{
child-byte-property=<0xFF>;
child-string-property = "child-string";
}
}
node2:node2@address{ #这里node2表示node2@address的别名
byte-property=<0xFF>;
string-property = "string";
string-list-property="string one","string two";
}
}
四、DTS的常用属性
compatible:用于驱动模块和节点硬件信息的匹配
reg:表示i2s模块的寄存器地址从oxff000000起始,长度为0x1000
interrupts:表示i2s模块上使用的中断类型和中断号
clocks:使用的时钟
clocks-name:时钟名称
dmas:使用的dma通道号
status:okay表示设备正常运行;disabled设备不可操作,但是后面可能恢复工做;fail发生了严重错误;fail-sss发生了严重错误,sss表示错误信息
五、DTS的资源获取的相关函数
struct device_node定义
/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;
struct device_node *parent; //父节点
struct device_node *child; //子节点
struct device_node *sibling;
#if defined(CONFIG_OF_KOBJ)
struct kobject kobj;
#endif
unsigned long _flags;
void *data;
#if defined(CONFIG_SPARC)
const char *path_component_name;
unsigned int unique_id;
struct of_irq_controller *irq_trans;
#endif
};
1、struct device_node *of_find_node_by_path(struct device_node *from,const char *path);
说明:根据路径找到节点
from:开始查找的节点,NULL表示从根节点开始查找
path:查找的节点名
返回值:
成功:device_node表示的节点
失败:NULL
2、struct device_node of_find_compatible_node(struct device_node *from,const char *type, const char *compat);
说明:根据compatible查找节点
from:开始查找的节点,NULL表示从根节点开始查找
type:指定 device_type 属性值
compat:指定 compatible 属性值
返回值:
成功:device_node表示的节点
失败:NULL
3、struct property *of_find_property(const struct device_node *np,const char *name,int *lenp);
说明:查找节点中的属性
np:device_node表示的节点
name:查找的属性名字
lenp:属性值的字节数
返回值:
成功:property表示的属性
失败:NULL
4、static inline int of_property_read_u32(const struct device_node *np,const char *propname,u32 *out_value);
说明: 读取节点中一个32位无符号整数
np:device_node表示的节点
propname:查找的属性名字
out_value:属性值的整数值
返回值:
成功:0
失败:负值
5、int of_property_read_u32_array(const struct device_node *np,const char *propname,u32 *out_values,size_t sz);
说明: 从节点中读取32位无符号整数数组
np:device_node表示的节点
name:查找的属性名字
out_value:读取到的数组值
sz :要读取的数组元素数量
返回值:
成功:0
失败:负值
6、int of_property_read_string(struct device_node *np,const char *propname,const char **out_string);
说明:从节点中读取字符串
np:device_node表示的节点
propname:查找的属性名字
out_string:读取到的数组值
返回值:
成功:0
失败:负值
六、dts-node被转换为platform_device的步骤
编译内核时通过DTC工具将DTS编译成二进制文件*.dtb,dtb结构由一个小的报头和三个大小可变的部分组成:内存预留块,结构块和字符串块,当以地址载入内存时,将类似于下图(较低的地址位于图的顶部):
dtb->platfrom_device的规则如下:
1.该device_node 必须含有 compatible属性。
2.该device_node 是根节点的子节点(必须有compatible属性)。
3.如果孙子节点或者孙孙子节点也想要转换成platfrom_device,则它的父节点device_node的compatible属性必含有如下特殊字符串:“simple-bus” ,“simple-mfd”,“isa”,“arm,amba-bus”.(只要compatilbe中含有其中任一即可,没有则被转换为对应框架的抽象设备,如struct i2c client)
dtb->device_node解析的流程如下:
setup_arch(&command_line) ->
//__atags_pointer为uboot传递给kernel的dtb文件的内存地址
setup_machine_fdt(__atags_pointer)->
dt_virt = fixmap_remap_fdt(dt_phys)
early_init_dt_scan(dt_virt) ->
//校验dtb和解析部分节点属性值
early_init_dt_verify(params)
//继续解析剩余节点和属性值
unflatten_device_tree()->
//数据保存到struct device_node结构中,并赋值给of_root(根节点),至此,dts文件在内内核中的解析完成
__unflatten_device_tree(initial_boot_params, &of_root,
early_init_dt_alloc_memory_arch)->
unflatten_dt_node
dtb->device_node解析的流程图如下:
device_node->platfrom_device的流程如下:
如果从start_kernel进行追溯,是找不到node->platform device的转换源头,(arm64)这个转换过程是由arch_initcall_sync(arm64_device_init)开始,调用 of_platform_populate开启
static int __init arm64_device_init(void)
{
...
of_platform_populate(NULL, of_default_bus_match_table,NULL, NULL);
...
}
arch_initcall_sync(arm64_device_init);
下面进入到of_platform_populate中,查看node->platfrom_device的转换过程:
of_platform_populate(NULL, of_default_bus_match_table,NULL, NULL)->
for_each_child_of_node(root, child)
of_platform_bus_create(child, matches, lookup, parent, true) ->
//据节点创建出对应的platform_device
of_platform_device_create_pdata(bus, bus_id, platform_data, parent)>
//为device匹配driver
bus_probe_device->device_initial_probe->__device_attach->
__device_attach_driver->driver_probe_device->really_probe->
drv->probe(dev)
device_node->platfrom_device的流程图如下: