Linux SPI设备如何从Device Tree走到driver probe

1 为什么SPI设备需要DTS描述

对于 USB 设备、PCIe 设备,你插上去,内核就能自动识别。因为总线本身有设备枚举机制。USB 设备插上后,主机控制器会发起枚举请求,设备返回自己的 Vendor ID、Product ID、Class 等信息,内核据此动态创建 struct device 并匹配驱动。但 SPI 总线天生不具备这个能力。

SPI 协议只定义了 MOSI、MISO、SCLK、CS 四根线(或更少),没有地址线,没有标准的设备 ID 寄存器,也没有统一的枚举命令。也就是说:
SPI 从设备是被动的、沉默的。主机不发命令,它不会自我介绍。

假设SoC 内部集成了 4 个 SPI 控制器(SPI0 ~ SPI3)。但具体外接什么设备,完全由板级设计决定:

  • 板子 A:SPI0 上挂了一颗 IMU 惯性测量单元
  • 板子 B:SPI0 上挂了一颗 CAN 控制器
  • 板子 C:SPI1 上挂了一颗 音频 DAC Codec

三块板子都用同一颗 Soc,但 SPI 总线上的从设备完全不同。

如果把这些设备信息”硬编码”在内核里,那就得给每块板子单独编译一个内核这明显不可行,也不符合 Linux 内核 Image + 设备树分离设计的哲学。

Linux内核中使用**Device Tree(设备树,简称 DT)**描述硬件清单,由Bootloader传给内核,内核启动时解析。

2 SPI设备树节点结构

假设一款Soc的SPI节点如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
spi0: spi@fe610000 {

compatible = "rockchip,rk3568-spi";

reg = <0xfe610000 0x1000>;

interrupts = <GIC_SPI 200 IRQ_TYPE_LEVEL_HIGH>;

status = "okay";


imu@0 {

compatible = "xxx,imu";

reg = <0>;

spi-max-frequency = <10000000>;

};
};

我们看一下每一步分表示什么

  • spi0: spi@fe610000对应:struct spi_controller
  • imu@0:对应struct spi_device
  • compatible:用于驱动匹配
  • reg = <0>:片选编号
  • spi-max-frequency:最大时钟频率

3 内核启动中SPI初始化

Linux内核启动的时候有个start_kernel函数,负责一系列初始化的工作。
start_kernel不断向下调用,最后会调用一个driver_init函数,用于初始化驱动模型。
让我们来看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void __init driver_init(void)
{
// 内核设备驱动的基础设施
bdi_init(&noop_backing_dev_info); // 初始化后备存储设备信息(文件系统相关)
devtmpfs_init(); // 初始化 devtmpfs(/dev 设备文件系统)
devices_init(); // 初始化全局设备链表(devices_kset)
buses_init(); // 初始化总线子系统(bus_kset)
classes_init(); // 初始化设备类子系统(class_kset)
firmware_init(); // 初始化 firmware 加载机制
hypervisor_init(); // 初始化虚拟化平台(hypervisor)总线

// 初始化总线类型和设备树体系
of_core_init(); // ★ 初始化 Device Tree 内核框架
platform_bus_init(); // ★ 注册 platform 总线
auxiliary_bus_init(); // 辅助总线(新版内核用于具体子系统)
memory_dev_init(); // 内存设备初始化(/sys/devices/system/memory)
node_dev_init(); // 拓扑节点初始化(/sys/devices/system/node)
cpu_dev_init(); // 启动 CPU 设备
container_dev_init(); // 注册容器总线(container bus)
}

主要由两个部分组成,一部分是内核设备驱动的基础,一部分是总线类型和设备树。

我们看一下SPI总线对应的代码

1
2
3
4
5
6
7
static int __init spi_init(void)
{
....
status = bus_register(&spi_bus_type);
}

postcore_initcall(spi_init);

上面会注册SPI总线,此时会出现/sys/bus/spi/。
drivers/spi/spi.c文件的末尾,SPI Core 使用 initcall 机制注册初始化函数。

整个调用过程如下

4 spi_controller如何创建

上面的DTS中,spi@fe610000就是一个SPI控制器,他是怎么被创建的呢?
首先DTS里面的内容被解析为device_node,生成platform_device,匹配具体的Soc的SPI控制器
(比如rockchip的),调用probe函数,使用spi_register_controller创建并注册spi_controller。流程如下

比如我们以drivers/spi/spi-rockchip.c为例子

1
2
3
4
5
6
static int rockchip_spi_probe(struct platform_device *pdev) {

ctlr = devm_spi_alloc_master();
...
spi_register_controller(ctlr);
}

spi_register_controller的具体作用如下
源码位置:drivers/spi/spi.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
int spi_register_controller(struct spi_controller *ctlr)
{
struct device *dev = ctlr->dev.parent;

// 参数校验和 bus_num 分配
status = spi_controller_check_ops(ctlr); // 检查必要的回调函数是否实现
if (ctlr->bus_num < 0)
ctlr->bus_num = of_alias_get_id(...); // 从设备树别名中获取编号

// 初始化锁和完成量
init_completion(&ctlr->xfer_completion);
if (!ctlr->max_dma_len)
ctlr->max_dma_len = INT_MAX;

// 注册设备到设备模型,名称如 "spi0"
dev_set_name(&ctlr->dev, "spi%u", ctlr->bus_num);

// 初始化 GPIO 片选(如果使用)
if (ctlr->use_gpio_descriptors)
spi_get_gpio_descs(ctlr);

// 将 device 正式注册到内核,之后用户空间可见
status = device_add(&ctlr->dev);

// 初始化排队传输队列
if (ctlr->transfer_one || ctlr->transfer_one_message)
spi_controller_initialize_queue(ctlr); // 启动 kthread_worker 消息泵


// 将控制器加入全局 spi_controller_list 链表
mutex_lock(&board_lock);
list_add_tail(&ctlr->list, &spi_controller_list);

// 匹配 board_info(静态平台数据方式注册的设备)
list_for_each_entry(bi, &board_list, list)
spi_match_controller_to_boardinfo(ctlr, &bi->board_info);
mutex_unlock(&board_lock);

// 核心步骤:遍历 DTS 中该 SPI 控制器的所有子节点
// 为每个子节点(如 imu@0)创建并注册一个 spi_device
of_register_spi_devices(ctlr);

// ACPI 设备扫描(x86 平台用,ARM 嵌入式一般用 DT)
acpi_register_spi_devices(ctlr);

return 0;

首先注册了device并加入SPI总线,创建了设备节点,比如/sys/class/spi_master/spi0,of_register_spi_devices扫描Device Tree子节点。

5 DTS如何创建spi_device

重点就是上面的of_register_spi_devices()函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static void of_register_spi_devices(struct spi_controller *ctlr)
{
struct spi_device *spi;
struct device_node *nc;

for_each_available_child_of_node(ctlr->dev.of_node, nc) {
if (of_node_test_and_set_flag(nc, OF_POPULATED))
continue;
spi = of_register_spi_device(ctlr, nc);
if (IS_ERR(spi)) {
dev_warn(&ctlr->dev,
"Failed to create SPI device for %pOF\n", nc);
of_node_clear_flag(nc, OF_POPULATED);
}
}
}

遍历controller节点下面子节点,没发现一个spi_device就调用of_register_spi_device创建一个

6 SPI设备和driver匹配

经过前面几步,系统中已经有了两个关键对象:

  • spi_device:由 of_register_spi_device() 创建,代表 DTS 中的 imu@0,其 modalias = "xxx,imu"dev.of_node 指向设备树的子节点。
  • spi_driver:由设备驱动代码注册(如调用 module_spi_driver(imu_driver)),其 id_table 包含 {"xxx,imu", 0}driver.of_match_table 指向对应的 of_device_id 表。

两者都注册在 spi_bus_type 总线上,但此时还未建立关系。

匹配入口spi_bus_type.match

1
2
3
4
5
6
7
8
9
const struct bus_type spi_bus_type = {
.name = "spi",
.dev_groups = spi_dev_groups,
.match = spi_match_device,
.uevent = spi_uevent,
.probe = spi_probe,
.remove = spi_remove,
.shutdown = spi_shutdown,
};

spi_devicedevice_add() 注册到总线时,内核会遍历总线上所有已注册的 spi_driver,调用 bus->match()(即 spi_match_device())判断设备与驱动是否匹配。

匹配函数spi_match_device

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static int spi_match_device(struct device *dev, struct device_driver *drv)
{
const struct spi_device *spi = to_spi_device(dev); // (A)
const struct spi_driver *sdrv = to_spi_driver(drv); // (B)

/* 优先匹配设备树 of_match_table */
if (of_driver_match_device(dev, drv))
return 1;

/* 匹配 ACPI */
if (acpi_driver_match_device(dev, drv))
return 1;

/* 匹配 spi_device_id 表 */
if (sdrv->id_table)
return !!spi_match_id(sdrv->id_table, spi);

/* 字符串比较驱动名和 modalias */
return strcmp(spi->modalias, drv->name) == 0;
}

整个匹配流程如下

7 总结

Linux SPI设备不是由驱动直接创建,而是通过Device Tree描述硬件信息。

启动过程中,SPI Controller驱动首先注册spi_controller,随后SPI Core解析Device Tree中的子节点,根据节点信息创建对应的spi_device。

当spi_device创建完成后,Linux通过SPI bus的match机制寻找对应spi_driver,匹配成功后调用probe函数完成设备初始化。

8 互动

设备树里面写了一个 SPI 设备,Linux 内核到底什么时候、怎么把它变成 struct spi_device,并最终调用驱动 probe?


Linux SPI设备如何从Device Tree走到driver probe
https://cj0510.github.io/2026/08/01/Linux Kernel/SPI/Linux SPI设备如何从Device Tree走到driver probe/
作者
CJ1018
发布于
2026年8月1日
许可协议
CC BY-NC-SA 4.0