论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内核启动过程中,设备树中的设备节点是如何与平台设备(platform_device)匹配,以及最终与平台驱动(platform_driver)匹配的。其中,of_device_id结构体是平台驱动中用于匹配设备树节点的关键结构。 在Linux内核中,我们可以通过以下方法来查看of_device_id匹配的设备信息: 1. **查看/sys文件系统**:在sysfs中,每个注册的平台设备(platform_device)和平台驱动(platform_driver)都有对应的目录。我们可以通过查看驱动的匹配表来了解哪些设备被匹配了。 2. **使用内核调试工具**:如动态调试(dynamic debug)、ftrace等。 3. **通过内核日志**:在驱动中增加打印信息,或者在启动时通过内核参数打开相关日志。 具体步骤: ### 方法1:通过/sys文件系统查看 假设我们有一个平台驱动,其名字为`my_driver`,在/sys/bus/platform/drivers/目录下会有以驱动名命名的目录。进入该目录,可以看到该驱动已经匹配的设备。 例如: ```bash cd /sys/bus/platform/drivers/my_driver ls ``` 在该目录下,通常会有一个符号链接指向匹配的设备,设备的命名通常是设备树中节点的路径(用点号代替斜杠)或者设备树节点中定义的设备名。 另外,我们也可以查看驱动的匹配表: ```bash cat /sys/bus/platform/drivers/my_driver/modalias ``` 这个文件会显示驱动支持的匹配条件(包括compatible字符串等)。但是,它不会显示当前匹配了哪些设备。 要查看当前匹配的设备,可以查看该驱动目录下的设备链接。 ### 方法2:通过内核日志 在内核启动时,可以通过设置内核启动参数来打印设备匹配信息。例如,设置`loglevel=8`(或者使用`debug`参数)可以打印更详细的内核信息。另外,可以在平台驱动的probe函数中加入打印,当设备匹配成功时,会调用probe函数,此时打印设备的信息。 例如,在驱动代码中: ```c static int my_driver_probe(struct platform_device *pdev) { dev_info(&pdev->dev, "Device matched and probed\n"); // ... 其他代码 } ``` ### 方法3:通过动态调试 如果驱动已经加载,我们可以使用动态调试来打开设备匹配相关的调试信息。 首先,需要确保内核编译时启用了`CONFIG_DYNAMIC_DEBUG`。然后,我们可以通过以下命令打开调试信息: ```bash echo 'file drivers/platform/* +p' > /sys/kernel/debug/dynamic_debug/control ``` 这将会打开所有在`drivers/platform/`目录下驱动的动态调试信息(具体路径需要根据驱动所在位置调整)。 然后,重新加载驱动(或者触发匹配事件),就可以在内核日志中看到详细的匹配过程。 ### 方法4:查看设备树节点是否被成功转换为platform_device 设备树中的节点会被内核转换为platform_device,前提是: - 该节点必须包含compatible属性。 - 该节点必须位于根节点下(或者包含在某个总线节点下,但平台设备通常是根节点的子节点)。 我们可以通过以下方式查看系统已经注册的平台设备: ```bash ls /sys/devices/platform/ ``` 这个目录下列出了所有注册的平台设备。每个设备对应一个目录,进入目录可以查看设备的信息,例如`of_node`符号链接指向该设备对应的设备树节点(在/sys/firmware/devicetree/base下)。 另外,我们可以通过`cat /sys/devices/platform/my_device/modalias`来查看该设备的匹配信息(通常包括compatible字符串)。 ### 方法5:通过ftrace跟踪 使用ftrace可以跟踪内核函数,例如跟踪`platform_match`(平台总线匹配函数): ```bash cd /sys/kernel/debug/tracing echo function > current_tracer echo platform_match > set_ftrace_filter echo 1 > tracing_on # 然后触发设备匹配事件(如加载驱动) cat trace ``` 以上方法中,最常用的是方法1(查看sysfs)和方法2(在probe函数中打印)。 总结:要查看of_device_id结构体匹配的设备信息,最直接的方法是在驱动的probe函数中打印设备信息,或者通过sysfs查看驱动匹配的设备。 引用说明: - 引用[2]提到平台设备驱动模型,设备和驱动分离,通过总线匹配。 - 引用[4]提到设备树节点如何转换为platform_device,并注册到平台总线。 因此,通过sys文件系统查看驱动目录下匹配的设备,或者通过打印probe函数中的设备信息,可以确定设备树匹配表中哪个设备被加载。 最后,生成相关问题:</think>要查看 Linux 内核中 `of_device_id` 结构体匹配的设备信息,可以通过以下方法实现: ### 一、核心查看方法 #### 1. **查看 `/sys/firmware/devicetree` 文件系统** ```bash # 列出所有匹配的设备树节点 ls /sys/firmware/devicetree/base | grep compatible # 查看具体节点的 compatible 属性 cat /sys/firmware/devicetree/base/<node_path>/compatible ``` - **作用**:直接读取设备树中注册的 compatible 字符串,即 `of_device_id` 的匹配依据[^4] - **示例输出**: ``` mediatek,disp-leds // 对应 of_device_id 的 .compatible 字段 ``` #### 2. **通过内核日志过滤匹配信息** 在内核启动参数中添加: ```bash loglevel=7 # 开启调试日志级别 ``` 查看日志中关键信息: ```bash dmesg | grep -E "of_device_id|matched" ``` - **典型日志**: ``` [ 2.650000] platform leds-disp: matched compatible: mediatek,disp-leds [ 2.652000] mtk_leds_disp: probe success for device: disp-backlight ``` ### 二、高级调试方法 #### 1. **动态调试驱动匹配过程** ```c // 在驱动代码中添加调试打印 static int led_disp_probe(struct platform_device *pdev) { const struct of_device_id *match; match = of_match_device(of_disp_leds_match, &pdev->dev); pr_info("Matched device: %s with compatible: %s\n", pdev->name, match->compatible); // 关键匹配信息 } ``` - **效果**:在 `dmesg` 中输出具体匹配的 compatible 字符串[^2] #### 2. **查看 `/sys/bus/platform/devices`** ```bash # 列出所有注册的平台设备 ls /sys/bus/platform/devices # 查看设备绑定的驱动 cat /sys/bus/platform/devices/<device_name>/driver/module/drivers ``` - **输出示例**: ``` platform:mtk_leds_disp // 显示绑定的驱动名称 ``` ### 三、设备树与驱动匹配流程 Linux 设备树匹配的核心流程如下[^4]: ```mermaid graph TD A[设备树节点] -->|of_platform_populate| B(创建 platform_device) B --> C{注册到 platform_bus_type} D[platform_driver] -->|of_match_table| C C -->|匹配成功| E[调用 driver.probe] E --> F[初始化硬件] ``` 1. 内核解析设备树节点生成 `platform_device` 2. 驱动通过 `of_device_id` 表声明兼容字符串 3. 总线层比较 `.compatible` 字段 4. 匹配成功后调用驱动的 `probe()` 函数[^2] ### 四、匹配失败排查 若设备未加载,检查: 1. **设备树节点**: ```dts leds_disp: disp-leds { compatible = "mediatek,disp-leds"; // 必须与驱动声明一致 status = "okay"; }; ``` 2. **驱动声明**: ```c static const struct of_device_id of_disp_leds_match[] = { { .compatible = "mediatek,disp-leds" }, // 匹配字符串 {} }; ``` 3. **内核配置**: ```bash zcat /proc/config.gz | grep CONFIG_LEDS_MTK # 确保驱动编译进内核 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值