转载地址:https://blog.csdn.net/gzzaigcnforever/article/details/50552658
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);
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;
- }
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;
- }
c.对于dts中定义的device node,只有其所属的parent node所属的compatible属性和调用of_platform_populate时传入的of_device_id相互匹配,则说明如果当前的device node只要包含有compatible属性就会被创建为platform device。
以一个简单的dts为例:
- /{
- soc0{ // platform device
- compatible = "my, soc0";
- node1{ // platform device
- compatible = "my, node1";
- node1_1{ // platform device
- compatible = "my, node1_1";
- node1_1_1{
- compatible = "my, node1_1_1";
- }
- }
- }
- node2{ }
- }
- soc1{ compatible = "my, soc1"; }
- }
则最终soc0会被首先作为/ root node的child node被加载为platform device,然后依次是node1,由于node1其相应的compatible和match id匹配,则其对应的child node(node1_1)允许被继续调用of_platform_bus_create,并且对含有compatible的device node生成为platform device (node1_1),如本例中的node1。同理对于node1而言会加载node1_1节点,但当node1_1生成为device后,由于无法匹配match id,则其无法再递归的处理其下的子节点(node1_1_1),从而使得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。