Linux驱动学习笔记:spi-imx.c收发消息的核心流程

spi-imx.c 分析策略与核心流程

一、spi-imx.c分析顺序

复制代码
1. probe函数 → 理解初始化做了什么
2. 回调函数注册 → 找到关键回调
3. 数据传输路径 → 跟踪实际传输流程
4. 硬件操作细节 → 理解寄存器操作

二、核心关键:spi-bitbang.c 的介入

重大发现

c 复制代码
/* spi_imx_probe 中 */
spi_imx->bitbang.chipselect = spi_imx_chipselect;
spi_imx->bitbang.setup_transfer = spi_imx_setupxfer;
spi_imx->bitbang.txrx_bufs = spi_imx_transfer;  // ← 最关键!

ret = spi_bitbang_start(&spi_imx->bitbang);  // ← 这里注册了回调!

关键理解

  • spi-imx.c 只实现了简单的 transfer_one(单次传输)接口(在bitbang.c中初始化为master->transfer_one = spi_bitbang_transfer_one),而没有实现复杂的 transfer_one_message(整个消息处理)接口,因此内核会自动使用核心层中的函数spi_transfer_one_message来作为"总指挥"。
  • 核心层的spi_transfer_one_message中,在发起每一次的xfer单次传输时,都会调用master->transfer_one,也就是spi_bitbang_transfer_one,而在函数spi_bitbang_transfer_one中,会调用txrx_bufs(spi, transfer),也就是spi_imx_transfer
  • spi_transfer_one_message->transfer_one->spi_bitbang_transfer_one->txrx_bufs->spi_imx_transfer.

三、调用链重建

从spi.c到spi-imx.c的完整路径

复制代码
用户态:
ioctl(SPI_IOC_MESSAGE, xfer)
    ↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
VFS层:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
sys_ioctl()
    ↓
file->f_op->unlocked_ioctl
    ↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
drivers/spi/spidev.c:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
spidev_ioctl()
    ↓
    识别命令:SPI_IOC_MESSAGE
    ↓
spidev_message()
    ├─ 拷贝TX数据:copy_from_user(tx_buf, user_tx, len)
    ├─ 构造spi_transfer和spi_message
    └─ 调用 spi_sync()
       ↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
drivers/spi/spi.c(SPI核心层):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
spi_sync()
    ↓
__spi_sync()
    ├─ 设置完成量
    └─ 调用 __spi_async()
       ↓
__spi_async()
    ├─ 验证参数
    └─ 将message加入队列
       ↓
__spi_pump_messages()  ← 队列处理函数
    ├─ 从队列取出message
    ├─ 准备硬件(prepare_transfer_hardware)
    ├─ 准备消息(prepare_message)
    ├─ 映射DMA(spi_map_msg)
    └─ 调用 master->transfer_one_message()  ← 关键跳转!
       ↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
drivers/spi/spi.c(SPI核心层的默认实现):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
spi_transfer_one_message()  ← 核心层提供的包装函数
    ↓
    /* ========== 关键:在这里遍历message ========== */
    list_for_each_entry(xfer, &msg->transfers, transfer_list) {
        ↓
        /* 调用驱动的transfer_one */
        ret = master->transfer_one(master, msg->spi, xfer);
        ↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
drivers/spi/spi-bitbang.c(Bitbang框架):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
spi_bitbang_transfer_one()  ← 只处理单个transfer!
        ↓
        /* 配置传输参数 */
        if (bitbang->setup_transfer)
            bitbang->setup_transfer(spi, t);  → spi_imx_setupxfer()
        ↓
        /* 执行实际传输 */
        status = bitbang->txrx_bufs(spi, t);  → spi_imx_transfer()
        ↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
drivers/spi/spi-imx.c(平台驱动):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
spi_imx_transfer()  ← 处理单个transfer
        ↓
        if (spi_imx->usedma)
            spi_imx_dma_transfer()
        else
            spi_imx_pio_transfer()
            ↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PIO模式详细流程:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
spi_imx_pio_transfer()
            ├─ 设置缓冲区指针:tx_buf, rx_buf, count
            ├─ 重置完成量:reinit_completion(&xfer_done)
            ├─ 填充TX FIFO:spi_imx_push()
            │   ↓
            │   while (txfifo < FIFO_SIZE && count > 0) {
            │       spi_imx->tx(spi_imx)  → 写寄存器
            │       txfifo++
            │   }
            │   ↓
            │   devtype_data->trigger()  → 启动硬件传输
            │
            ├─ 使能中断:intctrl(spi_imx, MXC_INT_TE)
            └─ 等待完成:wait_for_completion(&xfer_done)
                ↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
中断处理:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
spi_imx_isr()  ← 硬件中断触发
                ↓
                /* 读取RX FIFO */
                while (rx_available()) {
                    spi_imx->rx(spi_imx)  → 读寄存器
                    txfifo--
                }
                ↓
                if (count > 0) {
                    /* 还有数据,继续发送 */
                    spi_imx_push()
                } else if (txfifo > 0) {
                    /* 等待最后的接收 */
                    intctrl(spi_imx, MXC_INT_RR)
                } else {
                    /* 传输完成 */
                    intctrl(spi_imx, 0)  → 关闭中断
                    complete(&xfer_done)  → 唤醒等待线程
                }
                ↓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
返回路径:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
complete(&xfer_done)唤醒
    ↓
spi_imx_pio_transfer() 返回
    ↓
spi_imx_transfer() 返回
    ↓
spi_bitbang_transfer_one() 返回
    ↓ (继续处理下一个transfer)
    }  ← 结束for_each_entry循环
    ↓
spi_transfer_one_message() 完成
    ↓
spi_finalize_current_message()
    ├─ unprepare_message()  → 关闭时钟
    ├─ master->cur_msg = NULL
    └─ complete(msg->context)  → 唤醒用户线程
       ↓
__spi_sync() 被唤醒
    ↓
spi_sync() 返回
    ↓
spidev_message() 返回
    ├─ 拷贝RX数据:copy_to_user(user_rx, rx_buf, len)
    └─ 返回传输字节数
       ↓
spidev_ioctl() 返回
    ↓
用户态:ioctl() 返回

四、关键函数详解

4.1 spi_bitbang_start(注册回调)

c 复制代码
// drivers/spi/spi-bitbang.c
int spi_bitbang_start(struct spi_bitbang *bitbang)
{
    struct spi_master *master = bitbang->master;
    
    /* 关键:注册 transfer_one_message 回调 */
    if (!master->transfer_one_message)
        master->transfer_one_message = spi_bitbang_transfer_one;
    
    /* 其他初始化... */
    
    return spi_register_master(master);
}

4.2 spi_bitbang_transfer_one(bitbang核心)

c 复制代码
// drivers/spi/spi-bitbang.c
static int spi_bitbang_transfer_one(struct spi_master *master,
                                     struct spi_message *msg)
{
    struct spi_bitbang *bitbang = spi_master_get_devdata(master);
    struct spi_transfer *t;
    
    printk("[BITBANG_TRACE] transfer_one: msg=%px\n", msg);
    
    /* 遍历消息中的每个传输段 */
    list_for_each_entry(t, &msg->transfers, transfer_list) {
        
        printk("[BITBANG_TRACE] Processing transfer: len=%u\n", t->len);
        
        /* 1. 片选激活 */
        if (bitbang->chipselect) {
            bitbang->chipselect(msg->spi, BITBANG_CS_ACTIVE);
        }
        
        /* 2. 配置传输参数(速度、位宽等) */
        if (bitbang->setup_transfer) {
            bitbang->setup_transfer(msg->spi, t);
        }
        
        /* 3. 执行实际传输 ← 最关键! */
        if (bitbang->txrx_bufs) {
            status = bitbang->txrx_bufs(msg->spi, t);
            //          ↑ 调用 spi_imx_transfer()
        }
        
        /* 4. 延迟(如果需要) */
        if (t->delay_usecs)
            udelay(t->delay_usecs);
        
        /* 5. 片选控制 */
        if (t->cs_change) {
            bitbang->chipselect(msg->spi, BITBANG_CS_INACTIVE);
        }
    }
    
    /* 6. 传输完成,调用回调 */
    msg->status = 0;
    msg->actual_length = /* 累加所有transfer的len */;
    spi_finalize_current_message(master);
    
    return 0;
}

五、spi-imx.c 分析要点

5.1 probe函数(初始化)

c 复制代码
static int spi_imx_probe(struct platform_device *pdev)
{
    struct spi_master *master;
    struct spi_imx_data *spi_imx;
    
    /* 1. 分配master */
    master = spi_alloc_master(&pdev->dev, sizeof(*spi_imx));
    
    spi_imx = spi_master_get_devdata(master);
    spi_imx->bitbang.master = master;
    
    /* 2. 识别芯片型号(从设备树) */
    spi_imx->devtype_data = of_id->data;
    //                       ↑ 指向 imx51_ecspi_devtype_data
    
    /* 3. 注册bitbang回调 ← 你找到的关键代码! */
    spi_imx->bitbang.chipselect = spi_imx_chipselect;
    spi_imx->bitbang.setup_transfer = spi_imx_setupxfer;
    spi_imx->bitbang.txrx_bufs = spi_imx_transfer;  // ← 传输入口
    
    /* 4. 注册master回调 */
    spi_imx->bitbang.master->setup = spi_imx_setup;
    spi_imx->bitbang.master->prepare_message = spi_imx_prepare_message;
    spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message;
    
    /* 5. 初始化硬件资源 */
    spi_imx->base = devm_ioremap_resource(&pdev->dev, res);  // 寄存器映射
    spi_imx->clk_per = devm_clk_get(&pdev->dev, "per");     // 时钟
    
    /* 6. 注册中断 */
    devm_request_irq(&pdev->dev, irq, spi_imx_isr, 0, ...);
    
    /* 7. 初始化DMA(如果支持) */
    if (is_imx51_ecspi(spi_imx)) {
        spi_imx_sdma_init(&pdev->dev, spi_imx, master);
    }
    
    /* 8. 复位硬件 */
    spi_imx->devtype_data->reset(spi_imx);
    //                      ↑ 调用 mx51_ecspi_reset()
    
    /* 9. 启动bitbang框架 ← 这里注册transfer_one_message */
    ret = spi_bitbang_start(&spi_imx->bitbang);
    
    return 0;
}

5.2 devtype_data(芯片差异抽象)

c 复制代码
static struct spi_imx_devtype_data imx51_ecspi_devtype_data = {
    .intctrl = mx51_ecspi_intctrl,        // 中断控制
    .config = mx51_ecspi_config,          // 硬件配置
    .trigger = mx51_ecspi_trigger,        // 启动传输
    .rx_available = mx51_ecspi_rx_available,  // 检查RX FIFO
    .reset = mx51_ecspi_reset,            // 复位控制器
    .devtype = IMX51_ECSPI,
};

设计模式:函数指针表,实现多型号支持


六、核心传输流程

6.1 spi_imx_transfer(传输入口)

c 复制代码
static int spi_imx_transfer(struct spi_device *spi,
                             struct spi_transfer *transfer)
{
    struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
    
    printk("[SPI_IMX_TRACE] transfer: len=%u, usedma=%d\n",
           transfer->len, spi_imx->usedma);
    
    if (spi_imx->usedma)
        return spi_imx_dma_transfer(spi_imx, transfer);
    else
        return spi_imx_pio_transfer(spi, transfer);
}

6.2 PIO模式传输(中断方式)

c 复制代码
static int spi_imx_pio_transfer(struct spi_device *spi,
                                 struct spi_transfer *transfer)
{
    struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
    
    printk("[SPI_IMX_TRACE] pio_transfer: ENTER\n");
    
    /* 1. 设置缓冲区指针 */
    spi_imx->tx_buf = transfer->tx_buf;
    spi_imx->rx_buf = transfer->rx_buf;
    spi_imx->count = transfer->len;
    spi_imx->txfifo = 0;
    
    /* 2. 重新初始化完成量 */
    reinit_completion(&spi_imx->xfer_done);
    
    /* 3. 填充TX FIFO并启动传输 */
    spi_imx_push(spi_imx);
    //  ↓
    //  while (txfifo < FIFO_SIZE && count > 0) {
    //      spi_imx->tx(spi_imx);  → 写TX寄存器
    //      txfifo++;
    //  }
    //  spi_imx->devtype_data->trigger(spi_imx);  → 启动硬件
    
    /* 4. 使能TX FIFO空中断 */
    spi_imx->devtype_data->intctrl(spi_imx, MXC_INT_TE);
    
    /* 5. 等待传输完成 */
    timeout = wait_for_completion_timeout(&spi_imx->xfer_done, ...);
    
    printk("[SPI_IMX_TRACE] pio_transfer: EXIT, status=%d\n",
           timeout ? 0 : -ETIMEDOUT);
    
    return timeout ? transfer->len : -ETIMEDOUT;
}

6.3 中断处理(核心!)

c 复制代码
static irqreturn_t spi_imx_isr(int irq, void *dev_id)
{
    struct spi_imx_data *spi_imx = dev_id;
    
    printk("[SPI_IMX_TRACE] ISR: ENTER\n");
    
    /* 1. 读取所有RX FIFO中的数据 */
    while (spi_imx->devtype_data->rx_available(spi_imx)) {
        spi_imx->rx(spi_imx);  // → 读RX寄存器
        //  ↓
        //  val = readl(spi_imx->base + MXC_CSPIRXDATA);
        //  if (spi_imx->rx_buf) {
        //      *(u8 *)spi_imx->rx_buf = val;
        //      spi_imx->rx_buf++;
        //  }
        
        spi_imx->txfifo--;  // TX FIFO空出一个位置
        
        printk("[SPI_IMX_TRACE] ISR: Received byte, txfifo=%u\n",
               spi_imx->txfifo);
    }
    
    /* 2. 如果还有数据要发送 */
    if (spi_imx->count) {
        spi_imx_push(spi_imx);  // 继续填充TX FIFO
        return IRQ_HANDLED;
    }
    
    /* 3. 如果TX FIFO还有数据未发送完 */
    if (spi_imx->txfifo) {
        /* 使能RX中断,等待最后的接收 */
        spi_imx->devtype_data->intctrl(spi_imx, MXC_INT_RR);
        return IRQ_HANDLED;
    }
    
    /* 4. 传输完成 */
    spi_imx->devtype_data->intctrl(spi_imx, 0);  // 关闭中断
    complete(&spi_imx->xfer_done);  // 唤醒等待线程
    
    printk("[SPI_IMX_TRACE] ISR: Transfer complete!\n");
    
    return IRQ_HANDLED;
}

七、推荐的分析步骤

步骤1:添加probe调试

c 复制代码
static int spi_imx_probe(struct platform_device *pdev)
{
    printk("[SPI_IMX_TRACE] ========== PROBE START ==========\n");
    
    // ...原有代码...
    
    spi_imx->devtype_data = of_id->data;
    printk("[SPI_IMX_TRACE] probe: devtype=%d, intctrl=%pS, config=%pS\n",
           spi_imx->devtype_data->devtype,
           spi_imx->devtype_data->intctrl,
           spi_imx->devtype_data->config);
    
    // ...
    
    spi_imx->bitbang.txrx_bufs = spi_imx_transfer;
    printk("[SPI_IMX_TRACE] probe: Registered txrx_bufs=%pS\n",
           spi_imx->bitbang.txrx_bufs);
    
    ret = spi_bitbang_start(&spi_imx->bitbang);
    printk("[SPI_IMX_TRACE] probe: spi_bitbang_start returned %d\n", ret);
    printk("[SPI_IMX_TRACE] probe: master->transfer_one_message=%pS\n",
           master->transfer_one_message);
    
    printk("[SPI_IMX_TRACE] ========== PROBE END ==========\n");
}

步骤2:添加传输路径跟踪

c 复制代码
static int spi_imx_transfer(struct spi_device *spi,
                             struct spi_transfer *transfer)
{
    printk("[SPI_IMX_TRACE] ==> spi_imx_transfer: len=%u\n", transfer->len);
    
    /* 原有代码 */
    
    printk("[SPI_IMX_TRACE] <== spi_imx_transfer: ret=%d\n", ret);
    return ret;
}

步骤3:添加硬件操作跟踪

c 复制代码
static void mx51_ecspi_trigger(struct spi_imx_data *spi_imx)
{
    u32 reg = readl(spi_imx->base + MX51_ECSPI_CTRL);
    
    printk("[SPI_IMX_TRACE] trigger: BEFORE ctrl=0x%08x\n", reg);
    
    if (!spi_imx->usedma)
        reg |= MX51_ECSPI_CTRL_XCH;
    
    writel(reg, spi_imx->base + MX51_ECSPI_CTRL);
    
    printk("[SPI_IMX_TRACE] trigger: AFTER  ctrl=0x%08x (XCH=%d)\n",
           reg, !!(reg & MX51_ECSPI_CTRL_XCH));
}
相关推荐
蓬荜生灰2 小时前
STM32(7)-- GPIO输出,寄存器版
stm32·单片机·嵌入式硬件
Zeku2 小时前
内核日志分析:__spi_pump_messages的Caller_Optimization和KWorker_Thread
stm32·freertos·linux驱动开发·linux应用开发
程序员_小兵3 小时前
STM32之中断详解
c语言·stm32·单片机·嵌入式硬件·mcu
List<String> error_P12 小时前
STM32窗口看门狗WWDG详解
stm32·单片机·嵌入式硬件·定时器
鑫—萍13 小时前
嵌入式开发学习——STM32单片机入门教程
c语言·驱动开发·stm32·单片机·嵌入式硬件·学习·硬件工程
boneStudent13 小时前
STM32H750多通道数据采集系统
stm32·单片机·嵌入式硬件
youcans_15 小时前
【动手学STM32G4】(13)STM32G431之 TIM+ADC
stm32·单片机·嵌入式硬件·定时器
小灰灰搞电子17 小时前
STM32、GD32 ppm协议解析源码分享
stm32·ppm协议
List<String> error_P19 小时前
独立看门狗IWDG原理详解
stm32·单片机·嵌入式硬件·定时器