Linux驱动 2026年6月11日 24 分钟

arch_initcall何时被调用及device_node何时加入platform总线

arch_initcall 何时被调用及 device_node 何时加入 platform 总线 问题一:arch_i…

arch_initcall 何时被调用及 device_node 何时加入 platform 总线

问题一:arch_initcall 何时被调用?

结论

arch_initcall 是在 start_kernel()rest_init()kernel_init()kernel_init_freeable()do_basic_setup()do_initcalls() 的链路中被调用的。

此时:

CPU 已初始化
内存管理已就绪
调度器已启动
设备树已经解析成 device_node 树
用户空间还未启动

调用链路

start_kernel()
    │
    │  ← Linux 内核入口
    │
    ├── lockdep_init()
    ├── setup_arch(&command_line)
    │     ↑
    │     │  ← DTB 在这里被 setup_machine_fdt() 解析
    │     │  ← unflatten_device_tree() 在这里执行
    │     │  ← of_root 指向设备树根节点
    │     │
    ├── sched_init()
    ├── page_alloc_init()
    │     ...
    │
    └── rest_init()
         │
         ├── kernel_thread(kernel_init, ...)   // 创建 1 号进程(init 进程)
         └── kernel_thread(kthreadd, ...)    // 创建 kthreadd 进程
              │
              ▼
         kernel_init_freeable()
              │
              ├── smp_prepare_cpus()
              ├── do_pre_smp_initcalls()       // 0~3 级 early initcall
              ├── smp_init()
              ├── sched_init_smp()
              │
              └── do_basic_setup()            ← arch_initcall 在这里
                   │
                   ├── cpuset_init_smp()
                   ├── usermodehelper_init()
                   ├── shmem_init()
                   ├── driver_init()           // 驱动子系统初始化
                   ├── init_irq_proc()
                   ├── do_ctors()
                   ├── usermodehelper_enable()
                   │
                   └── do_initcalls()         ← arch_initcall 在这里
                        │
                        ├── do_initcall_level(0)  // early
                        ├── do_initcall_level(1)  // core
                        ├── do_initcall_level(2)  // postcore
                        ├── do_initcall_level(3)  // arch         ← customize_machine()
                        ├── do_initcall_level(4)  // subsys
                        ├── do_initcall_level(5)  // fs
                        ├── do_initcall_level(6)  // device
                        └── do_initcall_level(7)  // late

arch_initcall 的定义

在 ARM 架构下(i.MX6ULL 是 ARM),定义在:

include/linux/init.h:218

#define arch_initcall(fn)        __define_initcall(fn, 3)

数字 3 表示它是第 3 级 initcall,对应 arch 这一级。

所有 initcall 级别从低到高:

级别名称数字典型用途
0early0最早期初始化,如 lockdep、kmem_cache_init_pre_early
1core1核心子系统初始化
2postcore2post-core 初始化
3arch3架构相关初始化,如 customize_machine
4subsys4子系统初始化
5fs5文件系统初始化
6device6设备驱动初始化
7late7最后阶段初始化

arch_initcall 执行的源码对照

所有同级别的 initcall 函数通过链接脚本排在一起,执行入口在:

init/main.c:842-855

static void __init do_initcall_level(int level)
{
    initcall_t *fn;

    for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
        do_one_initcall(*fn);
}

do_initcalls() 遍历所有级别:

init/main.c:857-862

static void __init do_initcalls(void)
{
    int level;

    for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
        do_initcall_level(level);
}

所以当 level = 3 时,就会执行 arch_initcall(customize_machine)


关键:此时 DTB 已经解析好了

在调用 arch_initcall 之前,start_kernel() 已经执行过:

init/main.c:523

setup_arch(&command_line);

setup_arch() 在 ARM 上会做:

setup_processor()
    ↓
setup_machine_fdt(__atags_pointer)
    ↓
early_init_dt_scan_nodes()
    ↓
unflatten_device_tree()
    ↓
of_root 指向设备树根节点
    ↓
arm_memblock_init()
    ↓
paging_init()

所以当 arch_initcall(customize_machine) 执行时:

struct device_node 树已经准备好了
    ↓
of_platform_populate() 可以开始扫描

问题二:device_node 何时被添加到 platform 总线?

结论

是在 of_platform_device_create_pdata() 调用 of_device_add() 时注册的。

也就是在 arch_initcall(customize_machine)of_platform_populate()of_platform_bus_create()of_platform_device_create_pdata() 链路中被添加到 platform 总线的。


添加到 platform 总线的具体位置

关键代码在:

drivers/of/platform.c:186-188

dev->dev.bus = &platform_bus_type;
dev->dev.platform_data = platform_data;
of_dma_configure(&dev->dev, dev->dev.of_node);

if (of_device_add(dev) != 0)          // ← 关键:这里注册到平台总线
    goto err_clear_flag;

然后 of_device_add() 内部会调用 Linux 设备模型核心:

of_device_add()
    ↓
device_add()
    ↓
bus_add_device()
    ↓
platform_bus_type.devices 链表里加入这个 device
    ↓
bus_probe_device()
    ↓
device_attach()
    ↓
遍历 platform_bus 上已注册的 driver
    ↓
如果 driver_match_device() 返回成功
    ↓
调用 really_probe()
    ↓
pinctrl_bind_pins()
    ↓
driver->probe()

完整时间点

内核启动
    │
    ▼
start_kernel()
    │
    ▼
rest_init()
    │
    ▼
kernel_init()
    │
    ▼
kernel_init_freeable()
    │
    ▼
do_basic_setup()
    │
    ▼
do_initcalls()
    │
    ▼
do_initcall_level(3)  ← arch_initcall 级别
    │
    ▼
customize_machine()     ← arch_initcall(customize_machine)
    │
    ▼
of_platform_populate() ← 从 of_root 开始扫描
    │
    ▼
of_platform_bus_create()
    │
    ▼
of_platform_device_create_pdata()
    │     ├── 检查 status(of_device_is_available())
    │     ├── 检查 OF_POPULATED 标志(防止重复)
    │     ├── 分配 platform_device(platform_device_alloc())
    │     ├── of_device_alloc()
    │     │     ├── 转换 reg → IORESOURCE_MEM
    │     │     ├── 转换 interrupts → IORESOURCE_IRQ
    │     │     └── dev->dev.of_node = np
    │     │
    │     ├── dev->dev.bus = &platform_bus_type
    │     │
    │     └── of_device_add()    ← 关键:此时加入 platform 总线
    │           │
    │           ├── device_add()
    │           ├── bus_add_device()  // 加到 platform_bus 的 devices 链表
    │           └── bus_probe_device()
    │                 │
    │                 └── 如果 driver 已注册,立即触发 probe
    │
    ▼
platform_device 已在 platform_bus 上

添加到总线后的后续动作

of_device_add() 执行后,设备已经在 platform 总线上。

此时分两种情况:

情况一:driver 已经注册

如果对应的 platform_driver 已经通过 driver_register() 注册到 platform 总线,则立即触发匹配:

device_attach()
    ↓
driver_match_device()  ← platform_match()
    ↓
really_probe()
    ↓
pinctrl_bind_pins()   ← 自动配置引脚复用
    ↓
driver->probe()        ← 你的驱动 probe 被调用

情况二:driver 还没注册

如果驱动还是模块,还没加载,则设备会在总线上等待。

等驱动通过 module_init()platform_driver_register() 注册上来后,才会触发匹配和 probe()


重要区分:添加总线 vs 调用 probe

这是两个不同的时机:

事件发生位置说明
platform_device 添加到 platform 总线of_device_add()设备已注册,可以被 driver 匹配
probe() 被调用really_probe()driver 和 device 匹配成功后才调用

大多数设备树驱动的 probe() 不是在 of_platform_populate() 时立即触发的,而是:

设备树节点 → platform_device → 在总线上等待
    ↓
驱动模块加载 → driver_register()
    ↓
driver 和 device 匹配
    ↓
probe() 被调用

一句话总结

  1. arch_initcall(customize_machine) 是在内核启动的 do_basic_setup()do_initcalls() 链路中执行的,此时设备树已经解析成 device_node 树。


  2. device_node 变成 platform_device 后,在 of_platform_device_create_pdata()of_device_add() 时注册到 platform 总线。注册后如果对应驱动已加载,立即触发 probe();如果驱动是模块还没加载,设备在总线上等待驱动注册后匹配。



相关源码文件

文件作用
init/main.c:842do_initcall_level() 执行各级别 initcall
init/main.c:857do_initcalls() 遍历所有 initcall 级别
arch/arm/kernel/setup.c:820customize_machine() 注册 arch_initcall
drivers/of/platform.c:186of_device_add() 将 platform_device 注册到总线
drivers/of/platform.c:435of_platform_populate() 扫描设备树创建设备
drivers/base/platform.c:820platform_match() 驱动匹配函数
drivers/base/dd.c:278really_probe() 真正调用驱动的 probe
上一篇 Linux内核对象模型kobject-kset-sysfs-uevent详解

Linux内核对象模型kobject-kset-sysfs-uevent详解 1. 为什么 Linux 内核需要对象模型...

下一篇 GPIO子系统驱动分析

子系统层次图 接口实现地址 2. 重要的3个核心数据结构 记住GPIO Controller的要素,这有助于理解它的驱动...