简介

参考qemu调试内核 搭建调试环境。
使用的设备树文件是

  1. arch/arm/boot/dts/vexpress-v2p-ca9.dts

关于设备树文件的选定可以参考U-boot&设备树&Linux 文章。我们从 setup_machine_fdt函数开始分析。
我选定的Linux版本为

  1. VERSION = 4
  2. PATCHLEVEL = 19
  3. SUBLEVEL = 19
  4. EXTRAVERSION =
  5. NAME = "People's Front"

190428164215.png

setup_machine_fdt

这个函数传入设备树的物理地址,通过of_flat_dt_match_machine函数查找到最合适的struct machine_desc 。 关于of_flat_dt_match_machine匹配,最终会调用到of_fdt_is_compatible函数。此函数将会通过匹配 compatible = “arm,vexpress,v2p-ca9”, “arm,vexpress”; 中的字符串来确定是否符合,得到的 score越小越好。

  1. mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);

unflatten_device_tree

遍历每一个节点,填充struct device_node 结构体。将在设备树镜像里面各个节点平面化成为立体的树结构,转化后的节点我们一般称作 设备节点(device_node)。 但是这个时候还并没有和平台设备搭上关系,所有的东西仅仅只是设备树上面描述的内容。下面一步就是需要将设备节点(device_node)转化为平台设备(platform_device)。

关于这两个设备之间的转化,要从 of_platform_default_populate_init 入手。

of_platform_default_populate_init

本段的主要目的是讲解,内核读取设备树镜像,生成对应的device_node,struct platform_device
此函数为入口。

  1. arch_initcall_sync(of_platform_default_populate_init); # 内核的初始化段,执行一次就会被释放

1 创建保留的mem node

  1. # drivers/of/platform.c
  2. static const struct of_device_id reserved_mem_matches[] = {
  3. { .compatible = "qcom,rmtfs-mem" },
  4. { .compatible = "qcom,cmd-db" },
  5. { .compatible = "ramoops" },
  6. {}
  7. };
  8. for_each_matching_node(node, reserved_mem_matches)
  9. of_platform_device_create(node, NULL, NULL);

2. 创建 “/firmware”

这些操作可以不用分析,作用不大。了解就行。

  1. node = of_find_node_by_path("/firmware");
  2. if (node) {
  3. of_platform_populate(node, NULL, NULL, NULL);
  4. of_node_put(node);
  5. }

3. 填充其他的节点

前面填充了特殊的节点,现在填充替他通用的节点。

  1. of_platform_default_populate(NULL, NULL, NULL);

of_platform_default_populate

默认的填充函数。

  1. const struct of_device_id of_default_bus_match_table[] = {
  2. { .compatible = "simple-bus", },
  3. { .compatible = "simple-mfd", },
  4. { .compatible = "isa", },
  5. {} /* Empty terminated list */
  6. };
  7. # root = NULL
  8. # lookup = NULL
  9. # parent = NULL
  10. int of_platform_default_populate(struct device_node *root,
  11. const struct of_dev_auxdata *lookup,
  12. struct device *parent)
  13. of_platform_populate(root, of_default_bus_match_table, lookup,
  14. parent);

1.Populate platform_devices from device tree data

这里就是最重要的函数,使用设备树数据填充平台设备。

  1. # 记住,这里的 matches 还是 of_default_bus_match_table 这个数组
  2. int of_platform_populate(struct device_node *root,
  3. const struct of_device_id *matches,
  4. const struct of_dev_auxdata *lookup,
  5. struct device *parent)
  6. # 找到设备树根节点
  7. root = root ? of_node_get(root) : of_find_node_by_path("/");
  8. # 遍历每一个子节点
  9. for_each_child_of_node(root, child)
  10. rc = of_platform_bus_create(child, matches, lookup, parent, true);

1.1 of_platform_bus_create

  • struct device_node 这个结构体很重要,
    1. # drivers/of/platform.c
    2. Create a device for a node and its children.
    3. of_platform_bus_create
    4. # /* Make sure it has a compatible property */
    5. of_get_property(bus, "compatible", NULL)
    6. /* Skip nodes for which we don't want to create devices */
    7. # 跳过我们不想创建的节点
    8. of_match_node(of_skipped_node_table, bus)
    9. # 检查节点是否已经填充过了
    10. of_node_check_flag(bus, OF_POPULATED_BUS)
    11. # Given a device node, lookup the preferred Linux name
    12. # 这里主要是给节点起名字,然后将节点数据读取出来
    13. auxdata = of_dev_lookup(lookup, bus);
    14. # 这个函数就要开始读取镜像里面的数据来填充数据了。
    15. dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
    16. # 递归查找下一个节点
    17. for_each_child_of_node(bus, child)
    18. rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);

1.1.1.1 of_platform_device_create_pdata

  1. # drivers/of/platform.c
  2. of_platform_device_create_pdata - Alloc, initialize and register an of_device
  3. # 此函数 Allocate and initialize an of_device
  4. struct platform_device *dev = of_device_alloc()
  5. # 设置平台设备类型
  6. dev->dev.bus = &platform_bus_type;
  7. # 填充平台设备数据
  8. dev->dev.platform_data = platform_data;

数据填充总结

of_platform_default_populate_init 函数通过初始化函数修饰作为入口, 调用of_platform_default_populate函数来填充非特殊的平台设备。
然后由of_platform_default_populate调用of_platform_populate。来创建遍历每一个root下面的子节点,通过调用of_platform_bus_create函数来创建
当前节点以及其子节点的设备。
of_platform_bus_create 函数通过 of_platform_device_create_pdata 来 初始化 struct platform_device。填充dev->dev.bus 以及 dev->dev.platform_data两个数据。
然后递归调用of_platform_bus_create
那么最重要的两个函数

  • of_platform_bus_create 递归调用自身来遍历所有的子节点
  • of_platform_bus_create 调用 of_platform_device_create_pdata 来创建 struct platform_device 并且填充数据

平台设备和驱动的匹配

前面我们讲述了平台设备的创建,最终通过of_platform_device_create_pdata函数创建了 struct platform_device并 填充了dev->dev.bus 以及 dev->dev.platform_data两个数据。
首先我们需要明确一件事,设备和驱动之间存在着一个虚拟的总线。这个总线一头挂载着设备,一头挂载着驱动。那么产生了以下的几个问题。

  1. 1. 驱动以及设备是以什么样的方式挂载到虚拟总线上面的?
  2. 2. 驱动和设备的匹配过程?

platform_driver_register

这个函数就是平台驱动的注册函数

  1. # include/linux/platform_device.h
  2. platform_driver_register
  3. __platform_driver_register
  4. # drivers/base/platform.c
  5. int __platform_driver_register(struct platform_driver *drv,
  6. struct module *owner)
  7. drv->driver.owner = owner;
  8. drv->driver.bus = &platform_bus_type;
  9. # 最重要的就是这个 platform_drv_probe 函数
  10. drv->driver.probe = platform_drv_probe;
  11. drv->driver.remove = platform_drv_remove;
  12. drv->driver.shutdown = platform_drv_shutdown;
  13. # 然后调用驱动注册函数
  14. driver_register(&drv->driver);

platform_driver_register

参考资料

内核对设备树的操作

gtags 搭建搜索目录