Linux ·

基于Linux 3.10.49内核从dts文件里注册platform_device流程分析

Linux kernel 3.10.49+
在这里, 我们说说Linux 是怎么通过dts进行设备(platform_device)注册和初始化板载信息.
在arch/arm/mach-******/******.c找到DT_MACHINE_START 和 MACHINE_END 宏, 如下:
DT_MACHINE_START(******_DT, "************* SoC (Flattened Device Tree)")
    .atag_offset    = 0x100,
    .dt_compat    = ******_dt_compat,                // 匹配dts
    .map_io        = ******_map_io,                    // 板级地址内存映射, linux mmu
    .init_irq    = irqchip_init,                    // 板级中断初始化.
    .init_time    = ******_timer_and_clk_init,        // 板级时钟初始化,如ahb,apb等
    .init_machine  = ******_dt_init,              // 这里是解析dts文件入口.
    .restart    = ******_restart,                  // 重启, 看门狗寄存器相关可以在这里设置
MACHINE_END

其中.dt_compat    = ******_dt_compat 这个结构体是匹配是哪个dts文件, 如:
static const char * const ******_dt_compat[] = {
    "******,******-soc",
    NULL
 };
这个"******,******-soc" 字符串可以在我们的dts的根节点下可以找到.

好了, 我们来看看 init_machine  = ******_dt_init 这个回调函数.
1. arch/arm/mach-******/******.c : void __init ******_dt_init(void)
    ******_dt_init(void) --> of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
    of_default_bus_match_table 这个是struct of_device_id的全局变量.
    const struct of_device_id of_default_bus_match_table[] = {
        { .compatible = "simple-bus", },
    #ifdef CONFIG_ARM_AMBA
        { .compatible = "arm,amba-bus", },
    #endif /* CONFIG_ARM_AMBA */
        {} /* Empty terminated list */
    };
    我们设计dts时, 把一些需要指定寄存器基地址的设备放到以compatible = "simple-bus"为匹配项的设备节点下. 下面会有介绍为什么.

2. drivers/of/platform.c : int of_platform_populate(...)
    of_platform_populate(...) --> of_platform_bus_create(...)
    // 在这之前, 会有of_get_property(bus, "compatible", NULL)
    // 检查是否有compatible, 如果没有, 返回, 继续下一个, 也就是说没有compatible, 这个设备不会被注册
    for_each_child_of_node(root, child) {
        printk("[%s %s %d] child->name = %s, child->full_name = %s\n", __FILE__, __func__, __LINE__, child->name, child->full_name);
        rc = of_platform_bus_create(child, matches, lookup, parent, true);
        if (rc)
            break;
    }
    论询dts根节点下的子设备, 每个子设备都要of_platform_bus_create(...);
    全部完成后, 通过 of_node_put(root); 释放根节点, 因为已经处理完毕;

3. drivers/of/platform.c : of_platform_bus_create(bus, ...)
    dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent); // 我们跳到 3-1-1步去运行
    if (!dev || !of_match_node(matches, bus))  // 就是匹配
                                              // dt_compat    = ******_dt_compat, 也就是 compatible = "simple-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);  // dev->dev以本节点为父节点,  我们跳到 3-2-1步去运行
        if (rc) {
            of_node_put(child);
            break;
        }
    }

 3-1-1. drivers/of/platform.c : of_platform_device_create_pdata(...)
    if (!of_device_is_available(np))  // 查看节点是否有效, 如果节点有'status'属性, 必须是okay或者是ok, 才是有效, 没有'status'属性, 也有效
        return NULL;

    dev = of_device_alloc(np, bus_id, parent);  // alloc设备, 设备初始化. 返回dev, 所有的设备都可认为是platform_device, 跳到3-1-1-1看看函数做了什么事情
    if (!dev)
        return NULL;

    #if defined(CONFIG_MICROBLAZE)
        dev->archdata.dma_mask = 0xffffffffUL;
    #endif
        dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); // dev->dev 是 struct device. 继续初始化
        dev->dev.bus = &platform_bus_type;    //
        dev->dev.platform_data = platform_data;

    printk("[%s %s %d] of_device_add(device register) np->name = %s\n", __FILE__, __func__, __LINE__, np->name);
    if (of_device_add(dev) != 0) {      // 注册device, of_device_add(...) --> device_add(...) // This is part 2 of device_register()
        platform_device_put(dev);
        return NULL;
    }

 3-1-1-1. drivers/of/platform.c : of_device_alloc(...)
    1) alloc platform_device *dev
    2) 如果有reg和interrupts的相关属性, 运行of_address_to_resource 和 of_irq_to_resource_table, 加入到dev->resource
        dev->num_resources = num_reg + num_irq;
        dev->resource = res;
        for (i = 0; i < num_reg; i++, res++) {
            rc = of_address_to_resource(np, i, res);
            /* printk("[%s %s %d] res->name = %s, res->start = 0x%X, res->end = 0x%X\n", __FILE__, __func__, __LINE__, res->name, res->start, res->end); */
            WARN_ON(rc);
        }
        WARN_ON(of_irq_to_resource_table(np, res, num_irq) != num_irq);
   
    3) dev->dev.of_node = of_node_get(np); 
        // 这个node属性里有compatible属性, 这个属性从dts来, 后续driver匹配device时, 就是通过这一属性进匹配
        // 我们可以通过添加下面一句话来查看compatible.
        // printk("[%s %s %d] bus->name = %s, of_get_property(...) = %s\n", __FILE__, __func__, __LINE__, np->name, (char*)of_get_property(np, "compatible", NULL));
        // node 再给dev, 后续给驱动注册使用.
    4) 运行 of_device_make_bus_id 设定device的名字, 如: soc.2 或 ac000000.serial 等

3-2-1. drivers/of/platform.c :
    以 compatible = "simple-bus"的节点的子节点都会以这个节点作为父节点在这步注册设备.
    这是实际的板载设备, 也是最终目的.

下一篇分析,我们来讲讲platform_driver的注册, 是怎么匹配刚才我们注册过的platform_device的

参与评论