20260120 - Linux驱动学习笔记:SPI子系统核心层到具体硬件驱动

详细追踪从spi.c中的函数接口 spi_write()spi-imx.c 中具体硬件操作的完整调用链

完整的函数调用链

c 复制代码
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
第1步:应用层/设备驱动调用
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// oled_drv.c - 具体的字符设备驱动程序
static void spi_write_datas(const unsigned char *buf, int len)
{
    spi_write(oled, buf, len);
    //        ↑
    //        oled 是 struct spi_device*
}

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
第2步:SPI 核心层 - spi_write()	- spi.c 作为核心层
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// drivers/spi/spi.c
int spi_write(struct spi_device *spi, const void *buf, size_t len)
{
    struct spi_transfer t = {
        .tx_buf = buf,
        .len    = len,
    };
    
    return spi_sync_transfer(spi, &t, 1);
    //     ↓
}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
第3步:SPI 核心层 - spi_sync_transfer()
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// drivers/spi/spi.c
int spi_sync_transfer(struct spi_device *spi, 
                     struct spi_transfer *xfers,
                     unsigned int num_xfers)
{
    struct spi_message msg;
    
    // 初始化消息
    spi_message_init(&msg);
    
    // 将 transfer 加入消息
    spi_message_add_tail(&xfers[0], &msg);
    //  ↓
    //  msg.transfers 链表中有一个 transfer
    
    return spi_sync(spi, &msg);
    //     ↓
}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
第4步:SPI 核心层 - spi_sync()
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// drivers/spi/spi.c
int spi_sync(struct spi_device *spi, struct spi_message *message)
{
    int ret;
    
    mutex_lock(&spi->master->bus_lock_mutex);
    ret = __spi_sync(spi, message);
    //    ↓
    mutex_unlock(&spi->master->bus_lock_mutex);
    
    return ret;
}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
第5步:SPI 核心层 - __spi_sync()
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// drivers/spi/spi.c
static int __spi_sync(struct spi_device *spi, struct spi_message *message)
{
    DECLARE_COMPLETION_ONSTACK(done);
    int status;
    struct spi_master *master = spi->master;
    //                          ↑
    //                          获取对应的 spi_master
    //                          这个 master 是 spi-imx.c 创建的
    
    // 验证消息
    status = __spi_validate(spi, message);
    if (status != 0)
        return status;
    
    // 设置完成回调
    message->complete = spi_complete;
    message->context = &done;
    message->spi = spi;
    
    // 关键判断:新方法还是老方法
    if (master->transfer == spi_queued_transfer) {
        //     ↑
        //     master->transfer 在 spi_register_master() 时设置的
        //     如果 spi-imx.c 没有实现 master->transfer
        //     内核会自动设置为 spi_queued_transfer
        
        // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
        // 新方法(队列化传输)- 大多数驱动走这里
        // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
        
        spin_lock_irqsave(&master->bus_lock_spinlock, flags);
        
        status = __spi_queued_transfer(spi, message, false);
        //       ↓
        
        spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);
        
        if (status == 0) {
            // 在当前上下文中处理(优化)
            __spi_pump_messages(master, false);
            //  ↓
            
            // 等待完成
            wait_for_completion(&done);
            status = message->status;
        }
        
    } else {
        // 老方法(直接调用驱动的 transfer 函数)
        status = spi_async_locked(spi, message);
    }
    
    return status;
}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
第6步:SPI 核心层 - __spi_queued_transfer()
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// drivers/spi/spi.c
static int __spi_queued_transfer(struct spi_device *spi,
                                struct spi_message *msg,
                                bool need_pump)
{
    struct spi_master *master = spi->master;
    unsigned long flags;
    
    spin_lock_irqsave(&master->queue_lock, flags);
    
    // 检查 master 是否运行
    if (!master->running) {
        spin_unlock_irqrestore(&master->queue_lock, flags);
        return -ESHUTDOWN;
    }
    
    // 初始化消息状态
    msg->actual_length = 0;
    msg->status = -EINPROGRESS;
    
    // 将消息加入队列
    list_add_tail(&msg->queue, &master->queue);
    //            ↑
    //            消息现在在队列中等待处理
    
    // 如果需要,唤醒工作线程
    if (!master->busy && need_pump)
        kthread_queue_work(&master->kworker, &master->pump_messages);
    
    spin_unlock_irqrestore(&master->queue_lock, flags);
    return 0;
}


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
第7步:SPI 核心层 - __spi_pump_messages()
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

// drivers/spi/spi.c
static void __spi_pump_messages(struct spi_master *master, bool in_kthread)
{
    unsigned long flags;
    bool was_busy = false;
    int ret;
    
    // 获取队列中的第一条消息
    spin_lock_irqsave(&master->queue_lock, flags);
    
    if (list_empty(&master->queue) || !master->running) {
        master->busy = false;
        spin_unlock_irqrestore(&master->queue_lock, flags);
        return;
    }
    
    // 取出消息
    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);
    
    // 准备硬件(如果定义了)
    if (!was_busy && master->prepare_transfer_hardware) {
        ret = master->prepare_transfer_hardware(master);
        //    ↓
        //    这可能调用到 spi-imx.c 的函数
    }
    
    // 准备消息(如果定义了)
    if (master->prepare_message) {
        ret = master->prepare_message(master, master->cur_msg);
        //    ↓
        //    这可能调用到 spi-imx.c 的函数
    }
    
    // 关键:传输消息
    if (master->transfer_one_message) {
        // 方式A:一次传输整个 message
        ret = master->transfer_one_message(master, master->cur_msg);
        //    ↓
        //    这会调用到 spi-imx.c 的函数!
        
    } else {
        // 方式B:逐个传输 transfer(大多数驱动用这个)
        ret = spi_transfer_one_message(master, master->cur_msg);
        //    ↓
    }
}


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
第8步:SPI 核心层 - spi_transfer_one_message()
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

// drivers/spi/spi.c
static int spi_transfer_one_message(struct spi_master *master,
                                   struct spi_message *msg)
{
    struct spi_transfer *xfer;
    int ret = 0;
    
    // 设置片选
    spi_set_cs(msg->spi, true);
    
    // 遍历消息中的所有 transfer
    list_for_each_entry(xfer, &msg->transfers, transfer_list) {
        //                  ↑
        //                  msg->transfers 链表中的每个 transfer
        
        // 传输单个 transfer
        ret = master->transfer_one(master, msg->spi, xfer);
        //           ↑
        //           关键!这里调用 spi-imx.c 的 transfer_one 函数
        //           ↓
        //           ↓
        //           到达 spi-imx.c!
        
        if (ret < 0) {
            // 传输失败
            spi_transfer_one_message_failed(master, msg, ret);
            return ret;
        }
        
        // 如果需要延迟
        if (xfer->delay_usecs)
            udelay(xfer->delay_usecs);
        
        // 如果需要改变片选
        if (xfer->cs_change) {
            spi_set_cs(msg->spi, false);
            udelay(10);
            spi_set_cs(msg->spi, true);
        }
    }
    
    // 释放片选
    spi_set_cs(msg->spi, false);
    
    // 完成消息
    msg->status = 0;
    spi_finalize_current_message(master);
    
    return 0;
}

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
第9步:SPI Master 驱动 - spi_imx_transfer_one()
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

// drivers/spi/spi-imx.c
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);
    unsigned long hz_per_byte, byte_limit;
    
    // 配置传输参数
    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);
    
    // 配置 SPI 控制器寄存器
    spi_imx_config(spi_imx);
    //  ↓
    //  写硬件寄存器
    
    // 根据传输大小和是否支持 DMA 选择传输方式
    if (spi_imx->usedma) {
        // 使用 DMA 传输
        ret = spi_imx_dma_transfer(spi_imx, transfer);
        //    ↓
        wait_for_completion(&spi_imx->dma_tx_completion);
        wait_for_completion(&spi_imx->dma_rx_completion);
        
    } else {
        // 使用 PIO(中断)传输
        
        // 启动传输(写 FIFO,触发中断)
        spi_imx_push(spi_imx);
        //  ↓
        //  写数据到 TX FIFO
        //  启用中断
        
        // 等待传输完成
        wait_for_completion(&spi_imx->xfer_done);
        //  ↑
        //  中断处理函数会调用 complete(&spi_imx->xfer_done)
    }
    
    return 0;
}


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
第10步:硬件操作 - spi_imx_config()
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

// drivers/spi/spi-imx.c
static int spi_imx_config(struct spi_imx_data *spi_imx)
{
    unsigned int ctrl = MX51_ECSPI_CTRL_ENABLE;
    u32 clkdiv;
    
    // 计算时钟分频
    clkdiv = spi_imx_clkdiv_2(spi_imx->spi_clk, spi_imx->speed_hz);
    
    // 配置控制寄存器
    ctrl |= (spi_imx->bits_per_word - 1) << MX51_ECSPI_CTRL_BL_OFFSET;
    
    if (spi_imx->spi->mode & SPI_CPHA)
        ctrl |= MX51_ECSPI_CTRL_PHA;
    
    if (spi_imx->spi->mode & SPI_CPOL)
        ctrl |= MX51_ECSPI_CTRL_POL;
    
    // 写寄存器
    writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL);
    //     ↑
    //     直接写硬件寄存器!
    
    writel(clkdiv, spi_imx->base + MX51_ECSPI_CONFIG);
    
    return 0;
}


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
第11步:硬件操作 - spi_imx_push()
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

// drivers/spi/spi-imx.c
static void spi_imx_push(struct spi_imx_data *spi_imx)
{
    unsigned int burst_length;
    
    // 计算可以发送多少字节
    burst_length = spi_imx_get_fifosize(spi_imx) - spi_imx->txfifo;
    
    // 填充 TX FIFO
    while (spi_imx->txfifo < burst_length && spi_imx->count) {
        if (spi_imx->tx_buf) {
            // 发送数据
            spi_imx->tx(spi_imx);
            //       ↓
            //       根据字长调用不同的函数
            //       写数据到 TX FIFO 寄存器
        } else {
            // 发送 dummy 数据
            writel(0, spi_imx->base + MXC_CSPITXDATA);
        }
        
        spi_imx->txfifo++;
    }
    
    // 启用中断
    if (!spi_imx->usedma)
        spi_imx_intctrl(spi_imx, MXC_INT_TE | MXC_INT_RR);
    //  ↑
    //  启用发送空和接收就绪中断
}


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
第12步:中断处理 - spi_imx_isr()
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

// drivers/spi/spi-imx.c
static irqreturn_t spi_imx_isr(int irq, void *dev_id)
{
    struct spi_imx_data *spi_imx = dev_id;
    
    // 读取接收到的数据
    while (spi_imx->txfifo > 0) {
        // 从 RX FIFO 读取
        spi_imx->rx(spi_imx);
        //       ↓
        //       readl(spi_imx->base + MXC_CSPIRXDATA)
        
        spi_imx->txfifo--;
    }
    
    // 如果还有数据要发送
    if (spi_imx->count) {
        spi_imx_push(spi_imx);  // 继续发送
        return IRQ_HANDLED;
    }
    
    // 传输完成
    if (spi_imx->txfifo == 0) {
        // 禁用中断
        spi_imx_intctrl(spi_imx, 0);
        
        // 通知传输完成
        complete(&spi_imx->xfer_done);
        //  ↑
        //  这会唤醒 spi_imx_transfer_one() 中的 wait_for_completion()
    }
    
    return IRQ_HANDLED;
}


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
返回路径:层层返回
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

spi_imx_transfer_one() 返回
    ↓
spi_transfer_one_message() 继续下一个 transfer 或完成
    ↓
spi_finalize_current_message() 调用完成回调
    ↓
spi_complete() 被调用
    ↓
complete(&done)  // __spi_sync() 中的完成量
    ↓
wait_for_completion(&done) 返回  // __spi_sync() 中
    ↓
__spi_sync() 返回
    ↓
spi_sync() 返回
    ↓
spi_sync_transfer() 返回
    ↓
spi_write() 返回
    ↓
spi_write_datas() 返回  // 你的代码

关键连接点

连接点1:spi_device → spi_master

c 复制代码
// oled_drv.c
static struct spi_device *oled;

static int spidev_probe(struct spi_device *spi)
{
    oled = spi;
    // spi->master 指向 spi-imx.c 创建的 spi_master
}

// 调用时
spi_write(oled, buf, len);
//        ↓
//  struct spi_master *master = oled->master;
//        ↓
//  这个 master 就是 spi-imx.c 中的 master

连接点2:函数指针跳转

c 复制代码
// spi-imx.c 注册时设置的
static int spi_imx_probe(struct platform_device *pdev)
{
    struct spi_master *master;
    master = spi_alloc_master(...);
    
    // 设置函数指针
    master->transfer_one = spi_imx_transfer_one;
    //                     ↑
    //                     这个函数的地址保存在 master 结构中
    
    spi_register_master(master);
}

// SPI 核心层调用时
// drivers/spi/spi.c
ret = master->transfer_one(master, msg->spi, xfer);
//    ↑
//    通过函数指针调用
//    实际调用的是 spi_imx_transfer_one()

完整调用链图示

复制代码
oled_drv.c:spi_write_datas()
    ↓ 调用
spi.c:spi_write()
    ↓ 调用
spi.c:spi_sync_transfer()
    ↓ 调用
spi.c:spi_sync()
    ↓ 调用
spi.c:__spi_sync()
    ↓ 调用
spi.c:__spi_queued_transfer()  [消息入队]
    ↓ 返回
spi.c:__spi_pump_messages()    [从队列取消息]
    ↓ 调用
spi.c:spi_transfer_one_message()  [遍历 transfers]
    ↓ 调用 (函数指针)
spi-imx.c:spi_imx_transfer_one()  ← 进入硬件驱动!
    ↓ 调用
spi-imx.c:spi_imx_config()     [配置硬件寄存器]
    ↓ 调用
spi-imx.c:spi_imx_push()       [写 TX FIFO]
    ↓ 启用中断
    ↓ wait_for_completion()    [等待中断]
    ↑ complete()
spi-imx.c:spi_imx_isr()        [中断处理]
    ↓ 返回
spi-imx.c:spi_imx_transfer_one() 返回
    ↓ 返回
spi.c:spi_transfer_one_message() 返回
    ↓ 调用
spi.c:spi_finalize_current_message()
    ↓ 调用回调
spi.c:spi_complete()
    ↓ complete(&done)
spi.c:__spi_sync() 的 wait_for_completion() 返回
    ↓ 层层返回
oled_drv.c:spi_write_datas() 返回

数据结构关系

c 复制代码
// 数据结构的关联关系

struct spi_device *oled {
    .master = &imx_spi_master,  // 指向 spi-imx.c 创建的 master
    // ...
}
    ↓ 关联
struct spi_master imx_spi_master {
    .transfer_one = spi_imx_transfer_one,  // 函数指针
    .prepare_transfer_hardware = spi_imx_prepare_hardware,
    .unprepare_transfer_hardware = spi_imx_unprepare_hardware,
    // ...
}
    ↓ 实现
// spi-imx.c 中的实际函数
static int spi_imx_transfer_one(...) { ... }
static int spi_imx_prepare_hardware(...) { ... }
static int spi_imx_unprepare_hardware(...) { ... }

总结

步骤 函数 文件 说明
1 spi_write_datas() oled_drv.c 你的驱动调用
2 spi_write() spi.c SPI 核心 API
3 spi_sync_transfer() spi.c 构造消息
4 spi_sync() spi.c 同步传输
5 __spi_sync() spi.c 核心同步逻辑
6 __spi_queued_transfer() spi.c 消息入队
7 __spi_pump_messages() spi.c 处理消息队列
8 spi_transfer_one_message() spi.c 遍历 transfers
9 master->transfer_one() 函数指针 跳转到硬件驱动
10 spi_imx_transfer_one() spi-imx.c i.MX 硬件传输
11 spi_imx_config() spi-imx.c 配置硬件
12 spi_imx_push() spi-imx.c 写 FIFO
13 spi_imx_isr() spi-imx.c 中断处理

关键点 :从 SPI 核心层到具体硬件驱动的跳转是通过 master->transfer_one 函数指针实现的!

相关推荐
逐步前行4 小时前
STM32_标准库结构
stm32·单片机·嵌入式硬件
Hello_Embed4 小时前
libmodbus STM32 主机实验(USB 串口版)
笔记·stm32·学习·嵌入式·freertos·modbus
不做无法实现的梦~5 小时前
PX4各个模块的作用(3)
linux·stm32·嵌入式硬件·机器人·自动驾驶
不能跑的代码不是好代码5 小时前
STM32独立看门狗(IWDG)知识点及标准库使用指南
stm32·嵌入式硬件
-Springer-6 小时前
STM32 学习 —— 个人学习笔记5(EXTI 外部中断 & 对射式红外传感器及旋转编码器计数)
笔记·stm32·学习
xuxg200521 小时前
4G 模组 AT 命令解析框架课程正式发布
stm32·嵌入式·at命令解析框架
CODECOLLECT1 天前
京元 I62D Windows PDA 技术拆解:Windows 10 IoT 兼容 + 硬解码模块,如何降低工业软件迁移成本?
stm32·单片机·嵌入式硬件
BackCatK Chen1 天前
STM32+FreeRTOS:嵌入式开发的黄金搭档,未来十年就靠它了!
stm32·单片机·嵌入式硬件·freertos·低功耗·rtdbs·工业控制
全栈游侠1 天前
STM32F103XX 02-电源与备份寄存器
stm32·单片机·嵌入式硬件
辰哥单片机设计1 天前
STM32项目分享:车辆防盗报警系统
stm32·单片机·嵌入式硬件