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));
}