设备树替换了平台总线模型当中对硬件资源描述的device部分。所以设备树也是对硬件资源进行描述的文件。
在平台总线模型中,device部分是用platform_device结构体来描述硬件资源的。所以内核最终会将内核认识的device_node树转换platform_device。
但是并不是所有的节点都会被转换成platform_device,只有满足要求的才会转换成platform_device,
转换成platformdevice的节点可以在/sys/bus/platform/devices下查看。
节点要满足什么要求才会被转换成platform_device呢?
转换规则
1.根节点下包含compatible属性的子节点。
2.节点中compatible属性包含simple-bus,simple-mfd,isa其中之一的节点下包含compatible属性的子节。
3.如果节点的compatible属性包含arm,primecell值,则对应的节点会被转换成不会被转换成amba设各。不会被转换成platform_device。
主要核心函数
of_default_bus_match_table
const struct of_device_id of_default_bus_match_table[] = { { .compatible = "simple-bus", }, { .compatible = "simple-mfd", }, #ifdef CONFIG_ARM_AMBA { .compatible = "arm,amba-bus", }, #endif /* CONFIG_ARM_AMBA */ {} /* Empty terminated list */ };
arm
static int __init customize_machine(void) { /* * customizes platform devices, or adds new ones * On DT based machines, we fall back to populating the * machine from the device tree, if no callback is provided, * otherwise we would always need an init_machine callback. */ of_iommu_init(); if (machine_desc->init_machine) machine_desc->init_machine(); #ifdef CONFIG_OF else of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); #endif return 0; } arch_initcall(customize_machine);
arm64
在内核启动的时候会执行of_platform_default_populate_init函数,这个函数是用arch_initcall_sync来修饰的。
arch_initcall_sync(of_platform_default_populate_init);
所以系统启动的时候会调用of_platform_default_populate_init函数,然后调用一系列函数进行转换,转换流程如下,
arch/arm64/kernel/setup.c
static int __init arm64_device_init(void) { of_iommu_init(); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); return 0; } arch_initcall_sync(arm64_device_init);
of_platform_register_reconfig_notifier
int __init platform_bus_init(void) { int error; early_platform_cleanup(); error = device_register(&platform_bus); if (error) return error; error = bus_register(&platform_bus_type); if (error) device_unregister(&platform_bus); of_platform_register_reconfig_notifier(); return error; }
of_platform_notify函数是Linux内核中的一个通知机制,主要作用是把动态加载的设备树的节点解析出来,以便在驱动模型的基础上构建实际的设备驱动。在platform总线上的控制器模块都是挂在这条总线上,而of_platform_notify函数则是把这些控制器模块挂载到内核中。
在这个函数中,当动态加载一个设备树节点时,会先检查其父节点是否为一个总线,如果不是则返回NOTIFY_OK;如果已经被挂载,则返回NOTIFY_OK;否则会创建一个platform_device并将其挂载到内核中。当卸载一个设备树节点时,会先检查其是否已经被卸载,如果没有则会找到该设备并将其从内核中卸载。
static int of_platform_notify(struct notifier_block *nb, unsigned long action, void *arg) { struct of_reconfig_data *rd = arg; struct platform_device *pdev_parent, *pdev; bool children_left; switch (of_reconfig_get_state_change(action, rd)) { case OF_RECONFIG_CHANGE_ADD: /* verify that the parent is a bus */ if (!of_node_check_flag(rd->dn->parent, OF_POPULATED_BUS)) return NOTIFY_OK; /* not for us */ /* already populated? (driver using of_populate manually) */ if (of_node_check_flag(rd->dn, OF_POPULATED)) return NOTIFY_OK; /* pdev_parent may be NULL when no bus platform device */ pdev_parent = of_find_device_by_node(rd->dn->parent); pdev = of_platform_device_create(rd->dn, NULL, pdev_parent ? &pdev_parent->dev : NULL); of_dev_put(pdev_parent); if (pdev == NULL) { pr_err("%s: failed to create for '%s'\n", __func__, rd->dn->full_name); /* of_platform_device_create tosses the error code */ return notifier_from_errno(-EINVAL); } break; case OF_RECONFIG_CHANGE_REMOVE: /* already depopulated? */ if (!of_node_check_flag(rd->dn, OF_POPULATED)) return NOTIFY_OK; /* find our device by node */ pdev = of_find_device_by_node(rd->dn); if (pdev == NULL) return NOTIFY_OK; /* no? not meant for us */ /* unregister takes one ref away */ of_platform_device_destroy(&pdev->dev, &children_left); /* and put the reference of the find */ of_dev_put(pdev); break; } return NOTIFY_OK; } static struct notifier_block platform_of_notifier = { .notifier_call = of_platform_notify, }; void of_platform_register_reconfig_notifier(void) { WARN_ON(of_reconfig_notifier_register(&platform_of_notifier)); } #endif /* CONFIG_OF_DYNAMIC */ #endif /* CONFIG_OF_ADDRESS */
Linux内核是如何将device_node转换成platform_device
of_platform_populate函数
该函数主要调用了of_platform_populate函数
drivers/of/platform.c
of_platform_populate函数是Linux内核中的一个函数,主要作用是将设备树中的device node创建成platform device,为后续和各类驱动的platform driver匹配做准备。在Linux内核启动时,内核通过of_platform_populate函数,将dts中的device node创建成platform device。
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)
其中,root是第一级要探测的父节点或NULL表示树的根;matches是匹配表,NULL表示使用默认值;lookup是用于匹配id和platform_data与设备节点的auxdata表;parent是要挂载设备的父节点,NULL表示顶层。详细内容如下:
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; for_each_child_of_node(root, child) { rc = of_platform_bus_create(child, matches, lookup, parent, true); if (rc) break; } of_node_set_flag(root, OF_POPULATED_BUS); of_node_put(root); return rc; } of_platform_populate of_platform_bus_create 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 */ 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); 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; } dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent); if (!dev || !of_match_node(matches, bus)) return 0; 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; } } of_node_set_flag(bus, OF_POPULATED_BUS); return rc; }
转换规则1
if (strict && (!of_get_property(bus, “compatible”, NULL))) {
pr_debug(“%s() - skipping %s, no compatible prop\n”,
func, bus->full_name);
return 0;
}
转换规则3
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; }
处理根节点下的子节点(匹配规则2)
dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
if (!dev || !of_match_node(matches, bus))
return 0;
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;
}
}
of_node_set_flag(bus, OF_POPULATED_BUS);
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 */ 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); 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; } dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent); if (!dev || !of_match_node(matches, bus)) return 0; 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; } } of_node_set_flag(bus, OF_POPULATED_BUS); return rc; }
设备树转换成paltform_device以后就和平台总线模型一样啦。所以驱动和设备也会根据名字来匹配。匹配成功以后会执行驱动中的probe函数。
匹配优先级:name<id_table<of_match_table