内核杂谈——关于platform device 创建

内核杂谈——关于platform device 创建当拿到driver,不能用起来的时候需要去检查device了。虽说device和bus通常都是系统中带的,但也不要想当然的认为这个系统是帮你建好的。通常busdevicedriver三者中,bus基本不用干预,device干预的少,driver干预的多。从设备树中生成device从设备树中识别device的入口为arch_initcall_sync(of_platform_default_populate_init);staticint__initof_platform_defa

大家好,又见面了,我是你们的朋友全栈君。

当拿到driver,不能用起来的时候需要去检查device了。虽说device和bus通常都是系统中带的,但也不要想当然的认为这个系统是帮你建好的。

通常 bus device driver三者中,bus基本不用干预,device 干预的少,driver 干预的多。

从设备树中生成device

从设备树中识别 device的入口为 arch_initcall_sync(of_platform_default_populate_init);

static int __init of_platform_default_populate_init(void)
{ 
   
// /firmware 节点
	node = of_find_node_by_path("/firmware");
	if (node) { 
   
		of_platform_populate(node, NULL, NULL, NULL);
		of_node_put(node);
	}
// 传参为NULL,默认获取 / 节点
	of_platform_default_populate(NULL, NULL, NULL);

	return 0;
}

往下调用

int of_platform_default_populate(struct device_node *root,
				 const struct of_dev_auxdata *lookup,
				 struct device *parent)
{ 
   
	return of_platform_populate(root, of_default_bus_match_table, lookup,
				    parent);
}

注意参数 of_default_bus_match_table

const struct of_device_id of_default_bus_match_table[] = { 
   
	{ 
    .compatible = "simple-bus", },
	{ 
    .compatible = "simple-mfd", },
	{ 
    .compatible = "isa", },
#ifdef CONFIG_ARM_AMBA
	{ 
    .compatible = "arm,amba-bus", },
#endif /* CONFIG_ARM_AMBA */
	{ 
   } /* Empty terminated list */
};

到函数of_platform_populate后,出现两个条件情况
第一个 root = node = of_find_node_by_path(“/firmware”);
第二个 root = of_find_node_by_path(“/”) + of_default_bus_match_table[]
然后执行 of_platform_bus_create(child, matches, lookup, parent, true);
第一种情况:child 从 “/firmware” 节点开始找,没有match限制
第二种情况:child 从 “/” 节点开始找,有match限制
这两种情况中,通常第二点作为创建platform device的主要规则

进入该函数,该函数就是创建 platform device 的

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)
{ 
   
	struct platform_device *dev;
	int rc = 0;

	/* 节点 必须要有 compatible 词条 */
	if (strict && (!of_get_property(bus, "compatible", NULL))) 
		return 0;

	/* 节点含有 of_skipped_node_table中定义的 compatible 词条,不做 device */
	if (unlikely(of_match_node(of_skipped_node_table, bus)))
		return 0;
		
// 属性为 arm,primecell ,创建完就退出。针对 arm 的
	if (of_device_is_compatible(bus, "arm,primecell")) { 
   
		of_amba_device_create(bus, bus_id, platform_data, parent);
		return 0;
	}
// --------------------********重点重点重点在下面,规则的体现********-------------- 
// 需要有 of_default_bus_match_table 中定义的 compatible 词条
	dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
// 走到此处需要停下来 ,静静思考:若当前节点含有compatible词条,但词条不在
// of_default_bus_match_table 中,则当前节点下面的所有子节点,不做为platform device
	if (!dev || !of_match_node(matches, bus))
		return 0;
//词条在of_default_bus_match_table 中,则当前节点的下一个子节点,做为platform device
	for_each_child_of_node(bus, child) { 
   
// 若节点还有子节点,递归创建device,条件为上述,直到不满足条件退出
		rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
		if (rc) { 
   
			of_node_put(child);
			break;
		}
	}
	return rc;
}

of_platform_device_create_pdata 就是 具体创建 设备树中符合条件的device,将节点相关信息解析并保存到struct platform_device中。比如 irq,resource (内存信息,地址等)等。

一般情况 从 ” / ” 节点,找child,作为bus node ,child 节点是否有 compatible ,有则往下走;将child创建为device,然后判断child是否含有of_default_bus_match_table中的compatible,有,则child作为platform device就成了。然后继续以child 作为 parent,继续循环下去,找child,作为bus node ,child 节点是否有 compatible ,有则往下走;将child创建为device,然后判断child是否含有of_default_bus_match_table中的compatible,有,则child作为platform device就成了 …

所以,格式如下

1 最常规的,/ 下,A成了platform device
/ { 
   
	A { 
   
		compatible = "xxx"
    }
}
2 A成了platform device,b不为platform device,一般我们都是这么写的
A { 
   
	compatible = " of_default_bus_match_table. compatible"
	B { 
   
		compatible = " xxx"
	}
}
3 无限接力的.基本不存在
A { 
   
	compatible = " of_default_bus_match_table. compatible"
	B { 
   
		compatible = " of_default_bus_match_table. compatible "
		C { 
   
			...compatible 同上
		};
	};
};

平台总线的设备对应的bus为platform_bus_type,不属于这个bus范畴就不是platform device。
dev->dev.bus = &platform_bus_type;
platform bus的设备对应目录 /sys/bus/platform/devices

题外话
注意到属性arm,primecell,这个是arm中的AMBA总线。含有amba属性的节点会被注册成amba设备,不会成为platform设备。比如pl011串口,还有其他pl0xx乱七八糟其他arm特有的设备。
dev->dev.bus = &amba_bustype;
amba bus的设备对应目录 /sys/bus/amba/devices/

有人看到/sys/devices/platform这个目录,是社么?
虽然带有platform字眼,但是这个目录表示的是所有外部设备,soc外接的所有设备都汇总在这个目录下。不是平台总线。毕竟目录中没有总线bus这个词!
至于这目录怎么创建出来的,不懂。

从ACPI 生成device

acpi初始化函数

static struct acpi_scan_handler apd_handler = { 
   
	.ids = acpi_apd_device_ids,
	.attach = acpi_apd_create_device,
};

void __init acpi_apd_init(void)
{ 
   
	acpi_scan_add_handler(&apd_handler);
}

其中的ids,存放了 acpi中定义的属性,如下格式。这些都是厂商私有数据,不像设备树的of_default_bus_match_table 为通用属性。

static const struct acpi_device_id acpi_apd_device_ids[] = { 
   
	/* Generic apd devices */
#ifdef CONFIG_ARM64
	{ 
    "NXP0001", APD_ADDR(nxp_i2c_desc) },
#endif
// 自定义的描述符,需要添加到此处,否则系统起来,不会添加。和设备树比起来,多了一步。
	{ 
    }
};

当然也有禁用的 acpi 描述符 存放在forbidden_id_list

第一个字符串id 类比设备树里的 compatible 。注意在acpi table中的描述文件中也要添加该字符串
第二个是描述信息

static const struct apd_device_desc nxp_i2c_desc = { 
   
//如果这个 desc 没有定义 fixed_clk_rate, setup 函数直接 return 0
	.setup = acpi_apd_setup,
	.fixed_clk_rate = 350000000,
};

创建入口为 acpi_apd_create_device ——- acpi_create_platform_device —– platform_device_register_full

device 保存路径

最终会调用device_add,以kobject的形式呈现在sysfs下
/sys/devices/platform

I2C设备

i2c总线节点,一般是在 “ / ” 下,所以默认生成 platform device

i2c0: i2c@10002000 { 
   
	#address-cells = <1>;
	#size-cells = <0>;
	compatible = "arm,versatile-i2c";
	reg = <0x10002000 0x1000>;

	rtc@68 { 
   
		compatible = "dallas,ds1338";
		reg = <0x68>;
	};
};

如上 i2c节点在“ / ” 下,i2c@10002000会被注册成 platform device。
i2c节点下有 compatible ,但不是of_default_bus_match_table中的词条,所以该节点的子节点rtc不会被注册成 platform device,但是会注册成 i2c device。
到 i2c 代码初始化后,i2c@10002000 会被注册成 i2c adapter,以 i2c-0/1/2/…形式展示,然后会将其下的i节点注册成i2c device。
详见函数 of_i2c_register_devices

和 i2c 区别的是:
platform device 对应driver使用 module_platform_driver,
i2c device 对应的driver使用 module_i2c_driver。
两者匹配上也比较相似。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/163790.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • pycharm中创建虚拟环境「建议收藏」

    pycharm中创建虚拟环境「建议收藏」1什么是虚拟环境虚拟环境是用于依赖项管理和项目隔离的Python工具,允许Python站点包(第三方库)安装在本地特定项目的隔离目录中,而不是全局安装(即作为系统范围内的Python的一部分)。这听起来不错,但到底什么是虚拟环境呢?虚拟环境只是一个包含三个重要组件的目录:·安装了第三方库的site-packages/文件夹。·系统上安装的Python可执行文件的symlink符号链接。·确保执行Python代码的脚本使用在给定虚拟环境中安装的Python解释器和站点包。2.为什么使用虚

  • 推荐 20 款 IDEA 主题!「建议收藏」

    推荐 20 款 IDEA 主题!「建议收藏」官方对主题模块的介绍作为一名开发人员,您需要使用大量文本资源:编辑器中的源代码、搜索结果、调试器信息、控制台输入和输出等等。颜色和字体样式用于格式化这个文本,并帮助您更好地理解它一目了然…

  • mysql 层级结构查询

    mysql 层级结构查询

    2021年11月27日
  • 准备日志迁移

    准备日志迁移

  • 微信小程序累计访客数怎么查_小程序独立访客uv是什么

    微信小程序累计访客数怎么查_小程序独立访客uv是什么在小程序后台查看打开这里然后在这里查看,不过是昨天的数据当天的在下一天更新也可以登录后台顶部导航栏右侧将鼠标挺方在手机查看数据上方扫二维码即可查看还有都告诉了这么多了扫一扫下方小程序码支持一下,谢谢…

  • matlab的imread怎么用_imread函数参数的含义

    matlab的imread怎么用_imread函数参数的含义imread(matlab)                                            函数语法 A = imread(filename, fmt)[X, map] = imread(…)[…] = imread(filename)[…] = imread(URL,…)[…] = imread(

    2022年10月14日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号