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 函数指针实现的!

相关推荐
羽获飞2 小时前
51单片机UART-串口通讯的配置方法
stm32·单片机·嵌入式硬件
猫猫的小茶馆2 小时前
【Linux 驱动开发】一. 搭建开发环境
linux·汇编·arm开发·驱动开发·stm32·嵌入式硬件·mcu
猫猫的小茶馆2 小时前
【Linux 驱动开发】嵌入式 Linux 开发概念
linux·服务器·arm开发·stm32·单片机·嵌入式硬件·mcu
兆龙电子单片机设计2 小时前
【STM32项目开源】STM32单片机智能宠物喂养系统
stm32·单片机·开源·毕业设计·电子信息
Y1rong2 小时前
STM32之串口(三)
stm32·单片机·嵌入式硬件
Y1rong3 小时前
STM32之串口(二)
stm32·单片机·嵌入式硬件
Y1rong3 小时前
STM32之串口(一)
网络·stm32·嵌入式硬件
想睡觉的树3 小时前
解决keil5编译慢的问题-亲测有效-飞一般的感觉
c语言·stm32·嵌入式硬件
__万波__3 小时前
STM32L475串口打印改为阻塞式打印兼DMA, 两种打印方式实时切换
stm32·单片机·嵌入式硬件