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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "linux/device.h"
#include "linux/miscdevice.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "linux/kthread.h"
#include "linux/wait.h"
#include "linux/completion.h"
#include "linux/workqueue.h"
#include
#include
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "linux/device.h"
#include "linux/miscdevice.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "linux/kthread.h"
#include "linux/wait.h"
#include "linux/completion.h"
#include "linux/workqueue.h"
#include
#include
/**
*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=0x%08lx, \n",(unsigned long)phyaddr);
printk("vma start=0x%08lx, 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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;
}