论dts中的of_platform_populate如何选择性加载device node为platform device


1.  在比较新的linux内核中,设备树dts已经取代了传统的machine board device description,dts在内核中以各种device node的形式存在,而这些device node对于大部分的内核驱动模块platform_driver来说,最终需要有对应的platform device来与他匹配才可以完成一次device和driver的probe过程。

所有有必要将dts中需要加载为device的device node转为platform device,而这个过程是交给of_platform_populate来完成的(dts相关的device node tree是在start_kernel的setup_arch->unflatten_device_tree来加载dtb并解析)。

    of_platform_populate的入口一般是处于init_machine中,对于arm架构而言位于board.c中的DT_MACHINE_START()的init_machine中定义,而init_machine的调用是以一个module的形式而存在的,该module被加载后的目的,就是做device的create,在以前旧的board中,会将board.c中定义的相关device info转换为对应的device,以便后面的driver加载时可以match到相应的device。在基于dts的设备管理中,这个功能由of_platform_populate来实现

static int __init customize_machine(void)
{
	/* customizes platform devices, or adds new ones */
	if (machine_desc->init_machine)
		machine_desc->init_machine();
	return 0;
}
arch_initcall(customize_machine);
该module的initcall等级相比内核核心的core module来说是较低的,但一般比device module来的高,所以内核中是先存着device然后再当不同的driver被call加载后,完成一次驱动和设备的probe交互。在dts下这种过程典型的是platform device和driver的形式而存在。


2. 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)//NULL/NULL
{
	struct device_node *child;
	int rc = 0;

	root = root ? of_node_get(root) : of_find_node_by_path("/");//根节点,可以从非根节点/开始加载
	if (!root)
		return -EINVAL;

	for_each_child_of_node(root, child) {
		rc = of_platform_bus_create(child, matches, lookup, parent, true);
		if (rc)
			break;
	}

	of_node_put(root);
	return rc;
}
该该函数的功能主要可以总结如下:

a.根据所选择的device node根节点,来递归式的遍历从root node开始以下的所有device node

b.将device node转变为一个platform_device并将其作为device 通过device_add到内核

c.可以判断哪些device node是需要转为device到内核的。

d. 如果传入的root=NULL,则表明从dts的\节点开始逐一的递归处理,否则根据所选择的device node作为root,做递归处理。

e. struct of_device_id *matches,该match table重点是后续节点递归处理时,需要和该table mach后才可以继续递归处理。


需要说明的是dts中定义的各种device node,往往只是用来辅助核心的device node而存在的,也就是说这些node存在并不需要加载为platform device,那么哪些device node是不会在of_platform_populate中被解析为device的呢,具体可以从以下几个方面展开:

static int of_platform_bus_cof_platform_populatereate(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;

	/* Make sure it has a compatible property */
	if (strict && (!of_get_property(bus, "compatible", NULL))) {
		pr_debug("%s() - skipping %s, no compatible prop\n",
			 __func__, bus->full_name);
		return 0;
	}

	auxdata = of_dev_lookup(lookup, bus);//初始设备树解析时lookup为Null
	if (auxdata) {
		bus_id = auxdata->name;
		platform_data = auxdata->platform_data;
	}

	if (of_device_is_compatible(bus, "arm,primecell")) {
		of_amba_device_create(bus, bus_id, platform_data, parent);
		return 0;
	}

	dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);//创建platform device
	if (!dev || !of_match_node(matches, bus))//看该bus即父节点是否属于要继续加载子节点
		return 0;//matches需要用户的驱动支持of_platform_populate

	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;
		}
	}
	return rc;
}
可以看到是有一个类似bus的root device node下来逐步展开的,而这个root node可以不是函数重点关注的是:

a. of_get_property("compatible"),如果这个节点root device属性不存在,则表明其不需要生成为platform device

b. 随着root device node由函数of_platform_device_create_pdata创建为platform device后,需要检查当前节点的compatible是否和match table中定义的table list相互匹配:

const struct of_device_id *of_match_node(const struct of_device_id *matches,
					 const struct device_node *node)
{
	if (!matches)
		return NULL;

	while (matches->name[0] || matches->type[0] || matches->compatible[0]) {
		int match = 1;
		if (matches->name[0])
			match &= node->name
				&& !strcmp(matches->name, node->name);
		if (matches->type[0])
			match &= node->type
				&& !strcmp(matches->type, node->type);
		if (matches->compatible[0])
			match &= of_device_is_compatible(node,
						matches->compatible);
		if (match)
			return matches;
		matches++;
	}
	return NULL;
}
如果当该device node在被创建为platform device后,就不在和match table相匹配时,其对应的child node就不会再被创建platform device,即无论child node是否定义了"compatible",其对应的platform device均不会被生成。

c.对于dts中定义的device node,只有其所属的parent node所属的compatible属性和调用of_platform_populate时传入的of_device_id相互匹配,则说明如果当前的device node只要包含有compatible属性就会被创建为platform device。


以一个简单的dts为例:

/{
  soc0{
  compatible = "my, soc0";
  node1{
  compatible = "my, node1";                    
  node1_1{
  compatible = "my, node1_1";
  node1_1_1{
  compatible = "my, node1_1_1";
}
}
}
  node2{ }
}
  soc1{ compatible = "my, soc1"; }
}
a.假设现在在init_machine中调用of_platform_populate()时传入的root node为NULL,且mach id为”my, soc0","my,node1"

则最终soc0会被首先作为/ root node的child node被加载为platform device,然后依次是node1,由于其相应的compatible和match id匹配,则其对应的child node允许被继续调用of_platform_bus_create,并且对含有compatible的device node生成为platform device,如本例中的node1。同理对于node1而言会加载node1_1节点,但当node1_1生成为device后,由于无法匹配match id,则其无法再递归的处理其下的子节点,从而使得node1_1_1不会被生成为platform device。而这需要说明的是,这个device虽然不会被自动加载为platform device但在node1_1的device driver实现时,可以将node1_1_1形成一种特定的device,如i2c_client等,只是不会生成platform device。从而解释了为何该device node只是以一个device 的形式存在于内核而不是即是i2c_client和platform device。

而soc0下的node2由于不存在compatible属性,同样不会被生成device,以及其child node下的各级device node也都不会被加载。

b,假设传入的root node为soc1,且mach id为"my,node1"时

这个过程会加载node1,然后是node1_1,同样的node1_1_1不会被生成为platform_device.


3.总结

对于of_platform_populate如何选择性的加载device node为platform device在系统启动阶段,可以只关注该device node所属的parent node的compatible属于match id。

一旦自身包含compatible,则会自动调用of_platform_device_create_pdata生成一个platform device。




















<think>我们讨论的是Linux内核中设备树的处理流程,特别是`of_platform_default_populate_init`函数如何将设备树节点device_node)转换为platform_device并挂载到总线上。根据引用内容,我们可以分步骤解释: 1. **函数调用时机**:`of_platform_default_populate_init`是在内核初始化过程中通过`arch_initcall`(优先级3)注册的初始化函数(引用[2])。它在内核启动的`do_initcalls`阶段被执行。 2. **函数作用**:该函数负责遍历设备树(Device Tree)中的节点,并将符合条件的节点转换为`platform_device`(引用[4])。转换的条件包括: - 节点必须有`compatible`属性。 - 根节点的子节点(必须含有`compatible`属性)。 - 某些特定compatible属性(如`"simple-bus"`、`"simple-mfd"`、`"isa"`、`"arm,amba-bus"`)的节点的子节点(也必须含有`compatible`属性)。 3. **转换过程**(引用[5]): - 调用`of_platform_default_populate()`,它内部调用`of_platform_populate()`。 - `of_platform_populate()`遍历根节点的子节点(或者指定的父节点),对每个子节点调用`of_platform_bus_create()`。 - 在`of_platform_bus_create()`中: - 首先检查节点是否匹配(通过`of_default_bus_match_table`,其中包含上述特殊compatible字符串)。 - 如果匹配,则递归处理其子节点(因为这是一个总线节点,其子节点可能代表挂载在该总线上的设备)。 - 然后,无论是否匹配,都会调用`of_platform_device_create_pdata()`来创建该节点对应的设备。 - `of_platform_device_create_pdata()`会调用`of_device_alloc()`分配一个`platform_device`结构,并设置其资源(如内存、中断等)来自设备树节点的属性。 4. **挂载到总线**: - 创建`platform_device`后,通过`platform_device_add()`(在`of_platform_device_create_pdata()`内部调用)将其添加到内核中。 - `platform_device_add()`会将该设备注册到`platform_bus_type`总线上(引用[1])。注册到总线后,内核会将该设备与已注册的驱动程序进行匹配(通过比较驱动程序的`of_match_table`或`id_table`与设备的`compatible`属性)。 5. **特殊总线(如I2C)的处理**: - 对于I2C总线节点,设备树中通常表示为: ```dts i2c1: i2c@400a0000 { compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c"; // ... 其他属性 clock-frequency = <100000>; status = "okay"; eeprom@50 { compatible = "at24,24c02"; reg = <0x50>; }; }; ``` - 在`of_platform_default_populate_init`中,当处理到I2C总线节点(即上面的`i2c1`节点)时,由于它的compatible属性匹配了`of_default_bus_match_table`中的`"simple-bus"`等(实际上,I2C总线节点的compatible通常是特定的,但可能不会匹配`simple-bus`,所以这里需要更精确的解释),因此会递归处理其子节点(即`eeprom@50`)。但是,对于I2C子节点,它们不会被转换为`platform_device`,而是由I2C总线驱动处理(引用[2])。 - 具体来说,I2C总线驱动在初始化时(通常使用`subsys_initcall`)会注册一个`i2c_adapter`,然后调用`of_i2c_register_devices()`来注册该总线下的I2C设备(即生成`i2c_client`,而不是`platform_device`)。因此,设备树中I2C总线节点的子节点不会由`of_platform_default_populate_init`转换为`platform_device`。 6. **总结流程**: - 内核启动到`arch_initcall`阶段,执行`of_platform_default_populate_init`。 - 该函数遍历设备树根节点的子节点,对每个节点: - 如果节点是总线类型(匹配`of_default_bus_match_table`),则递归处理其子节点。 - 然后,为该节点创建`platform_device`(如果它满足条件),并注册到`platform_bus_type`总线上。 - 对于非platform bus的其他总线(如I2C、SPI等),它们的设备节点不会被转换为`platform_device`,而是由各自的总线驱动处理。 注意:设备树中有些节点不会转换为platform_device,比如: - 没有compatible属性的节点。 - 根节点本身。 - 处理为I2C、SPI等总线的设备节点(它们由各自的总线驱动处理)。 因此,`of_platform_default_populate_init`主要处理的是直接挂在平台总线(platform bus)上的设备,以及一些简单总线(如simple-bus)上的设备(这些总线上的设备也会被转换为platform_device)。 ### 挂载到总线的关键代码 在`of_platform_bus_create()`中,创建设备的函数调用链: ```c of_platform_bus_create() if (of_platform_bus_create(child, ...)) // 递归创建子总线设备 ... of_platform_device_create_pdata() struct platform_device *dev; dev = of_device_alloc(np, bus_id, parent); // 分配并设置platform_device ... of_device_add(dev); // 内部调用device_add,将设备添加到总线 ``` 其中,`of_device_alloc`会从设备树节点中提取资源(如内存区域、中断等)并填充到`platform_device`的`resource`数组中。然后,`of_device_add`最终调用`device_add`,该函数会将该设备(`dev->dev`)添加到其总线上(这里是`platform_bus_type`)。总线会进行驱动匹配,如果找到匹配的驱动,则调用驱动的`probe`函数。 ### 示例:设备树节点转换 假设设备树中有如下节点: ```dts / { ... mydevice { compatible = "myvendor,mydevice"; reg = <0x10000000 0x1000>; interrupts = <0 10 4>; }; }; ``` 这个节点会被转换为一个`platform_device`,其`name`为`"myvendor,mydevice"`(或者根据compatible生成),资源包括一块内存区域(起始0x10000000,长度0x1000)和一个中断号(10)。该设备会被注册到`platform_bus_type`总线上。 ### 相关问题 §§相关问题§§ 1. 设备树中哪些节点不会被转换为platform_device? 2. 平台总线和设备树的关系是什么? 3. 如何编写一个平台设备驱动来匹配设备树生成的platform_device? 4. I2C设备在设备树中是如何表示并被内核处理的?</think>### `of_platform_default_populate_init` 如何将设备树节点转换为 `platform_device` 并挂载到总线 `of_platform_default_populate_init` 是 Linux 内核初始化过程中的关键函数,负责将设备树(Device Tree)中的节点转换为 `platform_device` 并注册到对应的总线(主要是平台总线 `platform_bus_type`)。其工作流程如下: --- #### **1. 函数调用时机** - **执行阶段**:在 `arch_initcall` 优先级(优先级 3)被调用,位于内核启动流程的 `do_initcalls()` 阶段[^2]。 - **调用链**: ```mermaid graph TD start_kernel --> rest_init rest_init --> kernel_init kernel_init --> kernel_init_freeable kernel_init_freeable --> do_basic_setup do_basic_setup --> do_initcalls do_initcalls --> of_platform_default_populate_init ``` --- #### **2. 设备树节点转换条件** 并非所有设备树节点都会被转换,需满足以下条件[^4]: 1. **必须包含 `compatible` 属性**:标识设备驱动匹配的关键。 2. **特定节点类型**: - 根节点的直接子节点(需含 `compatible`)。 - 特殊总线节点的子节点(如 `simple-bus`, `simple-mfd`, `isa`, `arm,amba-bus` 等)。 --- #### **3. 转换与注册流程** ```mermaid graph LR A[of_platform_default_populate_init] --> B[of_platform_populate] B --> C{遍历根节点节点} C --> D[of_platform_bus_create] D --> E[检查节点是否匹配总线类型] E -->|是| F[递归处理子节点] E -->|否| G[of_platform_device_create_pdata] G --> H[of_device_alloc] H --> I[填充 platform_device 资源] I --> J[platform_device_register] J --> K[挂载到 platform_bus_type] ``` ##### 关键步骤详解: 1. **节点遍历**: 通过 `of_platform_populate()` 遍历设备树根节点的子节点[^5]。 2. **总线类型检查**: 使用 `of_default_bus_match_table` 匹配特殊总线类型(如 `simple-bus`)。若匹配,则递归处理其子节点[^4][^5]。 3. **创建 `platform_device`**: - **资源分配**:`of_device_alloc()` 根据节点属性(`reg`, `interrupts` 等)填充 `platform_device` 的 `resource` 结构[^5]。 - **设备初始化**:设置 `dev.of_node` 指向原始设备树节点,保留 `compatible` 等关键属性[^1]。 4. **注册到总线**: 通过 `platform_device_register()` 将设备添加到 `platform_bus_type` 总线。此时触发总线匹配机制: - 总线遍历已注册的 `platform_driver`。 - 匹配驱动与设备的 `compatible` 字符串。 - 若匹配成功,调用驱动的 `probe()` 函数[^1]。 --- #### **4. 总线挂载的底层实现** - **平台总线注册**: `platform_bus_type` 在内核启动早期(`core_initcall` 优先级)已注册: ```c struct bus_type platform_bus_type = { .name = "platform", .match = platform_match, // 匹配函数 }; core_initcall(platform_bus_init); // 优先级 1 ``` 其中 `platform_match()` 通过比较设备和驱动的 `compatible` 实现匹配[^1]。 - **设备注册触发匹配**: 当 `platform_device_register()` 执行时,内核调用: ```c bus_add_device(dev); // 将设备加入总线 bus_probe_device(dev); // 触发驱动匹配 ``` --- #### **5. 特殊总线节点的处理** 对于非平台总线(如 I2C、SPI): 1. **I2C 总线节点**: 设备树中的 I2C 控制器节点会被转换为 `platform_device`(因其在 `compatible` 中声明为平台设备),但挂载的 I2C 设备(如 `eeprom@50`)由 I2C 子系统通过 `of_i2c_register_devices()` 处理,生成 `i2c_client` 而非 `platform_device`[^2]。 2. **递归处理**: 若节点是 `simple-bus` 类型,`of_platform_bus_create()` 会递归处理其子节点,确保层级结构完整转换[^4][^5]。 --- ### 总结 - ✅ **转换前提**:仅含 `compatible` 的节点被转换,根节点、纯硬件描述节点(如 `clock`)除外[^3][^4]。 - ✅ **总线挂载**:通过 `platform_device_register()` 注册到 `platform_bus_type`,触发驱动匹配。 - ⚠️ **递归处理**:特殊总线节点(如 `simple-bus`)的子节点会被递归转换,形成设备层级[^5]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值