欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于Mips架构linux下设备树解析(三)

程序员文章站 2022-07-13 21:44:55
...

device node转换platform device

本设备树解析基于linux3.0.4内核版本

device_initcall()

从device_initcall()开始,我们分析追踪设备树device node 文件转换platform device流程,有关于device_initcall()内核初始化优先级加载机制,我们在此不进行赘述,只贴函数原型如下,函数路径见include/linux/init.h。

#define pure_initcall(fn)       __define_initcall("0",fn,0)
#define core_initcall(fn)       __define_initcall("1",fn,1)
#define core_initcall_sync(fn)      __define_initcall("1s",fn,1s)
#define postcore_initcall(fn)       __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn)  __define_initcall("2s",fn,2s)
#define arch_initcall(fn)       __define_initcall("3",fn,3)
#define arch_initcall_sync(fn)      __define_initcall("3s",fn,3s)
#define subsys_initcall(fn)     __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn)    __define_initcall("4s",fn,4s)
#define fs_initcall(fn)         __define_initcall("5",fn,5)
#define fs_initcall_sync(fn)        __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn)     __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn)     __define_initcall("6",fn,6)
#define device_initcall_sync(fn)    __define_initcall("6s",fn,6s)
#define late_initcall(fn)       __define_initcall("7",fn,7)
#define late_initcall_sync(fn)      __define_initcall("7s",fn,7s)

从上面可以看到我们当前内核设备树总线初始化加载优先级处于第六级,函数原型如下,函数路径见./arch/mips/xxx/common/setup.c,通过匹配设备树平台总线名称compatible = “simple-bus”,初始化完成结构体struct device node向结构体struct platform device转换

static struct of_device_id __initdata xxx_ids[] = {
        { .compatible = "simple-bus", },
        {},
};
int __init xxx_publish_devices(void)
{
    return of_platform_populate(NULL, xxx_ids, NULL, NULL);
}
device_initcall(xxx_publish_devices);

of_platform_populate()

继续追踪函数of_platform_populate(),函数原型如下,函数路径见./drivers/of/platform.c,通过上面调用可以看到传入了四个参数,第一个为root节点,为NULL,第二个参数为matches,为of_device_id静态存储数组名字,第三个参数与第四个参数均为NULL,结合下面我们贴的函数原型可以看出,该函数第一步对是否存在root节点判断,如果不为空,则从根文件路径查起。第二步通过查找matches,遍历根节点下所有子节点,第三步of_platform_bus_create()就是我们本文重点分析的struct device node转换struct platform device函数接口,第四步转换完成后将该节点记录到节点列表里。

int of_platform_populate(struct device_node *root,
            const struct of_device_id *matches,
            const struct of_dev_auxdata *lookup,
            struct device *parent)
{
 .................
    root = root ? of_node_get(root) : of_find_node_by_path("/");
 .................
    for_each_child_of_node(root, child) {
        rc = of_platform_bus_create(child, matches, lookup, parent, true);
        if (rc)
            break;
    }
    of_node_put(root);
 .................
}

of_platform_bus_create()

继续追踪of_platform_bus_create()函数,函数原型如下,函数路径见
/drivers/of/platform.c,追踪到这里我们又看到熟悉的设备树解析接口函数,第一步进行名字匹配of_get_property(),,第二步对platform device进行转换of_platform_device_create_pdata(),第三步of_match_node()函数进行校验,第四步将转换好的节点创建。

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)
{
 .................
    if (strict && (!of_get_property(bus, "compatible", NULL))) {
        pr_debug("%s() - skipping %s, no compatible prop\n",
             __func__, bus->full_name);
        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;
        }
    }
    return rc;
}

of_platform_device_create_pdata()

继续追踪函数of_platform_device_create_pdata()实现,函数原型如下,函数路径见/drivers/of/platform.c,终于追到struct platform_device结构体,结合下面函数实现,可以看出,第一步进行节点校验,确定是否存在。第二步对该节点申请空间。第三步初始化platform_bus_type与platform_data,第四步将绑定好的数据添加到平台设备里面。至此device node转换platform device流程解析已经完成了。接下来,我们将重点关注platform_bus_type,因为与我们设备树驱动成员变量of_match息息相关。

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;
    if (!of_device_is_available(np))
        return NULL;
    dev = of_device_alloc(np, bus_id, parent);
    if (!dev)
        return NULL;
    dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
    dev->dev.bus = &platform_bus_type;
    dev->dev.platform_data = platform_data;
    if (of_device_add(dev) != 0) {
        platform_device_put(dev);
        return NULL;
    }
    return dev;
}

platform_bus_type

继续追踪platform_bus_type,结构体初始化赋值如下,该结构体定义路径见
./drivers/base/platform.c,结构体内可以看到我们熟悉的成员变量.match = platform_match,platform_match()函数就是各个外设驱动调用得底层接口实现,追踪到这里层次越来越清晰。

struct bus_type platform_bus_type = {
    .name       = "platform",
    .dev_attrs  = platform_dev_attrs,
    .match      = platform_match,
    .uevent     = platform_uevent,
    .pm     = &platform_dev_pm_ops,
};
EXPORT_SYMBOL_GPL(platform_bus_type);

补充一下platform bus初始化函数,同样也是在./drivers/base/platform.c,在此不赘述了,只贴函数原型如下

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);
    return error;
}

platform_match()

现在我们回到platform_match()函数,追踪下它的函数实体,函数原型如下,函数路径见./drivers/base/platform.c,在这里对设备树dtb文件内节点名字与驱动名字做了校验匹配。

static int platform_match(struct device *dev, struct device_driver *drv)
{
    struct platform_device *pdev = to_platform_device(dev);
    struct platform_driver *pdrv = to_platform_driver(drv);
    /* Attempt an OF style match first */
    if (of_driver_match_device(dev, drv))
        return 1;
    /* Then try to match against the id table */
    if (pdrv->id_table)
        return platform_match_id(pdrv->id_table, pdev) != NULL;
    /* fall-back to driver name match */
    return (strcmp(pdev->name, drv->name) == 0);
}

of_driver_match_device()

继续追踪函数of_driver_match_device()实体,函数原型如下,函数路径见
./include/linux/of_device.h,可以看到只是简单封装。

static inline int of_driver_match_device(struct device *dev,
                     const struct device_driver *drv)
{
    return of_match_device(drv->of_match_table, dev) != NULL;
}

of_match_device()

继续追踪of_match_device()函数实体,函数原型如下,函数路径见
./drivers/of/device.c,这里我们终于又见到了我们熟悉的设备树解析接口函数of_match_node(),第一步我们对传入的设备树驱动of match成员变量与转换后platform device内的of_node节点是否存在做了判断校验,第二步才是调用of_match_node()去匹配。至此平台总线设备树驱动流程分析完成。

const struct of_device_id *of_match_device(const struct of_device_id *matches,
                       const struct device *dev)
{
    if ((!matches) || (!dev->of_node))
        return NULL;
    return of_match_node(matches, dev->of_node);
}
EXPORT_SYMBOL(of_match_device);

小结:本文总计两个部分,其中第一部分为设备树节点device node转换platform device,第二部分为补充部分,为设备树驱动匹配流程。
原创文章,转载请注明此处!

相关标签: 内核 linux