金牌会员
- 积分
- 1255
- 金钱
- 1255
- 注册时间
- 2017-11-18
- 在线时间
- 296 小时
|
10金钱
Kernel_3.0.35版本和Kernel_4.1.15版本内核在SPI驱动实现机制的差异
一、Kernel_4.1.15版本
1.SPI控制器驱动(基于NXP处理器平台分析)
入口函数
static int spi_imx_probe(struct platform_device *pdev)
{
//1.控制器分配
master = spi_alloc_master(&pdev->dev, sizeof(struct spi_imx_data) + sizeof(int) * num_cs);
//2.SPI控制器初始化(芯片厂商负责实现)
spi_imx->bitbang.chipselect = spi_imx_chipselect;//片选信号
spi_imx->bitbang.setup_transfer = spi_imx_setupxfer;//初始化SPI传送函数(包含8/16/32位传送)
/* Initialize the functions for transfer */
if (config.bpw <= 8)
{
spi_imx->rx = spi_imx_buf_rx_u8;//实际上,这是一个宏,下同,在spi_imx.c文件开头
spi_imx->tx = spi_imx_buf_tx_u8;
spi_imx->tx_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
spi_imx->rx_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
}
else if (config.bpw <= 16)
{
spi_imx->rx = spi_imx_buf_rx_u16;
spi_imx->tx = spi_imx_buf_tx_u16;
spi_imx->tx_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
spi_imx->rx_config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
}
else
{
spi_imx->rx = spi_imx_buf_rx_u32;
spi_imx->tx = spi_imx_buf_tx_u32;
spi_imx->tx_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
spi_imx->rx_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
}
spi_imx->bitbang.txrx_bufs = spi_imx_transfer;
//如果使用DMA进行发送(实际上在3.0.35版本的内核不支持使用DMA进行数据发送)
if (spi_imx->bitbang.master->can_dma && spi_imx_can_dma(spi_imx->bitbang.master, spi, transfer))
{
spi_imx->usedma = true;
ret = spi_imx_dma_transfer(spi_imx, transfer);
spi_imx->usedma = false; /* clear the dma flag */
if (ret != -EAGAIN)
return ret;
}
//因为不支持DMA,所以最终走的逻辑是这里
return spi_imx_pio_transfer(spi, transfer);//里面调用数据发送函数
......
......
spi_imx_push(spi_imx);//里面调用spi_imx->tx进行数据发送
spi_imx->tx(spi_imx);//调用发送函数,关键:spi_imx->tx的赋值,在前面函数spi_imx_setupxfer中完成
......
spi_imx->bitbang.master->setup = spi_imx_setup;
spi_imx->bitbang.master->cleanup = spi_imx_cleanup;
spi_imx->bitbang.master->prepare_message = spi_imx_prepare_message;
spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message;
spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;//设置SPI模式
......
......
//3.获取中断资源
irq = platform_get_irq(pdev, 0);
......
//4.申请中断处理函数
ret = devm_request_irq(&pdev->dev, irq, spi_imx_isr, 0, dev_name(&pdev->dev), spi_imx);
......
//5.注册SPI控制器--->跳转分析
ret = spi_bitbang_start(&spi_imx->bitbang);
......
if (master->transfer || master->transfer_one_message)//前面没有设置,继续往下走
......
......
master->prepare_transfer_hardware = spi_bitbang_prepare_hardware;
master->unprepare_transfer_hardware = spi_bitbang_unprepare_hardware;
master->transfer_one_message = spi_bitbang_transfer_one;//发送一个spi数据
//前面已经设置,因此不会走这个if逻辑
if (!bitbang->txrx_bufs)
{
bitbang->use_dma = 0;
bitbang->txrx_bufs = spi_bitbang_bufs;
if (!master->setup)
{
if (!bitbang->setup_transfer)
bitbang->setup_transfer = spi_bitbang_setup_transfer;
master->setup = spi_bitbang_setup;
master->cleanup = spi_bitbang_cleanup;
}
}
ret = spi_register_master(spi_master_get(master));//注册SPI控制器
status = of_spi_register_master(master);//注册SPI控制器
......
INIT_LIST_HEAD(&master->queue);//初始队列头(SPI就是通过队列来处理数据)
......
......
status = device_add(&master->dev);//向内核中添加设备
.....
.....
/* If we're using a queued driver, start the queue */
if (master->transfer)//这个msster->transfer在前面并没有初始化,所以走else分支
dev_info(dev, "master is unqueued, this is deprecated\n");
else
{
status = spi_master_initialize_queue(master);//初始化一个队列
master->transfer = spi_queued_transfer;//由于前面没有初始化master->transfer函数,因此在这里进行初始化了
if (!master->transfer_one_message)//这个函数在前面spi_bitbang_start函数开头已经设置了,因此跳过这里的if逻辑
master->transfer_one_message = spi_transfer_one_message;
/* Initialize and start queue */
ret = spi_init_queue(master);//初始化一个队列
//使用内核线程的方式来实现SPI数据的处理()
init_kthread_worker(&master->kworker);
master->kworker_task = kthread_run(kthread_worker_fn, &master->kworker, "%s", dev_name(&master->dev));
if (IS_ERR(master->kworker_task))
{
dev_err(&master->dev, "failed to create message pump task\n");
return PTR_ERR(master->kworker_task);
}
init_kthread_work(&master->pump_messages, spi_pump_messages);
}
....
}
//以上,SPI控制器的初始化就完成了
//接下来分析数据传送的函数调用流程
//前面初始化传送函数是spi_imx->tx(spi_imx);那么实际驱动中是如何调用呢?
//发送函数(有同步和异步两种方式,在这里只分析同步方式)
int spi_sync(struct spi_device *spi, struct spi_message *message);
__spi_sync(spi, message, 0);//同步发送函数(函数会阻塞,可能引起睡眠,因此不能在中断上下文及tasklet中调用,只能在workqueue中使用)
......
......
status = __spi_queued_transfer(spi, message, false);//发送一个SPI数据(spi数据是一message数据块的形式进行发送)
......
......
queue_kthread_work(&master->kworker, &master->pump_messages);//调度内核线程
insert_kthread_work(worker, work, &worker->work_list);//到这里结束,剩下的转去内核线程的执行函数去分析spi_pump_messages
//内核线程的执行函数
static void spi_pump_messages(struct kthread_work *work)
.......
.......
ret = master->transfer_one_message(master, master->cur_msg);//调用前面在spi_bitbang_start函数中初始化的master->transfer_one_message = spi_bitbang_transfer_one;//发送一个spi数据
//转去分析
static int spi_bitbang_transfer_one(struct spi_master *master, struct spi_message *m)
/* 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);//因为前面没有使用DMA,所以正式传送数据,走的是这个逻辑,跳转去分析bitbang->txrx_bufs(spi, t);
}
//函数指针bitbang->txrx_bufs(spi, t);在spi_imx_probe函数中进行初始化,赋值为spi_imx_transfer
static int spi_imx_transfer(struct spi_device *spi, struct spi_transfer *transfer)
......
spi_imx_pio_transfer(spi, transfer);
......
spi_imx_push(spi_imx);//里面调用spi_imx->tx进行数据发送
spi_imx->tx(spi_imx);//调用发送函数,关键:spi_imx->tx的赋值,在前面函数spi_imx_setupxfer中完成(分析到这里,已经和前面的初始化发送函数完全对应上了)
二、Kernel_3.0.35版本
NXP官方在3.0.35版本的内核,控制器硬件平台的初始化和4.1.15版本的内核是差不多的,不同的是在两个内核版本之间,对于SPI的数据处理机制不一样
所以省去一部分相同的初始流程,直接分析差异。
static int __devinit spi_imx_probe(struct platform_device *pdev)
......
......
ret = spi_bitbang_start(&spi_imx->bitbang);
......
INIT_WORK(&bitbang->work, bitbang_work);//注意这里初始化了一个工作队列,这是在4.1.15版本的内核所没有的
//在调度这个工作队列的时候会执行bitbang_work函数
INIT_LIST_HEAD(&bitbang->queue);//初始化队列头
......
//前面没有初始化,所以这个if逻辑是成立的
if (!bitbang->master->transfer)
bitbang->master->transfer = spi_bitbang_transfer;//发送数据
if (!bitbang->txrx_bufs)//前面已经初始化了,所以这个逻辑不成立
{
bitbang->use_dma = 0;
bitbang->txrx_bufs = spi_bitbang_bufs;
if (!bitbang->master->setup)
{
if (!bitbang->setup_transfer)
bitbang->setup_transfer = spi_bitbang_setup_transfer;
bitbang->master->setup = spi_bitbang_setup;
bitbang->master->cleanup = spi_bitbang_cleanup;
}
}
else if (!bitbang->master->setup)//前面probe函数中也初始化了,所以这个逻辑也不成立
{
return -EINVAL;
}
if (bitbang->master->transfer == spi_bitbang_transfer && !bitbang->setup_transfer)//前面probe函数中也初始化了,所以这个逻辑也不成立
{
return -EINVAL;
}
......
//创建工作队列,但是这里有个疑问,调用create_singlethread_workqueue创建的工作队列,只能在CPU0上工作
//对于多CPU的情况,是不是没有发挥多CPU的效率优势呢?还是说内核在SPI的实现机制上有bug,避免竞争只能这么做?
//这种使用工作队列来实现SPI数据处理的方式和4.1.15版本有所不同(其实在3.1.14版本就已经不一样了)
bitbang->workqueue = create_singlethread_workqueue(dev_name(bitbang->master->dev.parent));
......
status = spi_register_master(bitbang->master);//向内核注册SPI设备
//前面已经调用INIT_WORK(&bitbang->work, bitbang_work);
//在调度这个工作队列的时候会执行bitbang_work函数,所以这里插入分析bitbang_work函数
static void bitbang_work(struct work_struct *work)
......
/* transfer data. the lower level code handles any
* new dma mappings it needs. our caller always gave
* us dma-safe buffers.
*/
if (t->len) //这部分是和4.1.15版本的内核一样
{
/* 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);//调用发送函数bitbang->txrx_bufs(spi, t);在probe中被赋值
spi_imx_push(spi_imx);
spi_imx->tx(spi_imx);
}
3.0.15版本SPI同步数据的调用流程
static int __spi_sync(struct spi_device *spi, struct spi_message *message, int bus_locked)
int spi_async_locked(struct spi_device *spi, struct spi_message *message)
ret = __spi_async(spi, message);
master->transfer(spi, message);//找到这个函数master->transfer(spi, message);的赋值,在int spi_bitbang_start(struct spi_bitbang *bitbang)中赋值的
bitbang->master->transfer = spi_bitbang_transfer;//发送数据
queue_work(bitbang->workqueue, &bitbang->work);//调度工作队列,执行工作队列函数
static void bitbang_work(struct work_struct *work)
spi_imx_push(spi_imx);
spi_imx->tx(spi_imx);
//到这里,初始化和发送的流程已经前后对应起来
//两个内核版本在SPI控制器的初始化流程都差不多,唯一不同的是内核在实现SPI最核心的数据处理时使用了两种不同的实现机制。
//4.1.15版本和3.0.35版本spi核心的实现方式差异,是为了解决什么问题呢?或者哪一种效率更高呢?希望有经验的前辈回答一下谢谢~
|
|