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(); devices_init(); buses_init(); classes_init(); firmware_init(); hypervisor_init();
of_core_init(); platform_bus_init(); auxiliary_bus_init(); memory_dev_init(); node_dev_init(); cpu_dev_init(); container_dev_init(); }
|
主要由两个部分组成,一部分是内核设备驱动的基础,一部分是总线类型和设备树。
我们看一下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;
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;
dev_set_name(&ctlr->dev, "spi%u", ctlr->bus_num);
if (ctlr->use_gpio_descriptors) spi_get_gpio_descs(ctlr);
status = device_add(&ctlr->dev);
if (ctlr->transfer_one || ctlr->transfer_one_message) spi_controller_initialize_queue(ctlr);
mutex_lock(&board_lock); list_add_tail(&ctlr->list, &spi_controller_list);
list_for_each_entry(bi, &board_list, list) spi_match_controller_to_boardinfo(ctlr, &bi->board_info); mutex_unlock(&board_lock);
of_register_spi_devices(ctlr);
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_device 被 device_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); const struct spi_driver *sdrv = to_spi_driver(drv);
if (of_driver_match_device(dev, drv)) return 1;
if (acpi_driver_match_device(dev, drv)) return 1;
if (sdrv->id_table) return !!spi_match_id(sdrv->id_table, spi);
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?