快捷搜索:

真人注册32.Linux-2440下的DMA驱动(详解)

来源:http://www.shanghai-sourcing.com 作者:真人注册 人气:151 发布时间:2019-10-23
摘要:真人注册32.Linux-2440下的DMA驱动(详解)。指传输进程中,每实行三次,则读4次,然后写4次(如下图所示) 2.1所以,驱动代码如下所示: 2)     1)** DISRC0早先源存放器 ** 先是,DMA的每一种通道只好有

真人注册32.Linux-2440下的DMA驱动(详解)。指传输进程中,每实行三次,则读4次,然后写4次(如下图所示)

2.1 所以,驱动代码如下所示:

2)

 

 

1)**DISRC0早先源存放器 **

先是,DMA的每一种通道只好有三个源- >指标,所以输入指令 cat /proc/interrupts ,找到DMA3搁浅未被选拔

5)DCON0调控贮存器

 

单元传输:

真人注册32.Linux-2440下的DMA驱动(详解)。 

7)DCSRC0当前源寄放器

 真人注册 1

 

手续如下:

[0] : 目标地址选取,         0:传输时指标地址自动扩充,            1:目标地址固定

突发4传输:

真人注册32.Linux-2440下的DMA驱动(详解)。当DMA央浼XnXDREQ为低电平日,则DMA会直接传输数据,直到DMA诉求拉高,才告豆蔻年华段落

[30:0]真人注册32.Linux-2440下的DMA驱动(详解)。  : 寄存DMA当前的目标营地址

 

[1] : 目标地点选取,         0:目标在系统总线上,                         1:指标在外设总线上

 真人注册 2

request_irq(IRQ_DMA3, s3c_dma_irq, NULL, "s3c_dma", 1);// s3c_dma_irq:中断服务函数,这里注册DMA3中断服务函数
//NULL:中断产生类型, 不需要,所以填NULL
//1:表示中断时,传入中断函数的参数,本节不需要所以填1,切记不能填0,否则注册失败

[1]   : DMA通道使能        0:关闭DMA的通道0(禁绝DMA诉求)            1:开启DMA的通道0(开启DMA央浼)

6)DSTAT0状态寄放器

 

  • 1) 注册DMA中断,分配七个DMA缓冲区(源、目标)
  • 2) 注册字符设备,并提供文件操作集结fops
  • 真人注册32.Linux-2440下的DMA驱动(详解)。-> 2.1) 通过ioctl的cmd来决断是运用DMA运营五个地方之间的正片,仍然直接几个地方之间的正片
  • -> 2.2)如若DMA运行,则设置DMA的连锁硬件,并运转DMA传输

3.测量试验运转

即直接存款和储蓄器访问, DMA 传输格局没有必要 CPU 直接调整传输,通过硬件为 RAM 、I/O 设备开垦一条直接传送数据的通路,能使 CPU 的功能极为升高。

 

 

0:单服务格局,举例:有2个DMA乞求,它们会被每个履行贰回(单元传输/突发4传输)后截止,然后直到有下三遍DMA须求,再重复开头另贰次巡回。

 

[22]     : 重新加载按键选项             为0就能够

 4)

1.2 这便是说2440开采板怎么着来运行DMA,先来看2440的DMA寄放器

所以在linux中使用:

9)DMASKT奔驰G级IG0触发屏蔽寄放器

[19:0]   : 设置DMA传输的计数TC               

输入./dma_test DMA & ,使用DMA拷贝,输入 ls 立马有反馈,进而释放了CPU的压力:

dma_free_writecombine(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle);   //释放DMA缓存,与dma_alloc_writecombine()对应
//size:释放长度
//cpu_addr:虚拟地址,
//handle:物理地址
/*该函数只禁止cache缓冲,保持写缓冲区,也就是对注册的物理区写入数据,也会更新到对应的虚拟缓存区上*/
void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp); 
//分配DMA缓存区
//返回值为:申请到的DMA缓冲区的虚拟地址,若为NULL,表示分配失败,需要释放,避免内存泄漏
//参数如下:
  //*dev:指针,这里填0,表示这个申请的缓冲区里没有内容
  //size:分配的地址大小(字节单位)
  //*handle:申请到的物理起始地址
  //gfp:分配出来的内存参数,标志定义在<linux/gfp.h>,常用标志如下:
        //GFP_ATOMIC    用来从中断处理和进程上下文之外的其他代码中分配内存. 从不睡眠.
        //GFP_KERNEL    内核内存的正常分配. 可能睡眠.
      //GFP_USER      用来为用户空间页来分配内存; 它可能睡眠. 

 

[1] : 源地方选取,0:源在系统总线上,                       1:源在外设总线上

[2]   : 截止STOP            该位写1,立即终止DMA当前的传输

1.1在linux中,分配释放DMA缓冲区,只好接纳以下多少个函数

3)DIDST0开头目的存放器

8)DCDST0当前指标存放器

 真人注册 3

[29] : DMA传输计数中断使能/禁绝      0爬山涉水禁绝中断                                1:当传输达成后,发生中断

[30:0] : 贮存DMA源的集散地址

学了那样多驱动,轻易推出DMA的编纂套路:

2)**DISRCC0开头源调节存放器**

 

共有4个通道的寄存器,且每一种通道的贮存器内容都方兴未艾致,所以大家以DMA通道0为例:

[23]     : 软件/硬件供给源选拔      0:软件央浼            1:硬件须求(还索要设置[26:24]来采撷外设源)

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>

/* ./dma_test NORMAL
 * ./dma_test DMA
 */
#define NORMAL_COPY     0               //两个地址之间的正常拷贝
#define DMA_COPY        1              //两个地址之间的DMA拷贝

void print_usage(char *name)
{
    printf("Usage:n");
    printf("%s <NORMAL | DMA>n", name);
}

int main(int argc, char **argv)
{
    int fd,i=30;

     if (argc != 2)
    {
        print_usage(argv[0]);
        return -1;
    }

    fd = open("/dev/s3c_dma", O_RDWR);
    if (fd < 0)
    {
        printf("can't open /dev/s3c_dman");
        return -1;
    }

    if (strcmp(argv[1], "NORMAL") == 0)
    {
        while (i--)                //调用驱动的ioctl(),30次
        {
            ioctl(fd, NORMAL_COPY);
        }
    }
    else if (strcmp(argv[1], "DMA") == 0)
    {
        while (i--)                //调用驱动的ioctl(),30次        
        {
            ioctl(fd, DMA_COPY);
        }
    }
    else
    {
        print_usage(argv[0]);
        return -1;
    }
    return 0;     
}

2.2 应用测量试验程序如下所示:

握脚方式:

指传输进度中,每施行三回,则读1次,写1次.(如上图所示)

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>   
#include <asm/irq.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/dma-mapping.h>

#define  S3C_DMA_SIZE   512*1024          //DMA传输长度   512KB

#define NORMAL_COPY     0                 //两个地址之间的正常拷贝
#define DMA_COPY        1                 //两个地址之间的DMA拷贝

/*函数声明*/
static DECLARE_WAIT_QUEUE_HEAD(s3c_dma_queue);          //声明等待队列
static int s3c_dma_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long flags);

  /*
   * 定义中断事件标志
   * 0:进入等待队列        1:退出等待队列
   */
     static int s3c_dma_even=0;


static unsigned char   *source_virt;            //源虚拟地址
static unsigned int     source_phys;            //源物理地址

static unsigned char *dest_virt;              //目的虚拟地址
static unsigned int   dest_phys;              //目的虚拟地址


/*DMA3寄存器*/
struct  S3c_dma3_regs{
    unsigned int disrc3    ;          //0x4b0000c0
    unsigned int disrcc3   ;                    
    unsigned int didst3    ;                    
    unsigned int didstc3   ;               
    unsigned int dcon3     ;                
    unsigned int dstat3    ; 
    unsigned int dcsrc3    ; 
    unsigned int dcdst3    ;        
    unsigned int dmasktrig3;        //0x4b0000e0
};


 static volatile struct S3c_dma3_regs   *s3c_dma3_regs;

/*字符设备操作*/
static struct file_operations  s3c_dma_fops={
        .owner  = THIS_MODULE,
        .ioctl     = s3c_dma_ioctl,
};

/*中断服务函数*/
static irqreturn_t  s3c_dma_irq (int irq, void *dev_id)   
{
    s3c_dma_even=1;                             //退出等待队列
    wake_up_interruptible(&s3c_dma_queue);      //唤醒 中断
    return IRQ_HANDLED;
}

/*ioctl函数*/
static int s3c_dma_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long flags)
{
    int i;
    memset(source_virt, 0xAA, S3C_DMA_SIZE);          
    memset(dest_virt, 0x55, S3C_DMA_SIZE);   

    switch(cmd)
    {
    case NORMAL_COPY:                           //正常拷贝

             for(i=0;i<S3C_DMA_SIZE;i++)
                 dest_virt[i] =  source_virt[i];

             if(memcmp(dest_virt, source_virt, S3C_DMA_SIZE)==0)
           {
         printk("NORMAL_COPY OKn");
                return 0;
         }
         else
        {
         printk("NORMAL_COPY ERRORn");
               return -EAGAIN;
        }             

    case DMA_COPY:                               //DMA拷贝

        s3c_dma_even=0;     //进入等待队列

        /*设置DMA寄存器,启动一次DMA传输 */
        /* 源的物理地址 */
        s3c_dma3_regs->disrc3      = source_phys;      
        /* 源位于AHB总线, 源地址递增 */  
        s3c_dma3_regs->disrcc3     = (0<<1) | (0<<0);
        /* 目的的物理地址 */
        s3c_dma3_regs->didst3      = dest_phys;      
        /* 目的位于AHB总线, 目的地址递增 */
        s3c_dma3_regs->didstc3     = (0<<2) | (0<<1) | (0<<0);     
        /* 使能中断,单个传输,软件触发, */
        s3c_dma3_regs->dcon3=(1<<30)|(1<<29)|(0<<28)|(1<<27)|(0<<23)|(0<<20)|(S3C_DMA_SIZE<<0);  
        //启动一次DMA传输
        s3c_dma3_regs->dmasktrig3  = (1<<1) | (1<<0);     

        wait_event_interruptible(s3c_dma_queue, s3c_dma_even);    //进入睡眠,等待DMA传输中断到来才退出

        if(memcmp(dest_virt, source_virt, S3C_DMA_SIZE)==0)
        {
         printk("DMA_COPY OKn");
                return 0;
         }
        else
        {
       printk("DMA_COPY ERRORn");
             return -EAGAIN;
           }  

            break;
    }
    return 0;
}


static unsigned int major;
static struct class *cls;
static int s3c_dma_init(void)
{
    /*1.1 注册DMA3 中断  */
    if(request_irq(IRQ_DMA3, s3c_dma_irq,NULL, "s3c_dma",1)) 
    {
        printk("Can't    request_irq   "IRQ_DMA3"!!!n ");
        return -EBUSY;
    }

    /*1.2 分配两个DMA缓冲区(源、目的)*/
    source_virt=dma_alloc_writecombine(NULL,S3C_DMA_SIZE, &source_phys, GFP_KERNEL);
    if(source_virt==NULL)       
   {
        printk("Can't  dma_alloc   n ");
        return -ENOMEM;
   }

    dest_virt=dma_alloc_writecombine(NULL,S3C_DMA_SIZE, &dest_phys, GFP_KERNEL);
    if(dest_virt==NULL)       
   {
        printk("Can't  dma_alloc   n ");
        return -ENOMEM;
   }


    /*2.注册字符设备,并提供文件操作集合fops*/
    major=register_chrdev(0, "s3c_dma",&s3c_dma_fops);
    cls= class_create(THIS_MODULE, "s3c_dma");
    class_device_create(cls, NULL,MKDEV(major,0), NULL, "s3c_dma");

    s3c_dma3_regs=ioremap(0x4b0000c0, sizeof(struct S3c_dma3_regs));

    return 0;  
}

static void s3c_dma_exit(void)
{
    iounmap(s3c_dma3_regs);

    class_device_destroy(cls, MKDEV(major,0));
    class_destroy(cls);

    dma_free_writecombine(NULL, S3C_DMA_SIZE, dest_virt, dest_phys);
    dma_free_writecombine(NULL, S3C_DMA_SIZE, source_virt, source_phys);   

    free_irq(IRQ_DMA3, 1);

}
module_init(s3c_dma_init);
module_exit(s3c_dma_exit);
MODULE_LICENSE("GPL");

1) 源和指标都在系统总线上(比方:八个大要内部存款和储蓄器地址)
2) 当目的在外设总线上时,源在系统总线上(外设指:串口,放大计时器,I2C,I2S等)
3) 当指标在系统总线上时,源在外设总线上
4) 源和对象都在外设总线上

真人注册 4

 

[27] : 传输服务格局  

查询方式:

[30] : 中断哀告(DREQ)/中断回应(DACK)的共同一时间钟选择,        0:PCLK同步     1:HCLK同步

 

 

[21:20] : 传输数据大小    为00(8位)就能够

1.2.1 2440支撑4个通道的DMA调控器

[19:0]   : 传输计数当前值CUPRADOENCORE_TC            为0表示传输停止

DMA(Direct Memory Access)

其间4个通道的DMA外设哀告源,如下图所示(通过DCONn存放器的[26:24]来设置)

dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle)    //释放DMA缓存,与dma_alloc_coherent ()对应
//size:释放长度
//cpu_addr:虚拟地址,
//handle:物理地址

 

1.2.5 2440中的DMA寄放器如下图所示:

[0] : 源地址接纳,0:传输时源地址自动扩张,            1:源地址固定

  • 1)注册DMA中断,分配缓冲区
  • 2)注册字符设备,并提供文件操作集结fops
  •   -> 2.1)file_operations里安装DMA硬件相关操作,来运转DMA

 

 真人注册 5


/*该函数禁止cache缓存以及禁止写入缓冲区*/
void * dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp);         
//分配DMA缓存区,返回值和参数和上面的函数一直

输入 ./dma_test NORMAL & ,使用CPU正常拷贝,能够发掘占用了好些个财富,输入 ls 无反应:

1.2.2 且每一个通道都能够管理以下4种状态:

4)DIDSTC0初阶指标调整贮存器

1) 

而作者辈前边用的内存分配kmalloc()函数,是不可能用在DMA上,因为分红出来的内存大概在情理地址上是不总是的.

鉴于大家是用字符设备的测量检验方法测验的,而本例子只是用四个地方之间的正片来演示DMA的成效,所以接收字符设备格局编写

[31] : 专门的学业形式选用,   0:查询方式                  1:握网络麻豆式      (当源处于外设时,尽量筛选握腿形式)

[0]   : 软件央求触发      1:表示运营三回软件央求DMA,独有DCONn[23]=0和DMASKTRIGn[1]=1才有效,DMA传输时,该位自动清0

[21:20] :      DMA状态             00:空闲           01:忙

 

2.接下来,大家便来写二个DMA的字符设备驱动

[26:24] : DMA外设须求源选择

1.2.4 DMA有三种传输格局(通过DCONn贮存器的[31]来设置)

[2]  : 中断时直接纳,       0:当DMA传输计数=0,立刻产生搁浅       1:推行完自动加载后再发送中断(也便是计数为0,然后重新加载计数值)

 (PS: dma_free_writecombine()其实正是dma_free_conherent(),只但是是用了#define重命名而已。)

 3)

1.2.3 DMA有二种职业格局(通过DCONn贮存器的[28]来设置)

[30:0]  : 寄存DMA当前的源集散地址

(PS:实际这一个DMA相关的存放器,在linux内核中三星(Samsung)已打包好了,能够直接调用,可是那多个麻烦,还不比直接设置贮存器,能够参见: http://blog.csdn.net/mirkerson/article/details/6632273)

 

[30:0] : 设置DMA目标的营地址

(PS:假使有道具在HCLK上,该位应当设为1,比如:(SDRAM)内部存款和储蓄器数组, 反之当这么些设备在PCLK上,应当设为0,譬如:ADC,IIS,I2C,UART)

 

 

 

 真人注册 6

1.3接下去就起来说linux注册DMA中断

当DMA央求XnXDREQ有下降沿触发时,则DMA会传导三遍数据

(PS:假若伏乞源是系统总线上的,就只必要安装DCONn寄放器的[23]=0即可)

 

1:全服务情势,指该DMA若有诉求,则会占用DMA总线,向来传输,时期若有别的DMA诉求,唯有拭目以俟传输计数TC为0,才会实行别的DMA须求

1.驱动编写早先,先来说如何分配释放慢冲区、DMA相关寄放器介绍、使用DMA中断

[28] : 传输情势采取,         0:单元传输                            1:突发4传输

本文由金沙澳门官网55网址发布于真人注册,转载请注明出处:真人注册32.Linux-2440下的DMA驱动(详解)

关键词:

最火资讯