spi整体框架如下:
其中我主要讲到的是如下红框的内容
1.先说一下这个bitbang_work的由来
在spi设备驱动(主要是设备)的初始化中/drivers/spi/fsl_espi.c
fsl_espi_probe中调用了spi_bitbang_start,其中注册了队列处理bitbang_work
INIT_WORK(&bitbang->work, bitbang_work);
这就是它的由来了。
2.bitbang_work就实现了上图右侧部分的取message,这个message就是包含该主从设备配置和真实要发送的信息
简单看一下其中的几段代码
1)从message中获取配置数据然后配置到master
跟进去后会发现是设置master的位宽、模式、时钟频率、片选等
/* init (-1) or override (1) transfer params */ if (do_setup != 0) { if (!setup_transfer) { status = -ENOPROTOOPT; break; } status = setup_transfer(spi, t); if (status < 0) break; }
/* set up default clock polarity, and activate chip; * this implicitly updates clock and spi modes as * previously recorded for this device via setup(). * (and also deselects any other chip that might be * selected ...) */ if (cs_change) { bitbang->chipselect(spi, BITBANG_CS_ACTIVE); ndelay(nsecs); }
3)数据发送
这个就是数据收发的关键
/* transfer data. the lower level code handles any * new dma mappings it needs. our caller always gave * us dma-safe buffers. */ if (t->len) { /* REVISIT dma API still needs a designated * DMA_ADDR_INVALID; ~0 might be better. */ if (!m->is_dma_mapped) t->rx_dma = t->tx_dma = 0; status = bitbang->txrx_bufs(spi, t); }
3.数据收发具体实现
static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) { struct fsl_espi *fsl_espi; u32 word, len, bits_per_word; fsl_espi = spi_master_get_devdata(spi->master); fsl_espi->tx = t->tx_buf; fsl_espi->rx = t->rx_buf; bits_per_word = spi->bits_per_word; if (t->bits_per_word) bits_per_word = t->bits_per_word; len = t->len; fsl_espi->count = len; /* every frame owns one byte */ out_be32(&fsl_espi->regs->command, (spi->chip_select << 30) | (len - 1)); INIT_COMPLETION(fsl_espi->done); /* enable rx ints */ out_be32(&fsl_espi->regs->mask, SPIM_NE); /* transmit word */ word = fsl_espi->get_tx(fsl_espi); out_be32(&fsl_espi->regs->transmit, word); wait_for_completion(&fsl_espi->done); return t->len; }①设置master的command寄存器,要发送的字符长度和从设备的片选
②初始化完成量
③使能收中断
④发送第一个字
⑤完成量等待
这个部分不难理解,重点就是在之后数据的收发
来看一下中断处理函数
irqreturn_t fsl_espi_irq(s32 irq, void *context_data) { struct fsl_espi *fsl_espi = context_data; u32 event, rx_data, word; int ret; /* Get interrupt events(tx/rx) */ event = in_be32(&fsl_espi->regs->event); /* We need handle RX first */ if (event & SPIE_NE) { /* spin until RX is done */ void *event_ptr = &fsl_espi->regs->event; int limit = min(4, fsl_espi->count); ret = spin_event_timeout( SPIE_RXCNT(event = in_be32(event_ptr)) >= limit, 500, 0); if (!ret) return IRQ_NONE; rx_data = in_be32(&fsl_espi->regs->receive); if (fsl_espi->rx) fsl_espi->get_rx(rx_data, fsl_espi); } else { /* Clear the events */ out_be32(&fsl_espi->regs->event, event); return IRQ_HANDLED; } fsl_espi->count -= 4; if (fsl_espi->count > 0) { if ((event & SPIE_NF) == 0) { /* spin until TX is done */ ret = spin_event_timeout((event = in_be32(&fsl_espi->regs->event)) & SPIE_NF, 500, 0); if (!ret) return IRQ_NONE; } word = fsl_espi->get_tx(fsl_espi); out_be32(&fsl_espi->regs->transmit, word); } else { fsl_espi->count = 0; /* disable rx ints */ out_be32(&fsl_espi->regs->mask, 0); complete(&fsl_espi->done); } /* Clear the events */ out_be32(&fsl_espi->regs->event, event); return IRQ_HANDLED; }
⑥读取中断,判断中断类型
⑦读取数据,保存
⑧判断剩余长度,若没发送完继续发送一个字
⑨已发送完,关中断,返回完成量
⑩清中断,返回
关于这个中断处理容易进入一个误区,就是:
先发送一个字后,从设备回应给主设备时产生的中断
那么按照这个思路分析,如果从设备收到数据后故意不给回应(fpga可以模拟spi时序),那程序会一直等在⑤的地方
导致spi异常死锁...
这种情况是一定不能出现的
正确的机制是:当spi片选中某一从设备,发送第一个字时,片选、时钟、MOSI、MISO会同时有效
通俗一点就是,当我们在发数据的时候spi同时在接收数据(全双工)
即使从设备没有发数据,spi的外围电路会将MISO拉高或拉低,同样相当于数据进来了。
那么就很明了了,过程如下:
Ⅰ、使能收中断,发送第一个字,同时收到来的数据
Ⅱ、收中断到来,收取数据
Ⅲ、继续发送剩余数据
Ⅱ、Ⅲ循环
Ⅳ、数据发送完成,关中断,完成量返回