Linux内核中SPI 子系统的整体架构

SPI 子系统的整体架构

复制代码
用户空间应用程序
        ↓
    spidev.c (字符设备驱动,可以用内核写好的通用字符设备驱动,也可以自己写)
        ↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    SPI 核心层 (spi.c)
    - 注册/注销 SPI Master
    - 注册/注销 SPI 设备
    - 管理传输队列
    - 提供通用 API
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
        ↓
    SPI Master 驱动 (spi-imx.c, spi-s3c64xx.c 等)
    - 硬件初始化
    - 传输实现
    - 中断处理
        ↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    硬件控制器 (SPI0, SPI1...)

核心数据结构

1. SPI Master 控制器结构

c 复制代码
// include/linux/spi/spi.h

struct spi_master {					// SPI Master 控制器结构体
    struct device   dev;            // 设备模型
    
    struct list_head list;          // 链表节点
    
    /* 控制器属性 */
    s16         bus_num;            // 总线编号(SPI0, SPI1...)
    u16         num_chipselect;     // 片选信号数量
    u16         dma_alignment;      // DMA 对齐要求
    u16         mode_bits;          // 支持的模式位
    u32         bits_per_word_mask; // 支持的每字长度
    u32         min_speed_hz;       // 最小时钟频率
    u32         max_speed_hz;       // 最大时钟频率
    u16         flags;              // 标志位
    
    /* 传输相关 */
    spinlock_t              bus_lock_spinlock;
    struct mutex            bus_lock_mutex;
    bool                    bus_lock_flag;
    
    /* 队列化传输支持 */
    bool                    queued;
    struct kthread_worker   kworker;
    struct task_struct      *kworker_task;
    struct kthread_work     pump_messages;
    spinlock_t              queue_lock;
    struct list_head        queue;      // 消息队列
    struct spi_message      *cur_msg;   // 当前正在处理的消息
    bool                    idling;
    bool                    busy;
    bool                    running;
    bool                    rt;
    bool                    auto_runtime_pm;
    bool                    cur_msg_prepared;
    bool                    cur_msg_mapped;
    struct completion       xfer_completion;
    size_t                  max_dma_len;
    
    /* 方法函数 - 由具体驱动实现 */
    int     (*setup)(struct spi_device *spi);
    int     (*transfer)(struct spi_device *spi, struct spi_message *mesg);
    void    (*cleanup)(struct spi_device *spi);
    
    /* 队列化传输的方法 */
    int     (*prepare_transfer_hardware)(struct spi_master *master);
    int     (*transfer_one_message)(struct spi_master *master,
                                   struct spi_message *mesg);
    int     (*unprepare_transfer_hardware)(struct spi_master *master);
    int     (*prepare_message)(struct spi_master *master,
                              struct spi_message *message);
    int     (*unprepare_message)(struct spi_master *master,
                                struct spi_message *message);
    
    /* 单个传输的方法 */
    int     (*transfer_one)(struct spi_master *master,
                           struct spi_device *spi,
                           struct spi_transfer *transfer);
    
    /* GPIO 片选 */
    int     *cs_gpios;
    
    /* DMA 相关 */
    struct dma_chan     *dma_tx;
    struct dma_chan     *dma_rx;
    void                *dummy_rx;
    void                *dummy_tx;
};

2. SPI 设备结构

c 复制代码
// include/linux/spi/spi.h
struct spi_device {
    struct device       dev;        // 设备模型
    struct spi_master   *master;    // 所属 master
    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];
    int                 cs_gpio;        // 片选 GPIO
};

3. SPI 消息和传输结构

c 复制代码
// SPI 传输结构(一次传输)
struct spi_transfer {
    const void  *tx_buf;        // 发送缓冲区
    void        *rx_buf;        // 接收缓冲区
    unsigned    len;            // 传输长度
    
    dma_addr_t  tx_dma;         // DMA 地址
    dma_addr_t  rx_dma;
    struct sg_table tx_sg;      // scatter-gather 表
    struct sg_table rx_sg;
    
    unsigned    cs_change:1;    // 传输后改变片选
    unsigned    tx_nbits:3;     // 发送位宽
    unsigned    rx_nbits:3;     // 接收位宽
    u8          bits_per_word;  // 每字位数
    u16         delay_usecs;    // 传输后延迟
    u32         speed_hz;       // 时钟频率
    
    struct list_head transfer_list;  // 链表节点
};

// SPI 消息结构(多次传输)
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;
    unsigned            actual_length;
    int                 status;
    
    struct list_head    queue;      // 消息队列节点
    void                *state;
};

SPI Master 驱动框架流程

1. Master 驱动注册流程

c 复制代码
// drivers/spi/spi-xxx.c (具体的 Master 驱动)

static int spi_xxx_probe(struct platform_device *pdev)
{
    struct spi_master *master;
    struct spi_xxx_data *data;
    int ret;
    
    /* 1. 分配 spi_master 结构 */
    master = spi_alloc_master(&pdev->dev, sizeof(*data));
    if (!master)
        return -ENOMEM;
    
    /* 2. 获取私有数据 */
    data = spi_master_get_devdata(master);
    
    /* 3. 设置 master 属性 */
    master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
    master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
    master->bus_num = pdev->id;  // 总线号
    master->num_chipselect = 4;   // 支持 4 个片选
    master->dev.of_node = pdev->dev.of_node;
    
    /* 4. 设置传输方法(二选一)*/
    // 方式1:老式接口
    master->transfer = spi_xxx_transfer;
    
    // 方式2:队列化接口(推荐)
    master->transfer_one_message = spi_xxx_transfer_one_message;
    // 或者
    master->transfer_one = spi_xxx_transfer_one;
    
    /* 5. 设置其他方法 */
    master->setup = spi_xxx_setup;
    master->cleanup = spi_xxx_cleanup;
    master->prepare_transfer_hardware = spi_xxx_prepare_hw;
    master->unprepare_transfer_hardware = spi_xxx_unprepare_hw;
    
    /* 6. 硬件初始化 */
    data->regs = devm_ioremap_resource(&pdev->dev, res);
    data->clk = devm_clk_get(&pdev->dev, NULL);
    clk_prepare_enable(data->clk);
    
    /* 7. 注册中断 */
    ret = devm_request_irq(&pdev->dev, irq, spi_xxx_irq,
                          0, dev_name(&pdev->dev), data);
    
    /* 8. 注册 spi_master */
    ret = devm_spi_register_master(&pdev->dev, master);
    if (ret) {
        dev_err(&pdev->dev, "register master failed\n");
        goto err;
    }
    
    platform_set_drvdata(pdev, master);
    return 0;
    
err:
    spi_master_put(master);
    return ret;
}

2. SPI 核心层的关键函数

c 复制代码
// drivers/spi/spi.c

/**
 * spi_register_master - 注册 SPI master 控制器
 */
int spi_register_master(struct spi_master *master)
{
    static atomic_t     dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
    struct device       *dev = master->dev.parent;
    int                 status;
    
    /* 1. 分配总线号 */
    if (master->bus_num < 0) {
        master->bus_num = atomic_dec_return(&dyn_bus_id);
    }
    
    /* 2. 设置设备类型 */
    master->dev.class = &spi_master_class;
    master->dev.bus = &spi_bus_type;
    
    /* 3. 初始化队列 */
    INIT_LIST_HEAD(&master->queue);
    spin_lock_init(&master->queue_lock);
    spin_lock_init(&master->bus_lock_spinlock);
    mutex_init(&master->bus_lock_mutex);
    
    /* 4. 如果支持队列化传输 */
    if (master->transfer) {
        // 使用老式传输方法
    } else {
        // 使用队列化传输
        master->queued = true;
        spi_master_initialize_queue(master);
    }
    
    /* 5. 注册设备 */
    dev_set_name(&master->dev, "spi%u", master->bus_num);
    status = device_add(&master->dev);
    if (status < 0)
        goto done;
    
    /* 6. 从设备树扫描子设备 */
    of_register_spi_devices(master);
    
    /* 7. 从 ACPI 扫描子设备 */
    acpi_register_spi_devices(master);
    
done:
    return status;
}

/**
 * spi_sync - 同步传输
 */
int spi_sync(struct spi_device *spi, struct spi_message *message)
{
    DECLARE_COMPLETION_ONSTACK(done);
    int status;
    
    message->complete = spi_complete;
    message->context = &done;
    message->spi = spi;
    
    /* 提交消息到队列 */
    status = spi_async_locked(spi, message);
    if (status == 0) {
        /* 等待完成 */
        wait_for_completion(&done);
        status = message->status;
    }
    message->context = NULL;
    return status;
}

/**
 * spi_async - 异步传输
 */
int spi_async(struct spi_device *spi, struct spi_message *message)
{
    struct spi_master *master = spi->master;
    int ret;
    unsigned long flags;
    
    spin_lock_irqsave(&master->bus_lock_spinlock, flags);
    
    if (master->bus_lock_flag)
        ret = -EBUSY;
    else
        ret = __spi_async(spi, message);
    
    spin_lock_irqrestore(&master->bus_lock_spinlock, flags);
    
    return ret;
}

static int __spi_async(struct spi_device *spi, struct spi_message *message)
{
    struct spi_master *master = spi->master;
    
    message->spi = spi;
    
    /* 验证消息 */
    if (list_empty(&message->transfers))
        return -EINVAL;
    
    /* 提交到队列 */
    return __spi_queued_transfer(spi, message);
}

3. 队列化传输机制

c 复制代码
// drivers/spi/spi.c

/**
 * spi_queued_transfer - 队列化传输
 */
static int __spi_queued_transfer(struct spi_device *spi,
                                struct spi_message *msg)
{
    struct spi_master *master = spi->master;
    unsigned long flags;
    
    spin_lock_irqsave(&master->queue_lock, flags);
    
    /* 1. 如果 master 未运行,返回错误 */
    if (!master->running) {
        spin_unlock_irqrestore(&master->queue_lock, flags);
        return -ESHUTDOWN;
    }
    
    /* 2. 设置消息状态 */
    msg->actual_length = 0;
    msg->status = -EINPROGRESS;
    
    /* 3. 加入队列 */
    list_add_tail(&msg->queue, &master->queue);
    
    /* 4. 如果空闲,启动传输 */
    if (!master->busy && need_pump)
        kthread_queue_work(&master->kworker, &master->pump_messages);
    
    spin_unlock_irqrestore(&master->queue_lock, flags);
    return 0;
}

/**
 * spi_pump_messages - 消息泵(内核线程)
 */
static void spi_pump_messages(struct kthread_work *work)
{
    struct spi_master *master =
        container_of(work, struct spi_master, pump_messages);
    unsigned long flags;
    bool was_busy = false;
    int ret;
    
    /* 1. 获取下一条消息 */
    spin_lock_irqsave(&master->queue_lock, flags);
    if (list_empty(&master->queue) || !master->running) {
        master->busy = false;
        master->idling = true;
        spin_unlock_irqrestore(&master->queue_lock, flags);
        return;
    }
    
    /* 2. 取出第一条消息 */
    master->cur_msg = list_first_entry(&master->queue,
                                      struct spi_message, queue);
    list_del_init(&master->cur_msg->queue);
    master->busy = true;
    spin_unlock_irqrestore(&master->queue_lock, flags);
    
    /* 3. 准备硬件 */
    if (!was_busy && master->prepare_transfer_hardware) {
        ret = master->prepare_transfer_hardware(master);
        if (ret) {
            dev_err(&master->dev, "failed to prepare hardware\n");
            goto out;
        }
    }
    
    /* 4. 准备消息 */
    if (master->prepare_message) {
        ret = master->prepare_message(master, master->cur_msg);
        if (ret) {
            dev_err(&master->dev, "failed to prepare message\n");
            goto out;
        }
        master->cur_msg_prepared = true;
    }
    
    /* 5. 执行传输 */
    ret = master->transfer_one_message(master, master->cur_msg);
    if (ret) {
        dev_err(&master->dev, "failed to transfer message\n");
        goto out;
    }
    
    return;
    
out:
    /* 错误处理 */
    master->cur_msg->status = ret;
    spi_finalize_current_message(master);
}

4. 具体 Master 驱动实现示例

c 复制代码
// drivers/spi/spi-imx.c (i.MX SPI Master 驱动)

struct spi_imx_data {
    struct spi_bitbang bitbang;
    struct completion xfer_done;
    
    void __iomem *base;
    struct clk *clk_per;
    struct clk *clk_ipg;
    
    unsigned long spi_clk;
    unsigned int speed_hz;
    unsigned int bits_per_word;
    unsigned int spi_drctl;
    
    unsigned int count;
    void (*tx)(struct spi_imx_data *);
    void (*rx)(struct spi_imx_data *);
    void *rx_buf;
    const void *tx_buf;
    unsigned int txfifo; /* number of words pushed in tx FIFO */
    
    /* DMA */
    bool usedma;
    u32 wml;
    struct completion dma_rx_completion;
    struct completion dma_tx_completion;
};

/**
 * spi_imx_transfer_one - 传输一个 transfer
 */
static int spi_imx_transfer_one(struct spi_master *master,
                               struct spi_device *spi,
                               struct spi_transfer *transfer)
{
    struct spi_imx_data *spi_imx = spi_master_get_devdata(master);
    
    /* 1. 配置传输参数 */
    spi_imx->speed_hz = transfer->speed_hz;
    spi_imx->bits_per_word = transfer->bits_per_word;
    spi_imx->count = transfer->len;
    spi_imx->tx_buf = transfer->tx_buf;
    spi_imx->rx_buf = transfer->rx_buf;
    spi_imx->txfifo = 0;
    
    reinit_completion(&spi_imx->xfer_done);
    
    /* 2. 配置硬件 */
    spi_imx_config(spi_imx);
    
    /* 3. 根据是否使用 DMA 选择传输方式 */
    if (spi_imx->usedma) {
        ret = spi_imx_dma_transfer(spi_imx, transfer);
    } else {
        /* 启动传输(触发中断) */
        spi_imx_push(spi_imx);
        
        /* 等待完成 */
        wait_for_completion(&spi_imx->xfer_done);
    }
    
    return 0;
}

/**
 * spi_imx_isr - 中断处理函数
 */
static irqreturn_t spi_imx_isr(int irq, void *dev_id)
{
    struct spi_imx_data *spi_imx = dev_id;
    
    /* 1. 读取接收 FIFO */
    while (spi_imx_can_read(spi_imx))
        spi_imx->rx(spi_imx);
    
    /* 2. 写入发送 FIFO */
    if (spi_imx->count) {
        spi_imx_push(spi_imx);
        return IRQ_HANDLED;
    }
    
    /* 3. 传输完成 */
    if (spi_imx_can_read(spi_imx))
        return IRQ_HANDLED;
    
    /* 4. 通知完成 */
    complete(&spi_imx->xfer_done);
    
    return IRQ_HANDLED;
}

SPI 设备驱动使用 SPI Master

c 复制代码
// oled_drv.c

static struct spi_device *oled;

static int spidev_probe(struct spi_device *spi)
{
    /* 保存 spi_device 指针 */
    oled = spi;
    
    /* 配置 SPI 模式 */
    spi->mode = SPI_MODE_0;
    spi->bits_per_word = 8;
    spi->max_speed_hz = 10000000;  // 10MHz
    spi_setup(spi);
    
    // ... 其他初始化 ...
}

static void spi_write_datas(const unsigned char *buf, int len)
{
    /* 使用 SPI 核心层API发送数据 */
    spi_write(oled, buf, len);
    //  ↓
    //  内部调用流程:
    //  spi_write() 
    //    → spi_sync()
    //      → spi_async()
    //        → __spi_queued_transfer()
    //          → 加入队列
    //            → spi_pump_messages()
    //              → master->transfer_one_message()
    //                → 具体硬件驱动的传输函数
    //                  → 中断/DMA 完成
    //                    → complete()
    //                      → spi_sync() 返回
}

SPI 框架的完整调用链

复制代码
应用层/驱动层调用:
    spi_write(spi, buf, len)
        ↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    SPI 核心层 (spi.c)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    ↓
    spi_sync(spi, &message)
        ↓
    spi_async(spi, &message)
        ↓
    __spi_queued_transfer(spi, &message)
        ↓
    list_add_tail(&message->queue, &master->queue)
        ↓
    kthread_queue_work(&master->kworker, &master->pump_messages)
        ↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    内核线程: spi_pump_messages()
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
        ↓
    master->prepare_transfer_hardware(master)  // 准备硬件
        ↓
    master->prepare_message(master, message)    // 准备消息
        ↓
    master->transfer_one_message(master, message)  // 传输消息
        ↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    具体 Master 驱动 (spi-imx.c)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
        ↓
    spi_imx_transfer_one_message()
        ↓
    遍历 message->transfers 链表
        ↓
    for_each_transfer:
        spi_imx_transfer_one(master, spi, transfer)
            ↓
        配置硬件寄存器
            ↓
        启动传输(写 FIFO / 启动 DMA)
            ↓
        等待完成(中断或 DMA 回调)
            ↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    中断处理
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
            ↓
        spi_imx_isr(irq, dev_id)
            ↓
        读取 RX FIFO
        写入 TX FIFO
            ↓
        传输完成?
            ↓
        complete(&spi_imx->xfer_done)
            ↓
        spi_imx_transfer_one() 返回
            ↓
        下一个 transfer 或消息完成
            ↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    回到 SPI 核心层
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
            ↓
        spi_finalize_current_message(master)
            ↓
        message->complete(message->context)  // 回调
            ↓
        spi_sync() 中的 wait_for_completion() 返回
            ↓
        spi_write() 返回到调用者

总结

层次 职责 文件
SPI 核心层 消息队列管理、同步/异步传输、设备注册 drivers/spi/spi.c
SPI Master驱动 硬件初始化、传输实现、中断/DMA处理 drivers/spi/spi-imx.c
SPI 设备驱动 使用 SPI API 与设备通信 你的 oled_drv.c

关键点

  1. 分层设计:核心层提供统一接口,具体驱动实现硬件细节
  2. 队列化传输 :现代驱动推荐使用 transfer_one 而非老式 transfer
  3. 内核线程spi_pump_messages 在后台处理消息队列
  4. 同步/异步 :提供 spi_sync(阻塞)和 spi_async(非阻塞)两种方式
相关推荐
炸膛坦客6 小时前
FreeRTOS 学习:(二十五)任务时间统计相关 API 函数
stm32·操作系统·freertos
时光の尘7 小时前
【STM32】两万字详解SD卡移植最新版本FatFs文件系统(ff16)
stm32·mcu·dma·sd·fatfs·sdio·ff16
bai5459369 小时前
STM32 CubeIDE 使用串口中断模式
stm32·单片机·嵌入式硬件
fanged10 小时前
STM32(4)--时钟树
stm32·单片机·嵌入式硬件
__万波__11 小时前
STM32L475蜂鸣器实验
stm32·单片机·嵌入式硬件
List<String> error_P13 小时前
STM32 GPIO HAL库常用函数
stm32·单片机·hal库
小痞同学14 小时前
【铁头山羊STM32】HAL库 5.SPI部分
stm32·单片机·嵌入式硬件
蓬荜生灰14 小时前
STM32(5)-- 新建寄存器版工程
stm32·单片机·嵌入式硬件
大神与小汪15 小时前
STM32上进行Unix时间戳转换
stm32·嵌入式硬件·unix