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

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
");
            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
");
                            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版本放弃了原来的工作队列方式而使用内核线程的方式显然可能是原来工作队列的方式满足不了传输效率吧
//由于使用了内核线程的实现方式,所以我们可以很容易的改变调度的策略赋予不同的优先级。在某些对传输效率及时间有较高要求的场景4.1.15版本的实现
//方式明显是要优于3.0.35版本的。
原文地址:https://www.cnblogs.com/timemachine213/p/13254350.html