am335x spi 在中断里直接调用底层api接口,提高数据读取速度。避免内核调用工作队列导致读取数据不及时,丢数据
项目描述:最近做一个am335x主控板做数据读取显示,fpga做采集板,FPGA数据采集玩后会给am335x发送一个中断,当am335x收到中断后,开启一个软中断通过spi总线读取fpga的数据
问题:由于fpga每个中断间隔是 < 1ms,在中断读取的时候调用sync接口,底层会开启一个工作队列,这样就会超时。导致数据读取错误。
解决思路:在中断里掉用底层的发送接口。这样避开内核创建工作队列的代码
一、中断里的修改
static irqreturn_t fpga_to_arm_hard_irq(int irq, void *dev_id) { int ret = IRQ_WAKE_THREAD; return ret; } static irqreturn_t fpga_to_arm_soft_irq(int irq, void *dev_id) { struct ad7606_spi_chip *ad7606_chip = dev_id; struct spi_device *spi = (struct spi_device*)ad7606_chip->spi; struct spi_message message; struct spi_transfer msg_buf; memset(&msg_buf, 0, sizeof(msg_buf)); msg_buf.bits_per_word = 8; msg_buf.speed_hz = spi->max_speed_hz; spi_message_init(&message); message.spi = spi; msg_buf.rx_buf = ad7606_chip->data; msg_buf.len = MAX_FRAME_LEN; spi_message_add_tail(&msg_buf, &message); //spi_sync(spi, &message); spi->master->cur_msg = &message; spi->master->prepare_message(spi->master,&message); spi->master->cur_msg_prepared = true; spi->master->transfer_one(spi->master,spi,&msg_buf); if(ad7606_chip->data[0] != 0x55) { pr_err("data 55 receive error, %d\n", ad7606_chip->data[0]); } if(ad7606_chip->data[1] != 0xAA) { pr_err("data AA receive error\n"); } if(ad7606_chip->data[2] != 0x33) { pr_err("data 33 receive error\n"); } if(ad7606_chip->data[3] != 0xBB) { pr_err("data BB receive error\n"); } if(ad7606_chip->data[22] != 0x66) { pr_err("data 66 receive error\n"); } if(ad7606_chip->data[23] != 0x66) { pr_err("data 66 receive error\n"); } memcpy((void*)&ad7606_chip->arm_map->buf[ad7606_chip->arm_map->tail], (void*)&ad7606_chip->data[4], MAX_FRAME_DATA_LEN); if(ad7606_chip->arm_map->tail < DATA_LEN) ad7606_chip->arm_map->tail += MAX_FRAME_DATA_LEN; if(ad7606_chip->arm_map->tail == DATA_LEN) ad7606_chip->arm_map->tail = 0; return IRQ_HANDLED; }
二、spi核心层
导出spi_map_msg 接口,改接口在使用dma时必须要调用
//static int spi_map_msg(struct spi_master *master, struct spi_message *msg) int spi_map_msg(struct spi_master *master, struct spi_message *msg) EXPORT_SYMBOL_GPL(spi_map_msg);
三、omap驱动
C:\z_linux_picohood_prj\board-support\linux-4.4.x-mainline\drivers\spi\spi-omap2-mcspi.c
3.1 omap2_mcspi_transfer_one 修改
当使用dma 方式时,发现调用系统卡死,发送数据到4096个字节就奔溃了,调试发现需要关闭fifo,应该是fifo有个阈值,dma发送速度超过这个fifo大小就卡死了
3.2 omap2_mcspi_transfer_one 修改
在调试时。因为我的spi-omap2-mcspi.c是模块加载的方式,因此在内核加载时就崩溃。
经过调试发现,直接调用底层时需要再次打开电源管理,否则内核直接挂掉。
出错的现象,这个原因找了很久才知道时电源的问题,还要抽空仔细看看电源管理
3.3 omap2_mcspi_transfer_one 修改
当驱动直接调用底层接口时为了实现快速的数据读取,采用dma的方式。dma的方式需要修改2个地方
添加映射
/*add by lsh*/ spi_map_msg(master, master->cur_msg);
修改最小字节数
/*add by lsh*/ //#define DMA_MIN_BYTES 160 #define DMA_MIN_BYTES 0
3.3 omap2_mcspi_prepare_message修改
改了上面接口。发现驱动能跑起来。但是一直卡在驱动里。提示数据接收超时
又是艰苦的调试。分析代码加打印。各种折腾。最后确定是底层接口还需要调用 omap2_mcspi_set_cs
/*add by lsh*/ omap2_mcspi_set_cs(msg->spi, msg->spi->mode & SPI_CS_HIGH);
pm_runtime_get_sync(mcspi->dev);
到此算是满足了要求
解决的思路:从spi_sync(spi, &message);
分析spi_sync的具体调用过程。看其中用到哪些接口。排除不必要的接口,过程是很漫长。有空再写