Device Tree (四) - device_node -> platform_device
一,调用到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/base
proc_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.kset
np->kobj.kset = of_kset;
if (!np->parent) {
/* Nodes without parents are new top level trees */
//如果device_node的parent为空,这个device_node作为根节点,根的名字为base
name = 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_sysfs
if (!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.c
of_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_device
rc = of_platform_bus_create(child, matches, lookup, parent, true);
if (rc) {
of_node_put(child);
break;
}
}
device_links_supplier_sync_state_resume();
//设置已经被转化的flag
of_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;
}
//创建,初始化,注册一个device
dev = 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已经被转化的flag
of_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_device
dev = 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_type
dev->dev.bus = &platform_bus_type;
//device的platform_data
dev->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_node
dev->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_bus
dev->dev.parent = parent ? : &platform_bus;
//构造platform device ->device 的名字
if (bus_id)
dev_set_name(&dev->dev, "%s", bus_id);
else
of_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