3. Linux SPI子系统架构

Linux SPI子系统采用经典的分层架构设计,将SPI通信栈划分为三个清晰的层次:SPI核心层、SPI控制器驱动层和SPI设备驱动层。这种分层模型实现了硬件无关性与设备复用性,是理解SPI驱动开发的基础。

架构设计目标: 上层设备驱动无需关心底层控制器寄存器操作,下层控制器驱动无需关心具体设备协议细节,核心层负责两者之间的标准化数据传递与总线管理。

3.1 三层模型总体架构

下图展示了SPI子系统三层模型的完整结构,以及各层之间的数据流与控制流关系:

Linux SPI子系统三层架构模型 用户空间 (User Space) 应用程序 / 用户态驱动 (SPIDEV) SPI核心层 (SPI Core) spi.c / spi.h / spi-mem.c 总线注册 · 设备枚举 · 消息队列 · 数据传输抽象 master->transfer_one_message spi_sync / spi_async SPI控制器驱动 SPI Controller Driver • 硬件寄存器操作 • DMA/中断/PIO传输 • 时钟/片选/模式控制 SPI设备驱动 SPI Device Driver • 协议解析 (如ADC/传感器) • 数据格式转换 • 设备特定控制逻辑 硬件层 (Hardware) SPI控制器 (SoC内部) · SPI Flash · 传感器 · ADC · 显示屏 writel / readl SPI总线信号 说明: 控制器驱动与设备驱动通过SPI核心层解耦,核心层提供统一的spi_message传输接口。 设备驱动调用spi_sync/spi_async,核心层将请求排队并调度给控制器驱动。
关键理解: 三层模型中,SPI核心层是"交通警察",控制器驱动是"道路基础设施",设备驱动是"车辆"。车辆(设备驱动)不需要知道道路如何修建,只需要遵守交通规则(核心层API)即可到达目的地。

3.2 SPI核心层 (SPI Core)

SPI核心层位于drivers/spi/spi.c,是SPI子系统的中枢神经。它负责以下关键职责:

功能模块 核心数据结构/函数 职责描述
总线管理 spi_bus_type 注册/注销SPI总线类型,管理总线上所有设备与驱动
控制器注册 spi_register_controller() 将SPI控制器添加到核心层,分配bus_num,创建spidev节点
设备枚举 spi_new_device() / of_spi_register_devices() 从设备树或板级代码创建spi_device实例
消息传输 spi_sync() / spi_async() / __spi_queued_transfer() 提供同步/异步传输接口,管理消息队列与传输调度
DMA支持 spi_map_buf() / spi_unmap_buf() 为控制器驱动提供DMA缓冲区映射与解映射
核心层关键设计: 核心层维护一个spi_controller链表,每个控制器对应一个spi_master结构体。当设备驱动调用spi_sync()时,核心层将spi_message加入控制器的队列,并通过spi_transfer_one_message回调传递给控制器驱动。

3.3 SPI控制器驱动 (SPI Controller Driver)

控制器驱动直接操作硬件寄存器,是SPI子系统的"执行者"。每个SoC厂商需要为自己的SPI控制器编写驱动,实现核心层定义的回调接口。

控制器驱动的核心数据结构是struct spi_controller(旧内核为struct spi_master),关键回调函数包括:

struct spi_controller {
    struct device   dev;
    struct list_head list;
    u16             bus_num;        /* 总线编号 */
    u16             num_chipselect; /* 片选数量 */
    u32             min_speed_hz;   /* 最小时钟频率 */
    u32             max_speed_hz;   /* 最大时钟频率 */
    u8              mode_bits;      /* 支持的SPI模式位 */
    int             (*transfer_one_message)(struct spi_controller *ctlr,
                                            struct spi_message *mesg);
    int             (*transfer_one)(struct spi_controller *ctlr,
                                    struct spi_device *spi,
                                    struct spi_transfer *transfer);
    int             (*prepare_message)(struct spi_controller *ctlr,
                                       struct spi_message *mesg);
    int             (*unprepare_message)(struct spi_controller *ctlr,
                                         struct spi_message *mesg);
    int             (*setup)(struct spi_device *spi);
    void            (*cleanup)(struct spi_device *spi);
    /* DMA相关 */
    bool            can_dma;
    int             (*dma_map)(struct spi_controller *ctlr,
                               struct spi_transfer *xfer);
    void            (*dma_unmap)(struct spi_controller *ctlr,
                                 struct spi_transfer *xfer);
};

控制器驱动的典型实现流程:

  1. probe阶段: 获取硬件资源(时钟、中断、DMA、寄存器基址),初始化SPI控制器硬件,调用spi_register_controller()注册到核心层。
  2. setup回调: 根据spi_device的mode、bits_per_word、max_speed_hz配置控制器寄存器。
  3. transfer_one_message回调: 遍历消息中的transfer链表,对每个transfer调用transfer_one或直接处理。
  4. 传输完成: 在中断或DMA完成回调中调用spi_finalize_current_transfer()通知核心层。
常见陷阱: 控制器驱动必须正确处理SPI_CS_HIGH、SPI_LSB_FIRST等模式标志。许多SoC的SPI控制器在硬件上不支持所有模式,需要在驱动中通过软件模拟或限制模式选择。另外,transfer_one回调中不能睡眠,而transfer_one_message可以睡眠。

3.4 SPI设备驱动 (SPI Device Driver)

SPI设备驱动是面向具体外设的驱动层,负责实现设备特定的协议解析和数据交换。设备驱动通过核心层提供的API与控制器驱动交互,无需关心底层硬件细节。

设备驱动的核心数据结构与接口:

/* SPI设备驱动结构体 */
struct spi_driver {
    const struct spi_device_id *id_table;
    int (*probe)(struct spi_device *spi);
    int (*remove)(struct spi_device *spi);
    void (*shutdown)(struct spi_device *spi);
    struct device_driver driver;
};

/* SPI设备结构体(由核心层创建) */
struct spi_device {
    struct device       dev;
    struct spi_controller *controller;
    u32                 max_speed_hz;   /* 最大时钟频率 */
    u8                  chip_select;    /* 片选编号 */
    u8                  bits_per_word;  /* 每字位数 */
    u16                 mode;           /* SPI模式标志 */
    int                 irq;            /* 中断号(可选) */
    void                *controller_state;
    void                *controller_data;
    char                modalias[SPI_NAME_SIZE];
};

设备驱动开发的核心步骤:

步骤 操作 关键代码示例
1. 定义ID表 匹配设备树或板级信息 { .compatible = "vendor,device", .data = &device_data }
2. 实现probe 初始化设备,分配私有数据 spi_setup(spi); devm_kzalloc(&spi->dev, ...)
3. 数据传输 构造spi_message并发送 spi_sync(spi, &msg);
4. 注册驱动 将驱动注册到SPI总线 spi_register_driver(&my_spi_driver);

设备驱动中典型的SPI数据传输模式:

/* 同步传输示例:向SPI设备写入配置寄存器 */
static int spi_device_write_reg(struct spi_device *spi, u8 reg, u8 val)
{
    int ret;
    u8 tx_buf[2] = { reg, val };
    struct spi_transfer transfer = {
        .tx_buf         = tx_buf,
        .len            = 2,
        .speed_hz       = spi->max_speed_hz,
        .bits_per_word  = 8,
        .cs_change      = 0,
        .delay_usecs    = 10,
    };
    struct spi_message msg;

    spi_message_init(&msg);
    spi_message_add_tail(&transfer, &msg);
    ret = spi_sync(spi, &msg);
    return ret;
}

/* 同步读写传输示例:读取SPI ADC转换结果 */
static int spi_device_read_adc(struct spi_device *spi, u8 *rx_data, int len)
{
    int ret;
    u8 tx_dummy = 0xFF;  /* 发送任意字节以产生时钟 */
    struct spi_transfer transfer = {
        .tx_buf         = &tx_dummy,
        .rx_buf         = rx_data,
        .len            = len,
        .speed_hz       = 1000000,  /* 1MHz */
        .bits_per_word  = 8,
    };
    struct spi_message msg;

    spi_message_init(&msg);
    spi_message_add_tail(&transfer, &msg);
    ret = spi_sync(spi, &msg);
    return ret;
}
性能优化建议: 对于需要频繁传输的设备(如音频编解码器、高速ADC),建议使用spi_async()异步接口配合DMA传输,避免CPU在传输过程中空转。同时,尽量复用spi_message和spi_transfer结构体,减少动态内存分配。

3.5 三层模型的数据流与消息传递

下图展示了从设备驱动发起传输到硬件信号产生的完整数据流:

SPI消息传递与数据流 SPI设备驱动 spi_sync() / spi_async() spi_message SPI核心层 消息队列 · 传输调度 transfer_one_message 控制器驱动 硬件寄存器操作 SPI控制器硬件 SCLK/MOSI/MISO/CS 中断/DMA完成 spi_finalize_current_transfer() 核心层完成回调 complete() / 唤醒等待队列 设备驱动恢复执行 spi_sync() 返回 数据流说明: 实线为请求路径,虚线为完成路径。整个传输过程在核心层的调度下完成, 设备驱动通过同步或异步方式等待传输结果。

3.6 三层模型的关键数据结构关系

理解三层模型需要掌握以下核心数据结构之间的关联:

数据结构 所属层次 关键字段 作用
spi_controller 核心层/控制器驱动 bus_num, transfer_one_message, setup 代表一个SPI控制器,管理所有挂接的设备
spi_device 核心层/设备驱动 controller, chip_select, max_speed_hz, mode 代表一个SPI从设备,由核心层在枚举时创建
spi_driver 设备驱动 id_table, probe, remove 设备驱动的注册结构体,与spi_device匹配
spi_message 核心层 transfers, complete, context, status 一次SPI传输请求,包含多个transfer
spi_transfer 核心层 tx_buf, rx_buf, len, speed_hz, delay_usecs 一个单向或双向的SPI数据段
架构优势总结: 三层模型带来的核心收益包括:(1) 控制器驱动与设备驱动独立开发,互不干扰;(2) 核心层提供统一的调试接口(如/sys/kernel/debug/spi);(3) 支持热插拔和设备树动态配置;(4) 同一控制器可同时挂接不同速率、不同模式的设备。

3.7 设备树与三层模型的交互

在现代嵌入式Linux中,SPI设备信息通常通过设备树(Device Tree)描述。设备树节点与三层模型的对应关系如下:

/* 设备树中的SPI控制器节点 */
&spi1 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&spi1_pins>;
    cs-gpios = <&gpio3 20 GPIO_ACTIVE_LOW>;  /* 片选GPIO */

    /* SPI从设备节点 - 由核心层解析并创建spi_device */
    adc@0 {
        compatible = "ti,ads1256";
        reg = <0>;                    /* 片选编号,对应chip_select */
        spi-max-frequency = <19200000>;
        spi-cpol;                     /* 设置SPI模式为CPOL=1 */
        spi-cpha;                     /* 设置SPI模式为CPHA=1 */
        interrupts = <&gpio1 16 IRQ_TYPE_EDGE_FALLING>;
    };

    flash@1 {
        compatible = "jedec,spi-nor";
        reg = <1>;
        spi-max-frequency = <50000000>;
        spi-tx-bus-width = <4>;       /* 四线输出 */
        spi-rx-bus-width = <4>;       /* 四线输入 */
        m25p,fast-read;
    };
};

设备树解析流程:

  1. SPI控制器驱动probe时,调用spi_register_controller()注册控制器。
  2. 核心层在注册过程中调用of_spi_register_devices(),遍历控制器节点下的子节点。
  3. 对每个子节点,核心层解析reg(片选号)、spi-max-frequency、spi-cpol/spi-cpha等属性。
  4. 核心层创建spi_device实例,并添加到SPI总线。
  5. SPI总线匹配spi_driver的id_table或of_match_table,调用匹配驱动的probe函数。
调试技巧: 在/sys/bus/spi/devices/目录下可以查看所有已注册的SPI设备。每个设备目录下包含modalias、driver、power等属性文件,可用于验证设备树解析是否正确。如果设备未出现,检查compatible属性是否与驱动匹配,以及status = "okay"是否设置。

3.8 三层模型常见问题与排查思路

问题现象 可能原因 排查层次 解决方案
SPI设备未在sysfs中出现 设备树节点未正确解析 核心层 检查dmesg中of_spi_register_devices的日志,确认compatible匹配
传输超时或返回-EIO 控制器驱动transfer_one未正确实现 控制器驱动 检查中断处理、DMA回调、FIFO状态寄存器
数据错位或字节顺序错误 bits_per_word或模式设置不匹配 设备驱动 确认设备端与控制器端的CPOL/CPHA一致,检查LSB/MSB设置
片选信号异常 cs_gpio配置错误或控制器片选逻辑问题 控制器驱动 使用GPIO模拟片选,检查SPI_CS_HIGH标志
DMA传输卡死 DMA映射失败或缓冲区对齐问题 核心层/控制器驱动 检查dma_map_single返回值,确保缓冲区cache一致性
架构级陷阱: 在编写SPI设备驱动时,切勿直接访问控制器寄存器或操作片选GPIO。所有硬件操作必须通过核心层提供的API进行,否则会导致与控制器驱动的竞争条件,且无法在硬件平台间移植。同样,控制器驱动不应假设设备驱动的具体协议,保持通用性。