设备树的指定
关于内核怎么知道设备树的,可以参考我以前写的文章U-boot&设备树&Linux。
现在我们假设已经内核已经匹配到了设备树的镜像。下面我们来分析,设备树怎么转化为平台设备的。
首先我们知道设备树dtb文件仅仅只是一个平面的二进制文件。首先需要将其转化为一棵树,这个树上挂满了一个个的设备节点。
dtb文件转化为device_node
init\main.c
start_kernel
setup_arch(&command_line);
/* Register the kernel text, kernel data and initrd with memblock. */
arm_memblock_init(mdesc); //arch\arm\kernel\devtree.c
/*保留设备树的这一块内存*/
early_init_fdt_reserve_self();//drivers\of\fdt.c
/*保留设备树自身的这一块区域*/
# 使得系统运行中可以随时访问这一块区域
early_init_dt_reserve_memory_arch(__pa(initial_boot_params),
fdt_totalsize(initial_boot_params),0);
# 根据dtb中的memreserver中的信息调用memblock_reserve
early_init_fdt_scan_reserved_mem();
# create tree of device_nodes from flat blob
# 在这里创建 device_nodes
# dtb 里面包含了各个设备节点,现在要将他设置为一棵树
#
unflatten_device_tree();
# early_init_dt_alloc_memory_arch 是一个回调函数
__unflatten_device_tree(initial_boot_params, NULL, &of_root,
early_init_dt_alloc_memory_arch, false);
# 获取大小
size = unflatten_dt_nodes(blob, NULL, dad, NULL);
/* Allocate memory for the expanded device tree */
# 分配空间,
# 但是为什么这里还需要加上4了?是为了加上魔数占用的空间
mem = dt_alloc(size + 4, __alignof__(struct device_node));
# 填充对应的魔数
#
*(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef);
/* Second pass, do actual unflattening */
# 在这里就是真正的处理设备树节点了
unflatten_dt_nodes(blob, mem, dad, mynodes);
# 遍历每一个节点,填充struct device_node 结构体
unflatten_dt_nodes
# 遍历每一个节点,然后调用下面的函数,
# 构造 struct device_node 以及 struct property 的属性
populate_node(blob, offset, &mem,
nps[depth],
fpsizes[depth],
&nps[depth+1], dryrun);
# 构造每一个device_node 节点,填充 struct device_node 结构体
populate_node
# 获取节点 name属性
pathp = fdt_get_name(blob, offset, &l);
# 分配空间 sizeof(struct device_node) + allocl
# allocl 大小是 fpsize + l
np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,
__alignof__(struct device_node));
# 现在这里可以看到 struct device_node 是怎么填充的了
np->full_name = fn = ((char *)np) + sizeof(*np);
np->name = of_get_property(np, "name", NULL);
np->type = of_get_property(np, "device_type", NULL);
上面我们知道dtb是怎么转化为设备节点的了。
下面我们来分析设备节点怎么转化为平台设备。
设备节点转化为平台设备
在这一段,我想要理清下面两个问题。
1. 那些device_node可以转化成为platform_device?
2. 转化这个过程是怎样的?
那些设备节点可以转化为平台设备
节点必须含有 compatible 属性(不包含根节点)
2. 属于根节点的子节点(必须含有compatible 属性)
I2C以及SPI等子节点应该交给对应的总线驱动来处理,不应该被转化为platform_device。转化过程
struct platform_device
struct platform_device {
const char *name;
int id;
bool id_auto;
# struct device 结构体里面 包含了一个
# struct device_node *of_node;
# 这个结构体里面都是包含了读取设备树节点的属性值
struct device dev;
u32 num_resources; # resource 数组的长度
# 资源包含了 IO 资源,内存资源。中断资源, 这些资源都是从device node里面得到
struct resource *resource; # 指向了一个数组,这些资源来自设备树中的reg属性
const struct platform_device_id *id_entry;
char *driver_override; /* Driver name to force a match */
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
of_platform_default_populate_init
关于平台设备的初始化,是调用的这个函数来初始化平台设备。此函数使用了函数arch_initcall_sync修饰的。 关于这函数怎么被调用无需多言。 ```c of_platform_default_populate_init / Populate everything else. / of_platform_default_populate(NULL, NULL, NULL); of_platform_populate(root, of_default_bus_match_table, lookup,
parent);
for_each_child_of_node(root, child)
# 默认根节点下面的子节点都是总线节点,比如 i2C以及SPI
of_platform_bus_create(child, matches, lookup, parent, true);
of_platform_bus_create / Make sure it has a compatible property /
印证了我们的第一个问题,必须要有 compatible 属性
of_get_property(bus, “compatible”, NULL)
创建平台设备数据
dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent); for_each_child_of_node(bus, child)
# 递归循环下去,记住这里是递归
of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
小结
这篇文章简单的分析了设备树编译好的镜像文件怎么转化为设备节点然后转化为平台设备。