2. Linux SPI子系统架构:分层设计与数据结构

Linux SPI子系统采用经典的分层架构设计,将SPI总线操作抽象为核心层、控制器驱动层和设备驱动层三个层次。这种分层解耦了SPI控制器硬件差异与设备协议实现,使得开发者可以专注于各自层次的逻辑,无需关心底层细节。

Linux SPI子系统分层架构 用户空间 (User Space) SPI设备驱动层 (SPI Device Driver) spi_driver / spi_device / probe() / remove() / spi_transfer() SPI核心层 (SPI Core) spi_controller / spi_message / spi_transfer / 总线注册与匹配 SPI控制器驱动层 (SPI Controller Driver) spi_controller_ops / transfer_one() / setup() / prepare_message() 硬件层 (SPI Controller Hardware) 数据流方向:用户空间 → 设备驱动 → SPI核心 → 控制器驱动 → 硬件

2.1 分层设计概述

SPI子系统的三层架构各司其职,通过标准接口进行交互:

层次 核心职责 关键数据结构 开发者角色
SPI设备驱动层 实现具体SPI设备的协议逻辑,如ADC、传感器、显示屏等 spi_driver、spi_device 设备驱动开发者
SPI核心层 管理SPI总线、设备注册与匹配、数据传输框架 spi_controller、spi_message 内核维护者
SPI控制器驱动层 直接操作SPI控制器寄存器,完成硬件时序 spi_controller_ops SoC厂商/板级开发者
设计原则:设备驱动层不关心控制器是哪个SoC的,控制器驱动层不关心挂载的是什么设备。核心层作为桥梁,提供统一的spi_transfer()接口和消息队列机制。

2.2 SPI核心层数据结构

SPI核心层是子系统的中枢,负责管理总线、设备和数据传输。核心数据结构包括:

2.2.1 spi_controller —— 控制器抽象

每个SPI控制器硬件在核心层对应一个spi_controller实例,包含控制器的属性、队列管理和操作函数集:

struct spi_controller {
    struct device           dev;            /* 设备模型 */
    struct list_head        list;           /* 全局控制器链表 */
    u16                     bus_num;        /* 总线编号 */
    u16                     num_chipselect; /* 片选数量 */
    u32                     mode_bits;      /* 支持的SPI模式位掩码 */
    u32                     bits_per_word_mask;
    u32                     min_speed_hz;   /* 最低速率 */
    u32                     max_speed_hz;   /* 最高速率 */

    /* 队列与传输 */
    struct spi_message      *cur_msg;
    struct completion       xfer_completion;
    size_t                  queue_size;

    /* 操作函数集 */
    const struct spi_controller_ops *ops;

    /* 私有数据 */
    void                    *controller_data;
};
spi_controller 核心字段关系 spi_controller bus_num / num_chipselect mode_bits / speed cur_msg / queue spi_controller_ops 关键说明: • bus_num:系统唯一标识,用于设备树或板级代码引用 • ops:控制器驱动必须实现的回调函数集,包括 transfer_one()、setup() 等

2.2.2 spi_device —— 设备抽象

每个挂载在SPI总线上的从设备对应一个spi_device,由核心层在设备枚举时创建:

struct spi_device {
    struct device           dev;            /* 设备模型 */
    struct spi_controller   *controller;    /* 所属控制器 */
    struct spi_bus          *bus;           /* 所属总线 */
    u32                     max_speed_hz;   /* 设备最大速率 */
    u8                      chip_select;    /* 片选编号 */
    u8                      mode;           /* SPI模式:CPOL/CPHA/CS_HIGH等 */
    u8                      bits_per_word;  /* 每字位数,通常8 */
    int                     irq;            /* 中断号(可选) */
    void                    *controller_data;
    void                    *driver_data;   /* 驱动私有数据 */
};

2.2.3 spi_message 与 spi_transfer —— 传输单元

数据传输以spi_message为基本单位,一个消息包含多个spi_transfer段:

struct spi_message {
    struct list_head        transfers;      /* 传输段链表 */
    struct spi_device       *spi;           /* 目标设备 */
    unsigned                is_dma_mapped:1;
    /* 完成回调 */
    void                    (*complete)(void *context);
    void                    *context;
    unsigned                frame_length;
    struct spi_transfer     *state;
};

struct spi_transfer {
    const void              *tx_buf;        /* 发送缓冲区 */
    void                    *rx_buf;        /* 接收缓冲区 */
    unsigned                len;            /* 传输字节数 */
    dma_addr_t              tx_dma;         /* DMA发送地址 */
    dma_addr_t              rx_dma;         /* DMA接收地址 */
    unsigned                cs_change:1;    /* 传输后是否切换片选 */
    u8                      bits_per_word;
    u16                     delay_usecs;    /* 传输间延时 */
    u32                     speed_hz;       /* 本段速率 */
};
传输模型:一个spi_message包含一个或多个spi_transfer,所有传输段在片选有效期间连续执行。设备驱动通过spi_async()提交消息,核心层将其加入控制器队列,由控制器驱动依次处理。

2.3 SPI控制器驱动层

控制器驱动层负责实现spi_controller_ops中的回调函数,直接操作硬件寄存器:

struct spi_controller_ops {
    /* 设置SPI设备参数(模式、速率、字长等) */
    int     (*setup)(struct spi_device *spi);

    /* 单次传输(同步方式) */
    int     (*transfer_one)(struct spi_controller *ctlr,
                            struct spi_device *spi,
                            struct spi_transfer *transfer);

    /* 消息准备(DMA映射、FIFO配置等) */
    int     (*prepare_message)(struct spi_controller *ctlr,
                               struct spi_message *msg);

    /* 消息完成后的清理 */
    int     (*unprepare_message)(struct spi_controller *ctlr,
                                 struct spi_message *msg);

    /* 队列空闲时的回调 */
    void    (*handle_idle)(struct spi_controller *ctlr);
};
回调函数 调用时机 典型实现
setup() 设备注册或参数变更时 配置控制器寄存器:时钟极性、相位、分频系数
transfer_one() 每个传输段执行时 写入TX FIFO、启动SPI时钟、等待RX FIFO数据
prepare_message() 消息开始前 DMA映射缓冲区、清空FIFO、使能控制器
unprepare_message() 消息完成后 DMA解映射、关闭控制器、触发完成回调

2.4 SPI设备驱动层

设备驱动层通过spi_driver结构体注册到核心层,核心层通过设备树或板级代码匹配设备:

static const struct of_device_id my_spi_of_match[] = {
    { .compatible = "myvendor,my-spi-device" },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_spi_of_match);

static struct spi_driver my_spi_driver = {
    .driver = {
        .name   = "my_spi_device",
        .of_match_table = my_spi_of_match,
    },
    .probe  = my_spi_probe,
    .remove = my_spi_remove,
    .id_table = my_spi_id_table,  /* 非设备树匹配时使用 */
};

module_spi_driver(my_spi_driver);
设备驱动与核心层交互流程 SPI设备驱动 spi_driver.probe() SPI核心层 spi_register_driver() 控制器驱动 ops->setup() 数据传输流程 spi_write() / spi_read() / spi_write_then_read() → spi_message → spi_async() ① 构造message ② 提交到队列 ③ 控制器执行 ④ 完成回调 设备驱动通过 spi_async() 异步提交,或 spi_sync() 同步等待完成

2.5 关键数据结构关系总结

以下表格总结了各层核心数据结构之间的关联关系:

数据结构 所属层次 关联关系 生命周期
spi_controller 核心层 包含 spi_controller_ops,管理多个 spi_device 系统启动时注册,模块卸载时注销
spi_device 核心层 指向所属 spi_controller,由设备树或板级代码创建 设备枚举时创建,设备移除时销毁
spi_driver 设备驱动层 通过 probe() 绑定到 spi_device 驱动加载时注册,卸载时注销
spi_message 核心层 包含 spi_transfer 链表,指向目标 spi_device 每次传输动态创建,传输完成后释放
spi_transfer 核心层 作为 spi_message 的传输段,包含 tx_buf/rx_buf 随 spi_message 创建和销毁
常见误区:
  • 不要混淆 spi_device 和 spi_driver:前者是设备实例,后者是驱动逻辑。
  • 一个 spi_message 中的所有 spi_transfer 在片选有效期间连续执行,中间不会释放总线。
  • 控制器驱动的 transfer_one() 必须处理 cs_change 标志,否则多段传输可能出错。

2.6 设备树与板级匹配

SPI设备通常通过设备树(Device Tree)描述,核心层在解析设备树时自动创建spi_device:

/* 设备树示例 */
&spi1 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&spi1_pins>;

    my_adc@0 {
        compatible = "myvendor,my-spi-adc";
        reg = <0>;              /* 片选编号 */
        spi-max-frequency = <10000000>;
        spi-cpol;               /* 模式:CPOL=1 */
        spi-cpha;               /* 模式:CPHA=1 */
    };
};

核心层解析设备树后,执行以下步骤:

  1. 读取 reg 属性确定片选编号,创建 spi_device。
  2. 读取 spi-max-frequency 设置 max_speed_hz。
  3. 根据 spi-cpol、spi-cpha 等标志设置 mode 字段。
  4. 调用 spi_controller_ops.setup() 配置控制器硬件。
  5. 遍历已注册的 spi_driver,匹配 of_match_table,调用 probe()。
开发建议:编写SPI设备驱动时,优先使用设备树描述硬件参数,避免在驱动代码中硬编码片选号、速率等配置。设备树方式使驱动更具可移植性。