ZYNQ petalinux 设备树驱动双DMA循环切换传输数据

    技术2022-07-10  124

    1,单个DMA每次只能发送一定量的数据,但对于数据源来说数据时源源不断产生的,所以在单个DMA单次发送完成至下一次传输期间,这段时间的数据流失了,所以采用两个DMA实现循环发送数据,防止数据丢失。自定义一个IP核用于产生源源不断的测试数据模拟数据源,再自定义一个IP用于切换DMA发送数据。通过axi-gpio启动数据源产生数据,数据流再通过DMA切换模块分批次将数据送往DMA。每个DMA发送20万个数据便进行切换,所以数据源采用从1开始累加产生数据,每次在DMA中断中验证最后一个数据是否正确。

    2,添加驱动

    在Kconfig中,发现 XILINX_DMA

       config ALINX_ADC_DMA     tristate "ALINX ADC DMA Test client for AXI DMA"     depends on XILINX_DMA     help           ALINX ADC DMA  client.

     

    在Makefile中发现

    obj-$(CONFIG_XILINX_DMA) += xilinx_dma.o

     

    对应于/home/alinx/linux-xlnx-xilinx-v2017.4/drivers/dma/xilinx/xilinx_dma.c中

    设备树代码 pl.dtsi

    / {     amba_pl: amba_pl {         #address-cells = <1>;         #size-cells = <1>;         compatible = "simple-bus";         ranges ;         axi_dma_0: dma@40400000 {             #dma-cells = <1>;             clock-names = "s_axi_lite_aclk", "m_axi_s2mm_aclk";             clocks = <&clkc 15>, <&clkc 15>;             compatible = "xlnx,axi-dma0";             interrupt-names = "s2mm_introut";             interrupt-parent = <&intc>;             interrupts = <0 29 4>;             reg = <0x40400000 0x10000>;             xlnx,addrwidth = <0x20>;             xlnx,sg-length-width = <0x14>;             dma-channel@40400030 {                 compatible = "xlnx,axi-dma-s2mm-channel";                 dma-channels = <0x1>;                 interrupts = <0 29 4>;                 xlnx,datawidth = <0x20>;                 xlnx,device-id = <0x0>;             };         };         axi_dma_1: dma@40410000 {             #dma-cells = <1>;             clock-names = "s_axi_lite_aclk", "m_axi_s2mm_aclk";             clocks = <&clkc 15>, <&clkc 15>;             compatible = "xlnx,axi-dma1";             interrupt-names = "s2mm_introut";             interrupt-parent = <&intc>;             interrupts = <0 30 4>;             reg = <0x40410000 0x10000>;             xlnx,addrwidth = <0x20>;             xlnx,sg-length-width = <0x14>;             dma-channel@40410030 {                 compatible = "xlnx,axi-dma-s2mm-channel";                 dma-channels = <0x1>;                 interrupts = <0 30 4>;                 xlnx,datawidth = <0x20>;                 xlnx,device-id = <0x1>;             };         };         axi_gpio_0: gpio@41200000 {             #gpio-cells = <3>;             clock-names = "s_axi_aclk";             clocks = <&clkc 15>;             compatible = "xlnx,axi-gpio-test";             gpio-controller ;             reg = <0x41200000 0x10000>;             xlnx,all-inputs = <0x0>;             xlnx,all-inputs-2 = <0x0>;             xlnx,all-outputs = <0x1>;             xlnx,all-outputs-2 = <0x0>;             xlnx,dout-default = <0x00000000>;             xlnx,dout-default-2 = <0x00000000>;             xlnx,gpio-width = <0x1>;             xlnx,gpio2-width = <0x20>;             xlnx,interrupt-present = <0x0>;             xlnx,is-dual = <0x0>;             xlnx,tri-default = <0xFFFFFFFF>;             xlnx,tri-default-2 = <0xFFFFFFFF>;         };     }; };

    3,gpio驱动代码gpio_driver.c

    #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/fcntl.h> #include <linux/init.h> #include <linux/poll.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> /* Needed for Sequence File Operations */ #include <linux/mutex.h> #include <linux/sysctl.h> #include <linux/fs.h> #include <linux/cdev.h> #include "linux/device.h" #include "linux/miscdevice.h" #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/of_irq.h> #include <linux/slab.h> #include <linux/bitops.h> #include <linux/dmapool.h> #include <linux/of_dma.h> #include <linux/dma-mapping.h> #include <linux/kfifo.h>

    #include "linux/kthread.h" #include "linux/wait.h" #include "linux/completion.h" #include "linux/workqueue.h" #include <linux/io.h> #include <linux/uaccess.h>

    int major; static struct class *gpio_class   = NULL; void __iomem *base_regs;

    /*  *open 接口函数  *  * */ static int axi_gpio_open(struct inode *inode,struct file *file) {     printk("enter axi_gpio_open \r\n");     return 0; } /*  * write 接口函数  *  * */ static int axi_gpio_write(struct file *file,const char __user *buf, size_t count,loff_t *ppos) {     unsigned int ret=0;     int data;     printk("gpio write start !\n");     ret=copy_from_user(&data,buf,count);     if(ret!=0)     {         printk("data read fail\n");         return -1;     }     iowrite32(data,base_regs);

        return 0; } /*  * read 接口函数  *  * */ static int axi_gpio_read(struct file *file,char __user *buf,size_t size,loff_t *ppos) {

        printk("gpio read start!\n");     return 0; }

    static struct file_operations axi_gpio_fops={     .owner = THIS_MODULE,     .read  = axi_gpio_read,     .write = axi_gpio_write,     .open  = axi_gpio_open, }; unsigned int irq_num; static int gpio_probe(struct platform_device *pdev) {     struct resource *io;     printk("enter gpio probe\r\n");     //从设备树中获取gpio基地址,同时进行地址映射,虚拟地址保存在 base_regs 中     io = platform_get_resource(pdev, IORESOURCE_MEM, 0);     base_regs = devm_ioremap_resource(&pdev->dev, io);     printk("gpio base address is: 0X:%x\r\n",base_regs);     iowrite32(0x0,base_regs);     major         = register_chrdev( 0 , "axi_gpio_dev" , &axi_gpio_fops);     gpio_class     = class_create (THIS_MODULE , "axi_gpio_dev");     device_create(gpio_class, NULL, MKDEV(major,0), NULL, "axi_gpio_dev");     printk("major dev number= %d",major);     return 0; } static int gpio_remove(struct platform_device *pdev) {     unregister_chrdev(major,"axi_gpio_dev");     device_destroy(gpio_class,MKDEV(major,0));     class_destroy(gpio_class);

        iounmap(base_regs);     return 0;      } static const struct of_device_id my_axigpio_of_ids[] = {     { .compatible = "xlnx,axi-gpio-test",},     {} };

    static struct platform_driver my_axigpio_driver = {     .driver = {         .name = "my_axi_gpio",         .of_match_table = my_axigpio_of_ids,     },     .probe  = gpio_probe,     .remove = gpio_remove, };

    module_platform_driver(my_axigpio_driver);

    MODULE_AUTHOR("TES@gpio"); MODULE_DESCRIPTION("my gpio driver"); MODULE_ALIAS("my gpio linux driver"); MODULE_LICENSE("GPL"); 4,dma驱动代码dma_driver.c

    #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/fcntl.h> #include <linux/init.h> #include <linux/poll.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> /* Needed for Sequence File Operations */ #include <linux/mutex.h> #include <linux/sysctl.h> #include <linux/fs.h> #include <linux/cdev.h> #include "linux/device.h" #include "linux/miscdevice.h" #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/of_irq.h> #include <linux/slab.h> #include <linux/bitops.h> #include <linux/dmapool.h> #include <linux/of_dma.h> #include <linux/dma-mapping.h> #include <linux/kfifo.h>

    #include "linux/kthread.h" #include "linux/wait.h" #include "linux/completion.h" #include "linux/workqueue.h" #include <linux/io.h> #include <linux/uaccess.h> /**  *DMA驱动程序  *  * **/ //DMA MM2S控制寄存器 volatile unsigned int  * mm2s_cr; #define MM2S_DMACR        0X00000000

    //DMA MM2S状态控制寄存器 volatile unsigned int * mm2s_sr; #define MM2S_DMASR        0X00000004

    //DMA MM2S源地址低32位 volatile unsigned int * mm2s_sa; #define MM2S_SA            0X00000018

    //DMA MM2S传输长度(字节) volatile unsigned int * mm2s_len; #define MM2S_LENGTH        0X00000028

    //DMA S2MM控制寄存器 volatile unsigned int  * s2mm_cr; #define S2MM_DMACR        0X00000030

    //DMA S2MM状态控制寄存器 volatile unsigned int  * s2mm_sr; #define S2MM_DMASR        0X00000034

    //DMA S2MM目标地址低32位 volatile unsigned int  * s2mm_da; #define S2MM_DA            0X00000048

    //DMA S2MM传输长度(字节) volatile unsigned int  * s2mm_len; #define S2MM_LENGTH        0X00000058

    #define DMA_BUF_SIZE  1024*1024 void __iomem *base_regs; int major; dma_addr_t phyaddr;//物理地址 char *buf;//虚拟地址 static struct class *dma_class   = NULL; //DMA interrupt functions int dma_flag=0; unsigned int status=0; static int irq_cnt=0; static irqreturn_t dma0_s2mm_irq(int irq,void *dev_id) {     unsigned int data;     irq_cnt++;     if(irq_cnt%5==0)     {     data = (buf[399999]<<24)|(buf[399998]<<16)|(buf[399997]<<8)|(buf[399996]);     printk(" %d",data);     }     iowrite32(0x0000f000,base_regs  +  S2MM_DMASR);     iowrite32(0x00001001,base_regs  +  S2MM_DMACR);//open int & enable DMA     iowrite32(phyaddr   ,base_regs  +  S2MM_DA);     iowrite32(4000000   ,base_regs  +  S2MM_LENGTH);//write transmission length and DMA start transmission     return IRQ_HANDLED; }

    /*  *open 接口函数  *  * */ static int axi_dma_open(struct inode *inode,struct file *file) {     printk("enter axi_dma_open \r\n");     iowrite32(0x00001001,base_regs  +  S2MM_DMACR);//open int & enable DMA     iowrite32(phyaddr   ,base_regs  +  S2MM_DA);     iowrite32(400000   ,base_regs  +  S2MM_LENGTH);//write transmission length and DMA start transmission     return 0; } /*  * write 接口函数  *  * */ static int axi_dma_write(struct file *file,const char __user *buff, size_t count,loff_t *ppos) {

        printk("dma write start !\n");     return 0; } /*  * read 接口函数  *  * */ static int axi_dma_read(struct file *file,char __user *buff,size_t size,loff_t *ppos) {

        printk("dma read start !\n");     return 0; } static unsigned int axi_dma_poll(struct file *file, poll_table *wait) {     printk("enter axi_dma_poll \r\n");     return 0;         } static int axi_dma_mmap (struct file *file, struct vm_area_struct *vma) {     printk("enter axi_dma_mmap \r\n");     vma->vm_flags |= VM_IO;     vma->vm_flags |= VM_LOCKED;     vma->vm_pgoff = phyaddr >> PAGE_SHIFT;     vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);

        if(remap_pfn_range(vma, vma->vm_start,vma->vm_pgoff, vma->vm_end-vma->vm_start, vma->vm_page_prot)){         printk("axi_dma_mmap failed.\n");         return -EAGAIN;     }     printk("base start=0xlx, \n",(unsigned long)phyaddr);     printk("vma start=0xlx, size=%ld.\n",         (unsigned long)vma->vm_start,         (unsigned long)vma->vm_end - (unsigned long)vma->vm_start);        return 0; } static struct file_operations axi_dma_fops={     .owner = THIS_MODULE,     .read  = axi_dma_read,     .write = axi_dma_write,     .open  = axi_dma_open,     .mmap = axi_dma_mmap,               /* mmap method */ }; unsigned int irq_num; static int my_dma_probe(struct platform_device *pdev) {     struct resource *io;     struct device_node *node = pdev->dev.of_node;     struct device_node *child;     int err;     struct device *dev;     //从设备树中获取dma基地址,同时进行地址映射,虚拟地址保存在 base_regs 中     io = platform_get_resource(pdev, IORESOURCE_MEM, 0);     base_regs = devm_ioremap_resource(&pdev->dev, io);     printk("dma base address is: 0X:%x\r\n",base_regs);     //申请中断     for_each_child_of_node(node, child){         if(of_device_is_compatible(child, "xlnx,axi-dma-s2mm-channel"))         {             printk("axi dma s2mm valid\r\n");             irq_num = irq_of_parse_and_map(child, 0);             err = request_irq(irq_num, dma0_s2mm_irq, IRQF_TRIGGER_RISING, "dma0_s2mm_irq", NULL);             if (err)             {                 printk( "dma0 unable to request IRQ %d\n", irq_num);                 return err;             }         }         else         {             printk("dma0 Invalid channel compatible node\n");             return -EINVAL;         }     }     ///申请dma缓冲区--起始     dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);     if (!dev)         return -ENOMEM;     dev = &pdev->dev;     dev->coherent_dma_mask = 0xffffffff;     buf = dma_alloc_coherent(dev, PAGE_ALIGN(DMA_BUF_SIZE), &phyaddr, GFP_KERNEL);     if(!buf)     {         printk("axi vdma  buffer memory allocation failed\r\n");             return -ENOMEM;     }     else     {         printk("buf=0x%x\n",buf);         printk(" phyaddr=0x%x\r\n",phyaddr);     }     ///申请dma缓冲区--结束     major         = register_chrdev( 0 , "axi_dma0_dev" , &axi_dma_fops);     dma_class     = class_create (THIS_MODULE , "axi_dma0_dev");     device_create(dma_class, NULL, MKDEV(major,0), NULL, "axi_dma0_dev");     printk("major dev number= %d",major);     return 0; } static int my_dma_remove(struct platform_device *pdev) {     unregister_chrdev(major,"axi_dma0_dev");     device_destroy(dma_class,MKDEV(major,0));     class_destroy(dma_class);     free_irq(irq_num,NULL);     dma_free_coherent(NULL, PAGE_ALIGN(DMA_BUF_SIZE), buf, phyaddr);     iounmap(base_regs);     return 0;      } static const struct of_device_id my_dma_of_ids[] = {     { .compatible = "xlnx,axi-dma0",},     {} };

    static struct platform_driver my_dma_driver = {     .driver = {         .name = "my_axi_dma0",         .of_match_table = my_dma_of_ids,     },     .probe  = my_dma_probe,     .remove = my_dma_remove, };

    module_platform_driver(my_dma_driver);

    MODULE_AUTHOR("TEST@dma0"); MODULE_DESCRIPTION("dma0 driver"); MODULE_ALIAS("dma0 linux driver"); MODULE_LICENSE("GPL");

    5,测试文件 test_my_dma.c

    #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <getopt.h>            #include <fcntl.h>             #include <unistd.h> #include <errno.h> #include <malloc.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/time.h> #include <sys/mman.h> #include <sys/ioctl.h>

    #define DMA_BUF_SIZE  1024*1024

    void delay() {     int i=0,j=0;     for(i=0;i<10000;i++)         for(j=0;j<1000;j++)j++; } int main() {     int dma0_fd,dma1_fd,gpio_fd;     int i=0,j=0;     unsigned int * dma0_mmapBuf;     unsigned int * dma1_mmapBuf;     int dma0_flag,dma1_flag,gpio_flag;     int *dest;     dma0_fd = open("/dev/axi_dma0_dev", O_RDWR, 0);     if (dma0_fd < 0) {             printf("Open %s failed\n", "/dev/axivdma_dev");             return -1;     }     dma0_mmapBuf = mmap(NULL, DMA_BUF_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, dma0_fd, 0);          dma1_fd = open("/dev/axi_dma1_dev", O_RDWR, 0);     if (dma1_fd < 0) {

                printf("Open %s failed\n", "/dev/axivdma_dev");

                return -1;

        }     dma1_mmapBuf = mmap(NULL, DMA_BUF_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, dma1_fd, 0);     

        gpio_fd = open("/dev/axi_gpio_dev", O_RDWR, 0);     if (gpio_fd < 0) {             printf("Open %s failed\n", "/dev/axi_gpio_dev");             return -1;     }     gpio_flag=1;     printf("======start====\r\n");

        delay();

        write(gpio_fd,&gpio_flag,4);

        delay();

        while(1);

        printf("dma test Done.\n");

        return 0;

    }

     

     

    Processed: 0.011, SQL: 9