4. Linux SPI子系统架构:SPI核心层、SPI控制器驱动层、SPI设备驱动层

Linux SPI子系统采用经典的分层架构设计,将SPI总线操作抽象为三个层次:SPI核心层、SPI控制器驱动层和SPI设备驱动层。这种分层结构实现了硬件无关性与驱动复用,是嵌入式开发人员必须掌握的核心知识。

架构设计目标: SPI子系统通过分层解耦,使得控制器驱动开发者只需关注寄存器操作,设备驱动开发者只需关注协议逻辑,核心层则提供统一的框架与接口。

4.1 三层架构总览

下图展示了SPI子系统的完整分层结构及各层之间的调用关系:

Linux SPI子系统三层架构 用户空间 (User Space) 应用层:SPI字符设备 / IIO / 网络 / MTD SPI设备驱动层 (SPI Device Driver) spi_driver / spi_device / probe / remove / spi_transfer SPI核心层 (SPI Core) spi_register_master / spi_register_driver / 总线管理 / 消息队列 SPI控制器驱动层 (SPI Controller Driver) spi_master / transfer_one_message / 硬件寄存器操作 硬件层:SPI控制器 (SoC内部)

4.2 SPI核心层 (SPI Core)

SPI核心层是子系统的基础设施,位于drivers/spi/spi.c中,负责以下关键功能:

  • 总线管理: 注册和管理SPI总线类型 (spi_bus_type),维护设备与驱动的匹配。
  • 设备与驱动注册: 提供spi_register_master()和spi_register_driver()接口。
  • 消息队列调度: 管理spi_message的排队、拆分与传输调度。
  • DMA与PIO选择: 根据传输大小自动选择DMA或PIO模式。
  • 设备树解析: 解析设备树中SPI控制器的子节点,自动创建spi_device。
核心数据结构: struct spi_master 代表一个SPI控制器,struct spi_device 代表一个SPI从设备,struct spi_driver 代表设备驱动。三者通过核心层串联。

4.3 SPI控制器驱动层

控制器驱动层直接操作硬件寄存器,实现SPI总线的时序控制。每个SoC的SPI控制器都需要独立的驱动实现。

控制器驱动职责 说明
初始化与去初始化 时钟使能、GPIO配置、中断注册、DMA通道申请
传输回调实现 实现 master->transfer_one_message 或 transfer_one
片选控制 硬件CS或GPIO模拟CS的拉高/拉低
时钟极性/相位配置 根据 spi_device->mode 设置CPOL/CPHA
FIFO与DMA管理 利用硬件FIFO或DMA提高传输效率

控制器驱动的核心是填充struct spi_master的回调函数:

static int my_spi_transfer_one(struct spi_master *master,
                                struct spi_device *spi,
                                struct spi_transfer *transfer)
{
    // 1. 配置时钟极性/相位
    // 2. 设置数据长度和比特顺序
    // 3. 写入TX FIFO或启动DMA
    // 4. 等待传输完成(中断或轮询)
    // 5. 读取RX FIFO数据
    return 0;
}

static int my_spi_probe(struct platform_device *pdev)
{
    struct spi_master *master;
    // 分配并初始化spi_master
    master = devm_spi_alloc_master(&pdev->dev, 0);
    master->transfer_one = my_spi_transfer_one;
    master->num_chipselect = 2;  // 支持2个片选
    master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
    // 注册到核心层
    return devm_spi_register_master(&pdev->dev, master);
}

4.4 SPI设备驱动层

设备驱动层面向具体的SPI从设备(如ADC、传感器、显示屏),开发者只需实现协议逻辑,无需关心底层时序细节。

设备驱动开发者的视角: 你只需要填充 struct spi_driver 结构体,在 probe 函数中获取 spi_device 指针,然后通过 spi_write()、spi_read()、spi_sync_transfer() 等API完成数据收发。

设备驱动的典型实现模板:

static int my_spi_device_probe(struct spi_device *spi)
{
    struct device_data *data;
    // 设置SPI模式与速率
    spi->mode = SPI_MODE_0;
    spi->max_speed_hz = 10000000;  // 10MHz
    spi_setup(spi);

    // 分配私有数据
    data = devm_kzalloc(&spi->dev, sizeof(*data), GFP_KERNEL);
    spi_set_drvdata(spi, data);

    // 执行一次读操作验证通信
    u8 tx_buf[] = {0x01, 0x02};
    u8 rx_buf[2];
    struct spi_transfer t = {
        .tx_buf = tx_buf,
        .rx_buf = rx_buf,
        .len = 2,
    };
    spi_sync_transfer(spi, &t, 1);
    return 0;
}

static struct spi_driver my_spi_driver = {
    .driver = {
        .name = "my_spi_device",
        .of_match_table = my_spi_of_match,
    },
    .probe = my_spi_device_probe,
    .remove = my_spi_device_remove,
};
module_spi_driver(my_spi_driver);

4.5 三层协作流程

当应用层发起一次SPI读写时,三层之间的完整调用链如下:

SPI数据收发完整调用链 ① 应用层调用 spi_sync_transfer() 或 spi_write()/spi_read() ② SPI核心层:构造 spi_message,加入传输队列,唤醒 kthread ③ 控制器驱动层:transfer_one_message 回调,操作硬件寄存器 ④ 硬件层:SCLK输出、MOSI数据发送、MISO数据接收、CS片选控制 ⑤ 数据逐层返回:硬件中断 → 控制器驱动完成回调 → 核心层唤醒等待队列 → 应用层获取数据

4.6 各层关键数据结构对比

层次 核心结构体 关键字段/回调 所在文件
核心层 struct spi_master bus_num, num_chipselect, mode_bits, transfer_one_message include/linux/spi/spi.h
核心层 struct spi_device master, chip_select, mode, max_speed_hz, controller_state include/linux/spi/spi.h
核心层 struct spi_driver probe, remove, id_table, driver.pm include/linux/spi/spi.h
控制器驱动 struct spi_controller transfer_one, prepare_message, unprepare_message, set_cs drivers/spi/spi.c
设备驱动 struct spi_driver probe中调用spi_setup(), spi_sync_transfer() 设备驱动自身文件
常见误区: 初学者容易混淆 spi_master 和 spi_controller。在较新内核(5.10+)中,spi_master 被重命名为 spi_controller,但为了兼容性,spi_master 宏仍然保留。实际开发中建议使用 spi_controller。

4.7 设备树与三层的关联

设备树是连接三层的关键桥梁。控制器驱动通过设备树节点获取硬件资源,核心层解析子节点自动创建设备,设备驱动通过of_match_table匹配节点:

// 设备树片段
&spi1 {
    status = "okay";
    pinctrl-0 = &spi1_pins;
    cs-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>;

    adc@0 {                    // 核心层解析此节点
        compatible = "ti,ads1256";
        reg = <0>;             // 片选索引
        spi-max-frequency = <2000000>;
        spi-cpol;              // 时钟极性
        spi-cpha;              // 时钟相位
    };
};

// 控制器驱动层:通过 platform_driver 匹配 spi1 节点
// 核心层:自动为 adc@0 创建 spi_device
// 设备驱动层:通过 of_match_table 匹配 "ti,ads1256"

理解三层架构是掌握SPI驱动开发的基础。控制器驱动层处理硬件差异,核心层提供统一框架,设备驱动层实现具体功能。在实际项目中,大多数开发者只需要编写设备驱动层代码,但理解底层机制有助于调试和性能优化。