(三)从解析DTS到创建device_从device_node到并入设备驱动模型(结合源码)

从device_node到并入设备驱动模型

此篇博客有很多参考其他文章的内容,由于参考内容繁杂,不一一标注角标了,在末尾会贴上所有参考博客的link,如有侵权,请联系本人处理,谢谢。

深入,并且广泛
					 -沉默犀牛

上一篇文章已经详细的分析了两个问题:
1.如何根据Device Tree的信息,找到最适合的machine_desc
2.如何将DTB转换成节点是device_node的树状结构

那么为什么要做这两件事情呢?

我们现在要做的事情是把DTS中描述的节点(status = okay的)注册到kernel中,DTS的信息我们的kernel没法直接识别啊,就得通过上述的2把DTS中描述的每一个节点,转换成device_node结构体,我们可以认为DTS中写的每一个节点在这里都被解析为一个device_node。(该结构体中有parent、child、sibling成员,通过这些成员把device_node给连接成了树状结构)好,现在我们有了完全代表了DTS信息,又能被kernel识别的device_node,现在就要把这些device_node一个个的创建起来(比如有的device_node要注册到platform总线上,有的要注册到i2c总线…),这个创建的过程,就需要我们上述1中的machine_desc了!在定义machine_desc结构体的时候,会定义一个回调函数:xxxx_init(大部分情况)就是这个xxxx_init把device_node都注册起来。

再补充上面一段话的三个点
1.xxxx_init函数只是注册了platform总线上的device_node,那其他的呢?比如i2c上的device呢?在注册i2c总线时,调用qup_i2c_probe(),这个接口会添加i2c适配器,适配器添加完成后会调用of_i2c_register_devices()接口来i2c总线节点的子节点,然后调用i2c_new_device(),生成i2c设备。

2.我在定义回调函数后面写了个大部分情况,也就代表有的时候不会定义这个回调函数,那么kernel怎么注册device_node呢?事实上kernel会检测machine_desc有没有init函数,如果有,则调用;如果没有,则调用of提供的接口直接注册所有platform的device,这里就有一个问题了:这个machine_desc如果没有定义init函数,那我还要它干嘛呢?我去看(根据DTS选择最适配的machine_desc)那一段代码时发现,如果没有找到合适的machine_desc的话,有一行注释:does not return! 这似乎意味着,找不到合适的machine_desc也没啥关系,kernel还是会继续运行下去,platform上的device还是可以注册上。如果这一点的理解有误,请留言纠正,大恩不言谢!

3.DTS中的节点都转换成了device_node,但是DTS中描述的节点并不都是device啊,比如cpus node,memory node,choose node等。对于这些节点的处理,我会在下一篇文章中说明。

经过前面的介绍和解释,我们明确了这一篇文章的主题:
那些platform总线下的device_node如何注册到platform总线上的?

执行流程

进入kernel的入口后,会按照如下的调用流程start_kernel->rest_init->kernel_init->kernel_init_freeable->
do_basic_setup->do_initcallsdo_initcalls函数中,在do_initcalls函数中,kernel会依次执行各个initcall函数。

代码验证

在上述的调用流程中,会调用到调用customize_machine,也正是这个函数,检测了machine_desc是否有xxxx_init函数,代码如下:

static int __init customize_machine(void)
{

    if (machine_desc->init_machine)  //如果有init_machine()函数,则执行
        machine_desc->init_machine();
    else  //否则直接调用of提供的接口来注册device
        of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);

    return 0;
}
arch_initcall(customize_machine); 

以高通msm8953为例,看看它有没有定义init_machine函数:

DT_MACHINE_START(MSM8953_DT,
	"Qualcomm Technologies, Inc. MSM8953 (Flattened Device Tree)")
	.init_machine		= msm8953_init,    //这里定义了init函数
	.dt_compat		= msm8953_dt_match,    //这个参数就是之前匹配machine_desc用的
						       //这个参数将于DTS中的compatible属性值来比较
MACHINE_END

再看看这个函数的内容:

static void __init msm8953_init(void)
{
	board_dt_populate(NULL);
}
void __init board_dt_populate(struct of_dev_auxdata *adata)
{
	//传入NULL参数表示从root node开始scan
	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);  

	/* Explicitly parent the /soc devices to the root node to preserve
	 * the kernel ABI (sysfs structure, etc) until userspace is updated
	 */
	 //从soc节点开始scan
	of_platform_populate(of_find_node_by_path("/soc"),
			     of_default_bus_match_table, adata, NULL);
}

看来of_platform_populate()这个函数就是主要起作用的函数了,仔细分析一下:

int of_platform_populate(struct device_node *root,
			const struct of_device_id *matches,
			const struct of_dev_auxdata *lookup,
			struct device *parent)
{
	struct device_node *child;
	int rc = 0;
	
	 //之前传入的NULL,就是从/节点下开始,即根节点
	root = root ? of_node_get(root) : of_find_node_by_path("/"); 
	if (!root)
		return -EINVAL;

	pr_debug("%s()\n", __func__);
	pr_debug(" starting at: %s\n", root->full_name);

	for_each_child_of_node(root, child) {  //这里面是一个for循环,如果root节点下面有child,就执行一遍
		rc = of_platform_bus_create(child, matches, lookup, parent, true);  //重要函数
		if (rc) {
			of_node_put(child);
			break;
		}
	}
	of_node_set_flag(root, OF_POPULATED_BUS);

	of_node_put(root);
	return rc;
}

看来of_platform_bus_create()这个函数是个重要函数,仔细分析一下:

static int of_platform_bus_create(struct device_node *bus,
				  const struct of_device_id *matches,
				  const struct of_dev_auxdata *lookup,
				  struct device *parent, 
				  bool strict)
{
	const struct of_dev_auxdata *auxdata;
	struct device_node *child;
	struct platform_device *dev;
	const char *bus_id = NULL;
	void *platform_data = NULL;
	int rc = 0;

	//确保device node有compatible属性的代码
	if (strict && (!of_get_property(bus, "compatible", NULL))) {
		pr_debug("%s() - skipping %s, no compatible prop\n",
			 __func__, bus->full_name);
		return 0;
	}

	if (of_node_check_flag(bus, OF_POPULATED_BUS)) {
		pr_debug("%s() - skipping %s, already populated\n",
			__func__, bus->full_name);
		return 0;
	}
	
	//在传入的lookup table寻找和该device node匹配的附加数据
	auxdata = of_dev_lookup(lookup, bus);
	if (auxdata) {
		bus_id = auxdata->name;
		platform_data = auxdata->platform_data;
	}
	
	/*ARM公司提供了CPU core,除此之外,它设计了AMBA的总线来连接SOC内的各个block。
	符合这个总线标准的SOC上的外设叫做ARM Primecell Peripherals。
	如果一个device node的compatible属性值是arm,primecell的话,
	可以调用of_amba_device_create来向amba总线上增加一个amba device。*/
	if (of_device_is_compatible(bus, "arm,primecell")) {
		/*
		 * Don't return an error here to keep compatibility with older
		 * device tree files.
		 */
		of_amba_device_create(bus, bus_id, platform_data, parent);
		return 0;
	}

	//如果不是ARM Primecell Peripherals,那么我们就需要向platform bus上增加一个platform device了 
	dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
	if (!dev || !of_match_node(matches, bus))
		return 0;
	
	// 一个device node可能是一个桥设备,因此要重复调用of_platform_bus_create来把所有的device node处理掉。
	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);
		if (rc) {
			of_node_put(child);
			break;
		}
	}
	of_node_set_flag(bus, OF_POPULATED_BUS);
	return rc;
}

具体增加platform device的代码在of_platform_device_create_pdata()中,代码如下:

static struct platform_device *of_platform_device_create_pdata(
					struct device_node *np,
					const char *bus_id,
					void *platform_data,
					struct device *parent)
{
	struct platform_device *dev;

	if (!of_device_is_available(np) ||
	    of_node_test_and_set_flag(np, OF_POPULATED))  //check status属性,和是否已经注册过
		return NULL;

	// of_device_alloc除了分配struct platform_device的内存,还分配了该platform device需要的resource的内存
	//这里就根据struct device_node创建了struct platform_device
	dev = of_device_alloc(np, bus_id, parent);  
	if (!dev)
		goto err_clear_flag;

	//设定platform_device 中的其他成员 
	dev->dev.bus = &platform_bus_type;
	dev->dev.platform_data = platform_data;
	of_dma_configure(&dev->dev, dev->dev.of_node);
	of_msi_configure(&dev->dev, dev->dev.of_node);
	of_reserved_mem_device_init_by_idx(&dev->dev, dev->dev.of_node, 0);

	if (of_device_add(dev) != 0) {  //把这个platform device加入统一设备模型系统中 
		of_dma_deconfigure(&dev->dev);
		platform_device_put(dev);
		goto err_clear_flag;
	}

	return dev;

err_clear_flag:
	of_node_clear_flag(np, OF_POPULATED);
	return NULL;
}

到这里我们就很清楚device_node是如何最终成为platform_device了。

猜你喜欢

转载自blog.csdn.net/qq_35065875/article/details/82868976