DMA驱动详解

本文介绍了直接存储器访问(DMA)的概念,详细讲解了Linux中分配和释放DMA缓冲区的方法,并深入探讨了2440平台的DMA控制器,包括其4个通道、工作模式、传输模式以及寄存器配置。此外,还提到了如何注册DMA中断,并概述了编写DMA字符设备驱动的步骤。
摘要由CSDN通过智能技术生成

1. DMA介绍

DMA(Direct Memory Access):即直接存储器访问, DMA 传输方式无需 CPU 直接控制传输,通过硬件为 RAM 、I/O 设备开辟一条直接传送数据的通路,能使 CPU 的效率大为提高。

2.驱动编写之前,先来讲如何分配释放缓冲区、DMA相关寄存器介绍、使用DMA中断

2.1在linux中,分配释放DMA缓冲区,常用以下几个函数

/*该函数只禁止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      用来为用户空间页来分配内存; 它可能睡眠.

/*该函数禁止cache缓存以及禁止写入缓冲区,从而使CPU读写的地址和DMA读写的地址内容一致*/
void * dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp);         
//分配DMA缓存区,返回值和参数和上面的函数一直
dma_free_writecombine(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle);   //释放DMA缓存,与dma_alloc_writecombine()对应
//size:释放长度
//cpu_addr:虚拟地址,
//handle:物理地址
dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle)    //释放DMA缓存,与dma_alloc_coherent ()对应
//size:释放长度
//cpu_addr:虚拟地址,
//handle:物理地址

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

而我们之前用的内存分配kmalloc()函数,是不能用在DMA上,因为分配出来的内存可能在物理地址上是不连续的.

2.1如何来启动DMA,先来看2440的DMA寄存器

2.1.1支持4个通道的DMA控制器

其中4个通道的DMA外设请求源,如下图所示(通过DCONn寄存器的[26:24]来设置)

(PS:如果请求源是系统总线上的,就只需要设置DCONn寄存器的[23]=0即可)
在这里插入图片描述

2.1.2且每个通道都可以处理以下4种情况:

  1. 源和目标都在系统总线上(比如:两个物理内存地址)
  2. 当目标在外设总线上时,源在系统总线上(外设指:串口,定时器,I2C,I2S等)
  3. 当目标在系统总线上时,源在外设总线上
  4. 源和目标都在外设总线上

2.1.3DMA有两种工作模式(通过DCONn寄存器的[28]来设置)

查询模式:

当DMA请求XnXDREQ为低电平时,则DMA会一直传输数据,直到DMA请求拉高,才停止

握手模式:

当DMA请求XnXDREQ有下降沿触发时,则DMA会传输一次数据

在这里插入图片描述

2.1.4DMA有两种传输模式(通过DCONn寄存器的[31]来设置)

单元传输:

指传输过程中,每执行一次,则读1次,写1次.(如上图所示)

突发4传输:

指传输过程中,每执行一次,则读4次,然后写4次(如下图所示)
在这里插入图片描述

2.1.52440中的DMA寄存器如下图所示:

在这里插入图片描述
共有4个通道的寄存器,且每个通道的寄存器内容都一致,所以我们以DMA通道0为例:

1)DISRC0初始源寄存器

[30:0] : 存放DMA源的基地址

2)DISRCC0初始源控制寄存器

[1] : 源位置选择,0:源在系统总线上, 1:源在外设总线上

[0] : 源地址选择,0:传输时源地址自动增加, 1:源地址固定

3)DIDST0初始目标寄存器

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

4)DIDSTC0初始目标控制寄存器

[2] : 中断时间选择, 0:当DMA传输计数=0,立即发生中断 1:执行完自动加载后再发送中断(也就是计数为0,然后重新加载计数值)

[1] : 目的位置选择, 0:目的在系统总线上,    1:目的在外设总线上

[0] : 目的地址选择, 0:传输时目的地址自动增加,     1:目的地址固定

5)DCON0控制寄存器

[31] : 工作模式选择,   0:查询模式     1:握手模式 (当源处于外设时,尽量选择握手模式)

[30] : 中断请求(DREQ)/中断回应(DACK)的同步时钟选择, 0:PCLK同步 1:HCLK同步

(PS:如果有设备在HCLK上,该位应当设为1,比如:(SDRAM)内存数组, 反之当这些设备在PCLK上,应当设为0,比如:ADC,IIS,I2C,UART)

[29] : DMA传输计数中断使能/禁止 0:禁止中断 1:当传输完成后,产生中断

[28] : 传输模式选择,         0:单元传输 1:突发4传输

[27] : 传输服务模式

0:单服务模式,比如:有2个DMA请求,它们会被顺序执行一次(单元传输/突发4传输)后停止,然后直到有下一次DMA请求,再重新开始另一次循环。

1:全服务模式,指该DMA若有请求,则会占用DMA总线,一直传输,期间若有其它DMA请求,只有等待传输计数TC为0,才会执行其它DMA请求

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

[23] : 软件/硬件请求源选择   0:软件请求       1:硬件请求(还需要设置[26:24]来选择外设源)

[22] : 重新加载开关选项 为0即可

[21:20] : 传输数据大小 为00(8位)即可

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

6)DSTAT0状态寄存器

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

[19:0] : 传输计数当前值CURR_TC 为0表示传输结束

7)DCSRC0当前源寄存器

[30:0] : 存放DMA当前的源基地址

8)DCDST0当前目标寄存器

[30:0] : 存放DMA当前的目的基地址

9)DMASKTRIG0触发屏蔽寄存器

[2] : 停止STOP 该位写1,立刻停止DMA当前的传输

[1] : DMA通道使能 0:关闭DMA的通道0(禁止DMA请求) 1:开启DMA的通道0(开启DMA请求)

[0] : 软件请求触发   1:表示启动一次软件请求DMA,只有DCONn[23]=0和DMASKTRIGn[1]=1才有效,DMA传输时,该位自动清0

2.2接下来就开始讲linux注册DMA中断

首先,DMA的每个通道只能有一个源- >目的,所以输入命令 cat /proc/interrupts ,找到DMA3中断未被使用

所以在linux中使用:

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

3.接下来,我们便来写一个DMA的字符设备驱动

步骤如下:

  1. 注册DMA中断,分配两个DMA缓冲区(源、目的)
  2. 注册字符设备,并提供文件操作集合fops
    -> 2.1) 通过ioctl的cmd来判断是使用DMA启动两个地址之间的拷贝,还是直接两个地址之间的拷贝
    -> 2.2)若是DMA启动,则设置DMA的相关硬件,并启动DMA传输
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值