基于Linux 3.10.49内核从dts文件里注册platform_device流程分析
Linux kernel 3.10.49+
在这里, 我们说说Linux 是怎么通过dts进行设备(platform_device)注册和初始化板载信息.
在arch/arm/mach-******/******.c找到DT_MACHINE_START 和 MACHINE_END 宏, 如下:
DT_MACHINE_START(******_DT, "************* SoC (Flattened Device Tree)")
.atag_offset = 0x100,
.dt_compat = ******_dt_compat, // 匹配dts
.map_io = ******_map_io, // 板级地址内存映射, linux mmu
.init_irq = irqchip_init, // 板级中断初始化.
.init_time = ******_timer_and_clk_init, // 板级时钟初始化,如ahb,apb等
.init_machine = ******_dt_init, // 这里是解析dts文件入口.
.restart = ******_restart, // 重启, 看门狗寄存器相关可以在这里设置
MACHINE_END
其中.dt_compat = ******_dt_compat 这个结构体是匹配是哪个dts文件, 如:
static const char * const ******_dt_compat[] = {
"******,******-soc",
NULL
};
这个"******,******-soc" 字符串可以在我们的dts的根节点下可以找到.
好了, 我们来看看 init_machine = ******_dt_init 这个回调函数.
1. arch/arm/mach-******/******.c : void __init ******_dt_init(void)
******_dt_init(void) --> of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
of_default_bus_match_table 这个是struct of_device_id的全局变量.
const struct of_device_id of_default_bus_match_table[] = {
{ .compatible = "simple-bus", },
#ifdef CONFIG_ARM_AMBA
{ .compatible = "arm,amba-bus", },
#endif /* CONFIG_ARM_AMBA */
{} /* Empty terminated list */
};
我们设计dts时, 把一些需要指定寄存器基地址的设备放到以compatible = "simple-bus"为匹配项的设备节点下. 下面会有介绍为什么.
2. drivers/of/platform.c : int of_platform_populate(...)
of_platform_populate(...) --> of_platform_bus_create(...)
// 在这之前, 会有of_get_property(bus, "compatible", NULL)
// 检查是否有compatible, 如果没有, 返回, 继续下一个, 也就是说没有compatible, 这个设备不会被注册
for_each_child_of_node(root, child) {
printk("[%s %s %d] child->name = %s, child->full_name = %s\n", __FILE__, __func__, __LINE__, child->name, child->full_name);
rc = of_platform_bus_create(child, matches, lookup, parent, true);
if (rc)
break;
}
论询dts根节点下的子设备, 每个子设备都要of_platform_bus_create(...);
全部完成后, 通过 of_node_put(root); 释放根节点, 因为已经处理完毕;
3. drivers/of/platform.c : of_platform_bus_create(bus, ...)
dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent); // 我们跳到 3-1-1步去运行
if (!dev || !of_match_node(matches, bus)) // 就是匹配
// dt_compat = ******_dt_compat, 也就是 compatible = "simple-bus",
// 如果匹配成功, 以本节点为父节点, 继续轮询本节点下的所有子节点
return 0;
for_each_child_of_node(bus, child) {
pr_debug(" create child: %s\n", child->full_name);
rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict); // dev->dev以本节点为父节点, 我们跳到 3-2-1步去运行
if (rc) {
of_node_put(child);
break;
}
}
3-1-1. drivers/of/platform.c : of_platform_device_create_pdata(...)
if (!of_device_is_available(np)) // 查看节点是否有效, 如果节点有'status'属性, 必须是okay或者是ok, 才是有效, 没有'status'属性, 也有效
return NULL;
dev = of_device_alloc(np, bus_id, parent); // alloc设备, 设备初始化. 返回dev, 所有的设备都可认为是platform_device, 跳到3-1-1-1看看函数做了什么事情
if (!dev)
return NULL;
#if defined(CONFIG_MICROBLAZE)
dev->archdata.dma_mask = 0xffffffffUL;
#endif
dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); // dev->dev 是 struct device. 继续初始化
dev->dev.bus = &platform_bus_type; //
dev->dev.platform_data = platform_data;
printk("[%s %s %d] of_device_add(device register) np->name = %s\n", __FILE__, __func__, __LINE__, np->name);
if (of_device_add(dev) != 0) { // 注册device, of_device_add(...) --> device_add(...) // This is part 2 of device_register()
platform_device_put(dev);
return NULL;
}
3-1-1-1. drivers/of/platform.c : of_device_alloc(...)
1) alloc platform_device *dev
2) 如果有reg和interrupts的相关属性, 运行of_address_to_resource 和 of_irq_to_resource_table, 加入到dev->resource
dev->num_resources = num_reg + num_irq;
dev->resource = res;
for (i = 0; i < num_reg; i++, res++) {
rc = of_address_to_resource(np, i, res);
/* printk("[%s %s %d] res->name = %s, res->start = 0x%X, res->end = 0x%X\n", __FILE__, __func__, __LINE__, res->name, res->start, res->end); */
WARN_ON(rc);
}
WARN_ON(of_irq_to_resource_table(np, res, num_irq) != num_irq);
3) dev->dev.of_node = of_node_get(np);
// 这个node属性里有compatible属性, 这个属性从dts来, 后续driver匹配device时, 就是通过这一属性进匹配
// 我们可以通过添加下面一句话来查看compatible.
// printk("[%s %s %d] bus->name = %s, of_get_property(...) = %s\n", __FILE__, __func__, __LINE__, np->name, (char*)of_get_property(np, "compatible", NULL));
// node 再给dev, 后续给驱动注册使用.
4) 运行 of_device_make_bus_id 设定device的名字, 如: soc.2 或 ac000000.serial 等
3-2-1. drivers/of/platform.c :
以 compatible = "simple-bus"的节点的子节点都会以这个节点作为父节点在这步注册设备.
这是实际的板载设备, 也是最终目的.
下一篇分析,我们来讲讲platform_driver的注册, 是怎么匹配刚才我们注册过的platform_device的