spi-imx.c 分析策略与核心流程
一、spi-imx.c分析顺序
1. probe函数 → 理解初始化做了什么 2. 回调函数注册 → 找到关键回调 3. 数据传输路径 → 跟踪实际传输流程 4. 硬件操作细节 → 理解寄存器操作二、核心关键:spi-bitbang.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(注册回调)
// drivers/spi/spi-bitbang.cintspi_bitbang_start(structspi_bitbang*bitbang){structspi_master*master=bitbang->master;/* 关键:注册 transfer_one_message 回调 */if(!master->transfer_one_message)master->transfer_one_message=spi_bitbang_transfer_one;/* 其他初始化... */returnspi_register_master(master);}4.2 spi_bitbang_transfer_one(bitbang核心)
// drivers/spi/spi-bitbang.cstaticintspi_bitbang_transfer_one(structspi_master*master,structspi_message*msg){structspi_bitbang*bitbang=spi_master_get_devdata(master);structspi_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);return0;}五、spi-imx.c 分析要点
5.1 probe函数(初始化)
staticintspi_imx_probe(structplatform_device*pdev){structspi_master*master;structspi_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);return0;}5.2 devtype_data(芯片差异抽象)
staticstructspi_imx_devtype_dataimx51_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(传输入口)
staticintspi_imx_transfer(structspi_device*spi,structspi_transfer*transfer){structspi_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)returnspi_imx_dma_transfer(spi_imx,transfer);elsereturnspi_imx_pio_transfer(spi,transfer);}6.2 PIO模式传输(中断方式)
staticintspi_imx_pio_transfer(structspi_device*spi,structspi_transfer*transfer){structspi_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);returntimeout?transfer->len:-ETIMEDOUT;}6.3 中断处理(核心!)
staticirqreturn_tspi_imx_isr(intirq,void*dev_id){structspi_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 FIFOreturnIRQ_HANDLED;}/* 3. 如果TX FIFO还有数据未发送完 */if(spi_imx->txfifo){/* 使能RX中断,等待最后的接收 */spi_imx->devtype_data->intctrl(spi_imx,MXC_INT_RR);returnIRQ_HANDLED;}/* 4. 传输完成 */spi_imx->devtype_data->intctrl(spi_imx,0);// 关闭中断complete(&spi_imx->xfer_done);// 唤醒等待线程printk("[SPI_IMX_TRACE] ISR: Transfer complete!\n");returnIRQ_HANDLED;}七、推荐的分析步骤
步骤1:添加probe调试
staticintspi_imx_probe(structplatform_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:添加传输路径跟踪
staticintspi_imx_transfer(structspi_device*spi,structspi_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);returnret;}步骤3:添加硬件操作跟踪
staticvoidmx51_ecspi_trigger(structspi_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));}