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

  • 16
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值