在nuc972板子上测试SPI noflash的时候发现启动比nand慢很多,从上电到进入console用了60秒。
通过抓SPI波形发现两个字节之间间隔有5us, 而发送一个字节所花费的时间只有1us左右,也就是说连个字节发送间隔太大导致大部分时间都被浪费在无效的等待上。
下图可以看出到两个发送字节之间比较宽(这已经是程序修改后的图形,修改前简直发一个字节然后空等一大段时间,非常稀疏)
SPI的驱动是 drivers\spi\spi-nuc970-p0.c, 发送函数是nuc970_spi0_txrx,发送一个自己成功会进中中断nuc970_spi0_irq, 在中断中又会检查是否所有数据都已发送完毕,如果没有则继续触发发送下一个字节nuc970_spi0_gobusy,然后又等待进中断。。。
因为进入中断是有堆栈切换之类的东西,每个字节发送完都要进中断触发下一个字节的发送,这样很多时间就浪费在中断切换上了。
去测试裸机SPI程序,发现裸机的发送两字节间隔只有700ns左右,远小于5us。裸机是直接忙等检查中断flag,不进入中断程序,如下:
for(i=0; i<256; i++) {
spiWrite(0, 0, 0x00);
spiIoctl(0, SPI_IOC_TRIGGER, 0, 0);
while(spiGetBusyStatus(0));
u8DataBuffer[i] = spiRead(0, 0);
}
按照裸机的这个裸机修改linux SPI的中断驱动后,发现SPI的性能提升很多,上电到进入console需要20s就够了。
以下是我修改后的函数:
static int nuc970_spi0_txrx(struct spi_device *spi, struct spi_transfer *t)
{
struct nuc970_spi *hw = (struct nuc970_spi *)to_hw(spi);
int i=0, offset;
unsigned int status;
hw->tx = t->tx_buf;
hw->rx = t->rx_buf;
hw->len = t->len;
hw->count = 0;
/*clear flag first*/
status = __raw_readl(hw->regs + REG_CNTRL);
__raw_writel(status, hw->regs + REG_CNTRL);
for(i = 0; i < hw->len; i++)
{
__raw_writel(hw_tx(hw, hw->count), hw->regs + REG_TX0);
nuc970_spi0_gobusy(hw);
/*wait int flag*/
status = __raw_readl(hw->regs + REG_CNTRL);
while((status & ENFLG) == 0)
{
status = __raw_readl(hw->regs + REG_CNTRL);
}
if (hw->rx)
hw_rx(hw, __raw_readl(hw->regs + REG_RX0), hw->count);
/*clear flag*/
__raw_writel(status, hw->regs + REG_CNTRL);
hw->count++;
}
return hw->count;
}
另外
request_irq(hw->irq, nuc970_spi0_irq, 0, pdev->name, hw);
这句直接注释掉了,应为SPI发送不在中断里面进行了。