一,调用到of_platform_populate的流程
kernel V5.10:
start_kernel(void)
----setup_arch(&command_line);
--------setup_machine_fdt(__fdt_pointer); /* D:\work\source_code\msm-kernel\msm_kernel\arch\arm64\kernel\setup.c */
--------unflatten_device_tree(); /* D:\work\source_code\msm-kernel\msm_kernel\drivers\of\fdt.c */
----arch_call_rest_init();
--------rest_init();
------------kernel_thread(kernel_init, NULL, CLONE_FS);
----------------kernel_init_freeable();
--------------------do_basic_setup();
------------------------driver_init();
----------------------------firmware_init(); /* D:\work\source_code\msm-kernel\msm_kernel\drivers\base\firmware.c */
--------------------------------firmware_kobj = kobject_create_and_add("firmware", NULL);
----------------------------of_core_init();
--------------------------------of_kset = kset_create_and_add("devicetree", NULL, firmware_kobj);
--------------------------------__of_attach_node_sysfs(np);
--------------------------------proc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base");
----------------------------platform_bus_init();
------------------------do_initcalls();
--------------------------------do_initcall_level(level, command_line);
--------------------------------arch_initcall_sync(of_platform_default_populate_init);
------------------------------------of_platform_populate(root, of_default_bus_match_table, lookup, parent); /* D:\work\source_code\msm-kernel\msm_kernel\drivers\of\platform.c */
二,Device_Tree与sysfs
在of_core_init()函数中在/sys/firmware/devicetree/base目录下面为设备树展开成sysfs的目录和二进制属性文件,所有的node节点就是一个目录,所有的property属性就是一个二进制属性文件。
1, 创建/sys/firmware 目录
int __init firmware_init(void)
{firmware_kobj = kobject_create_and_add("firmware", NULL);if (!firmware_kobj)return -ENOMEM;return 0;
}
2, of_core_init
void __init of_core_init(void)
{struct device_node *np;/* Create the kset, and register existing nodes */mutex_lock(&of_mutex);//创建/sys/firmware/devicetree目录of_kset = kset_create_and_add("devicetree", NULL, firmware_kobj);if (!of_kset) {mutex_unlock(&of_mutex);pr_err("failed to register existing nodes\n");return;}for_each_of_allnodes(np) {//为设备树展开成sysfs的目录和二进制属性文件__of_attach_node_sysfs(np);if (np->phandle && !phandle_cache[of_phandle_cache_hash(np->phandle)])phandle_cache[of_phandle_cache_hash(np->phandle)] = np;}mutex_unlock(&of_mutex);/* Symlink in /proc as required by userspace ABI */if (of_root)//创建软链接 /proc/device-tree -> /sys/firmware/devicetree/baseproc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base");
}
3, __of_attach_node_sysfs
int __of_attach_node_sysfs(struct device_node *np)
{const char *name;struct kobject *parent;struct property *pp;int rc;if (!IS_ENABLED(CONFIG_SYSFS) || !of_kset)return 0;// kobj.ksetnp->kobj.kset = of_kset;if (!np->parent) {/* Nodes without parents are new top level trees *///如果device_node的parent为空,这个device_node作为根节点,根的名字为basename = safe_name(&of_kset->kobj, "base");parent = NULL;} else {//非根节点的名字name = safe_name(&np->parent->kobj, kbasename(np->full_name));parent = &np->parent->kobj;}if (!name)return -ENOMEM;//在父亲节点的目录下,为node创建目录rc = kobject_add(&np->kobj, parent, "%s", name);kfree(name);if (rc)return rc;//为property创建二进制属性文件for_each_property_of_node(np, pp)__of_add_property_sysfs(np, pp);of_node_get(np);return 0;
}
4,__of_add_property_sysfs
int __of_add_property_sysfs(struct device_node *np, struct property *pp)
{int rc;/* Important: Don't leak passwords */bool secure = strncmp(pp->name, "security-", 9) == 0;if (!IS_ENABLED(CONFIG_SYSFS))return 0;//判断node已经被绑定 node->kobj.state_in_sysfsif (!of_kset || !of_node_is_attached(np))return 0;sysfs_bin_attr_init(&pp->attr);pp->attr.attr.name = safe_name(&np->kobj, pp->name);pp->attr.attr.mode = secure ? 0400 : 0444;pp->attr.size = secure ? 0 : pp->length;pp->attr.read = of_node_property_read;//在所属节点的目录中,为property创建二进制属性文件rc = sysfs_create_bin_file(&np->kobj, &pp->attr);WARN(rc, "error adding attribute %s to node %pOF\n", pp->name, np);return rc;
}
三,of_platform_populate执行流程
1, 整体流程
D:\work\source_code\msm-kernel\msm_kernel\drivers\of\platform.cof_platform_default_populate_init()|of_platform_default_populate();|of_platform_populate();|of_platform_bus_create()_____________________|_________________| |of_platform_device_create_pdata() of_platform_bus_create()_________________|____________________| |
of_device_alloc() of_device_add()
2, 关键代码分析
2.1,of_platform_default_populate
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);
}
EXPORT_SYMBOL_GPL(of_platform_default_populate);
2.2, of_platform_populate
/**
* of_platform_populate() - Populate platform_devices from device tree data
* @root: parent of the first level to probe or NULL for the root of the tree
* @matches: match table, NULL to use the default
* @lookup: auxdata table for matching id and platform_data with device nodes
* @parent: parent to hook devices from, NULL for toplevel
*
* Similar to of_platform_bus_probe(), this function walks the device tree
* and creates devices from nodes. It differs in that it follows the modern
* convention of requiring all device nodes to have a 'compatible' property,
* and it is suitable for creating devices which are children of the root
* node (of_platform_bus_probe will only create children of the root which
* are selected by the @matches argument).
*
* New board support should be using this function instead of
* of_platform_bus_probe().
*
* Returns 0 on success, < 0 on failure.
*/
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;//获取根节点root = root ? of_node_get(root) : of_find_node_by_path("/");if (!root)return -EINVAL;pr_debug("%s()\n", __func__);pr_debug(" starting at: %pOF\n", root);//遍历根节点下的子节点device_links_supplier_sync_state_pause();for_each_child_of_node(root, child) {//将device_node转化为platform_devicerc = of_platform_bus_create(child, matches, lookup, parent, true);if (rc) {of_node_put(child);break;}}device_links_supplier_sync_state_resume();//设置已经被转化的flagof_node_set_flag(root, OF_POPULATED_BUS);of_node_put(root);return rc;
}
EXPORT_SYMBOL_GPL(of_platform_populate);
2.3, of_platform_bus_create
/**
* of_platform_bus_create() - Create a device for a node and its children.
* @bus: device node of the bus to instantiate
* @matches: match table for bus nodes
* @lookup: auxdata table for matching id and platform_data with device nodes
* @parent: parent for new device, or NULL for top level.
* @strict: require compatible property
*
* Creates a platform_device for the provided device_node, and optionally
* recursively create devices for all the child nodes.
*/
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;/* Make sure it has a compatible property *///节点中必须有compatible属性if (strict && (!of_get_property(bus, "compatible", NULL))) {pr_debug("%s() - skipping %pOF, no compatible prop\n",__func__, bus);return 0;}/* Skip nodes for which we don't want to create devices *///在of_skipped_node_table中的节点不会被转化if (unlikely(of_match_node(of_skipped_node_table, bus))) {pr_debug("%s() - skipping %pOF node\n", __func__, bus);return 0;}//判断节点是否已经被转化if (of_node_check_flag(bus, OF_POPULATED_BUS)) {pr_debug("%s() - skipping %pOF, already populated\n",__func__, bus);return 0;}auxdata = of_dev_lookup(lookup, bus);if (auxdata) {bus_id = auxdata->name;platform_data = auxdata->platform_data;}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;}//创建,初始化,注册一个devicedev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);//当某个根子节点的compatible属性为"simple-bus"、"simple-mfd"、"isa"、"arm,amba-bus"时,当前节点中的子节点将会被转换成platform_device节点if (!dev || !of_match_node(matches, bus))return 0;//转化当前节点中的子节点for_each_child_of_node(bus, child) {pr_debug(" create child: %pOF\n", child);rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);if (rc) {of_node_put(child);break;}}//设置node已经被转化的flagof_node_set_flag(bus, OF_POPULATED_BUS);return rc;
}
2.4,of_platform_device_create_pdata
/**
* of_platform_device_create_pdata - Alloc, initialize and register an of_device
* @np: pointer to node to create device for
* @bus_id: name to assign device
* @platform_data: pointer to populate platform_data pointer with
* @parent: Linux device model parent device.
*
* Returns pointer to created platform device, or NULL if a device was not
* registered. Unavailable devices will not get registered.
*/
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;//判断设备是否可用 status = okay,并且没有被转化过if (!of_device_is_available(np) ||of_node_test_and_set_flag(np, OF_POPULATED))return NULL;//创建并初始化一个platform_devicedev = of_device_alloc(np, bus_id, parent);if (!dev)goto err_clear_flag;dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);if (!dev->dev.dma_mask)dev->dev.dma_mask = &dev->dev.coherent_dma_mask;//device的bus_typedev->dev.bus = &platform_bus_type;//device的platform_datadev->dev.platform_data = platform_data;of_msi_configure(&dev->dev, dev->dev.of_node);//会调用到device_add(&ofdev->dev),将新转化来的设备添加到设备层次结构中if (of_device_add(dev) != 0) {platform_device_put(dev);goto err_clear_flag;}return dev;err_clear_flag:of_node_clear_flag(np, OF_POPULATED);return NULL;
}
2.5,of_device_alloc
/**
* of_device_alloc - Allocate and initialize an of_device
* @np: device node to assign to device
* @bus_id: Name to assign to the device. May be null to use default name.
* @parent: Parent device.
*/
struct platform_device *of_device_alloc(struct device_node *np,const char *bus_id,struct device *parent)
{struct platform_device *dev;int rc, i, num_reg = 0, num_irq;struct resource *res, temp_res;dev = platform_device_alloc("", PLATFORM_DEVID_NONE);if (!dev)return NULL;/* count the io and irq resources *///统计reg属性的数量while (of_address_to_resource(np, num_reg, &temp_res) == 0)num_reg++;//统计中断irq属性的数量num_irq = of_irq_count(np);/* Populate the resource table *///根据num_irq和num_reg的数量申请相应struct resource内存空间if (num_irq || num_reg) {res = kcalloc(num_irq + num_reg, sizeof(*res), GFP_KERNEL);if (!res) {platform_device_put(dev);return NULL;}//设置platform_device中num_resources成员dev->num_resources = num_reg + num_irq;//设置platfrom_device中的resource成员dev->resource = res;//将device_node中的reg属性转换成platform_device中的struct resource成员for (i = 0; i < num_reg; i++, res++) {rc = of_address_to_resource(np, i, res);WARN_ON(rc);}//将device_node中的irq属性转换成platform_device中的struct resource成员if (of_irq_to_resource_table(np, res, num_irq) != num_irq)pr_debug("not all legacy IRQ resources mapped for %pOFn\n",np);}//将platform_device的dev.of_node成员指针指向device_nodedev->dev.of_node = of_node_get(np);//将platform_device的dev.fwnode成员指针指向device_node的fwnode成员dev->dev.fwnode = &np->fwnode;//设备parent为platform_bus, struct device platform_busdev->dev.parent = parent ? : &platform_bus;//构造platform device ->device 的名字if (bus_id)dev_set_name(&dev->dev, "%s", bus_id);elseof_device_make_bus_id(&dev->dev);return dev;
}
EXPORT_SYMBOL(of_device_alloc);
首先,函数先统计设备树中reg属性和中断irq属性的个数,然后分别为它们申请内存空间,链入到platform_device中的struct resources成员中。除了设备树中"reg"和"interrupt"属性之外,还有可选的"reg-names"和"interrupt-names"这些io中断资源相关的设备树节点属性也在这里被转换。
将相应的设备树节点生成的device_node节点链入到platform_device的dev.of_node中。
四,什么样的device_node会被解析成platform_device
首先,对于所有的device_node,如果要转换成platform_device,必须满足以下条件:
- 一般情况下,只对设备树中根的子节点进行转换,也就是子节点的子节点并不处理。但是存在一种特殊情况,就是当某个根子节点的compatible属性为"simple-bus"、"simple-mfd"、"isa"、"arm,amba-bus"时,当前节点中的子节点将会被转换成platform_device节点。
- 节点中必须有compatible属性。
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 */
};
五,DeviceTree Kernel API
kernel中获取dts的资源可以参考如下链接提供的API函数:
DeviceTree Kernel API — The Linux Kernel documentation
参考链接:
https://www.cnblogs.com/downey-blog/p/10486568.html