device_node转换成platform_device

简介: device_node转换成platform_device

设备树替换了平台总线模型当中对硬件资源描述的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


目录
相关文章
|
2月前
ARM64技术 —— Device Memory Type
ARM64技术 —— Device Memory Type
|
3月前
|
TensorFlow 算法框架/工具 异构计算
解决No registered ‘MultiDeviceIteratorGetNextFromShard‘ OpKernel for GPU devices compatible with node
在使用TensorFlow 1.15版本进行多GPU分布式训练时遇到的"No registered 'MultiDeviceIteratorGetNextFromShard' OpKernel for GPU devices"错误,并提供了通过降级TensorFlow到1.14.0版本来解决此问题的方法。
38 1
|
Linux
dtb展开成device_node
dtb展开成device_node
176 0
mode9-node的系统模块fs
mode9-node的系统模块fs
88 0
mode9-node的系统模块fs
|
内存技术
嵌入式 VFS: Cannot open root device "mtdblock2" or unknown-block(2,0)
系统启动后,虽然nand驱动表现正常,但是最后挂载rootfs时候出错: Kernel command line: root=/dev/mtdblock2 rw init=/linuxrc console=ttyAMA1,115200 mem=64M rootfstype=yaffs2。
2258 0
|
TensorFlow 算法框架/工具 异构计算
成功解决 gpu_device.cc:1120] Creating TensorFlow device (/device:GPU:0) -> (device: 0, name: GeForce 94
成功解决 gpu_device.cc:1120] Creating TensorFlow device (/device:GPU:0) -> (device: 0, name: GeForce 94
|
Oracle IDE 关系型数据库
How to Dynamically Add and Remove SCSI Devices on Linux (文档 ID 603868.1)
How to Dynamically Add and Remove SCSI Devices on Linux (文档 ID 603868.1)
2003 0