OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 4275|回复: 2

Kernel_3.0.35版本和Kernel_4.1.15版本内核在SPI驱动实现机制的差异

[复制链接]

82

主题

589

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1255
金钱
1255
注册时间
2017-11-18
在线时间
296 小时
发表于 2020-9-5 18:25:14 | 显示全部楼层 |阅读模式
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核心的实现方式差异,是为了解决什么问题呢?或者哪一种效率更高呢?希望有经验的前辈回答一下谢谢~


没有脑袋
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

82

主题

589

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1255
金钱
1255
注册时间
2017-11-18
在线时间
296 小时
 楼主| 发表于 2020-9-5 18:26:23 | 显示全部楼层
知其然也要知其所以然
没有脑袋
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165353
金钱
165353
注册时间
2010-12-1
在线时间
2108 小时
发表于 2020-9-7 01:45:36 | 显示全部楼层
帮顶
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-25 17:47

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表