• 《Linux驱动:DMA直接内存访问》


    一、前言

    DMA(Direct Memory Aaccess)直接内存访问,意思是不通过CPU的干涉直接将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。它的作用就是解决大量数据传输过度消耗CPU资源的问题。DMA节省了大量的CPU资源,使CPU专注于更加实用的操作。

    二、DMA传输主体

    DAM的本质就是实现数据的直接传输,即内存的某一地址空间(源)传输到内存的另一地址空间(目的)。存在四种主体之间的数据传输:

    • 目的地址空间和源地址空间都在系统总线(内存到内存)。
    • 源地址空间是系统总线,目的地址空间是外设总线(内存到外设)。
    • 源地址空间是外设总线,目的地址空间是系统总线(外设到内存)。
    • 源地址空间是外设总线,目的地址空间是外设总线(外设到外设)。

    三、S3c2440上的DMA

    3.1 DMA请求源

    s3c2440的DMA源有两种,一是软件触发(S/W - softwore),二是硬件触发(H/W - hardwore)。对于硬件触发,S3c2440的四个DMA通道支持的请求源如下图:
    在这里插入图片描述
    nXDREQ0 and nXDREQ1可以接入外部硬件设备,由外部硬件设备触发。

    3.2 DMA状态机

    在这里插入图片描述
    State-1:DMA等待DMA请求,此时DMA ACK和INT REQ(DMA中断)为0。当DMA收到DMA请求时,转到状态2。
    State-2:DMA将DMA ACK设置为1,同时将从DCON[19:0]寄存器中读取数据设置CURR_TC,完成后跳转到状态3。
    State3:启动子状态机来处理一次原子操作,即完成一次数据从源读出然后写到目的的操作。这时需要分单服务模式和全服务模式来讨论。在单服务模式中,子有限状态机完成一次原子操作后CURR_TC - 1,主状态机便将DMA ACK设为0,然后调回到状态1,然后等待下一次的DMA请求。而在全服务模式时,子状态机将一直运行直到CURR_TC为0,然后再将INT REQ设为1并把DMA ACK设为0,调回到状态1,等待下一次的DMA请求。

    3.3 DMA请求模式

    由DCON寄存器配置。DMD_HS -> DCON[31]。

    • Demand Mode

    当XnXDREQ有效时,即刻不断的进行数据传输,直到XnXDREQ失效时停止传输,等待下一次XnXDREQ有效。

    • Handshake Mode

    当XnXDREQ有效时,进行一次数据传输,这一次传输完成后,需要等待两个周期的XnXDREQ失效,如果XnXDREQ没有两个周期的失效则一直等待,只有当XnXDREQ有两个周期的失效后再次有效时才开始下一次数据传输。
    在这里插入图片描述

    3.4 DMA服务模式

    由DCON寄存器配置。SERVMODE -> DCON[27]。

    • Whole service

    在全服务模式时,子状态机将一直运行直到CURR_TC为0,然后再将INT REQ设为1并把DMA ACK设为0,调回到状态1,等待下一次的DMA请求。

    • Single service

    在单服务模式中,子有限状态机完成一次原子操作后CURR_TC - 1,主状态机便将DMA ACK设为0,然后调回到状态1,然后等待下一次的DMA请求。

    3.5 DMA传输模式

    由DCON寄存器配置。TSZ -> DCON[28]。

    • singel

    一次DMA传输(State-3的子状态机),只进行一次读/写操作。

    • brust

    一次DMA传输(State-3的子状态机),进行四次读/写操作。

    3.6 DMA读写数据大小

    由DCON寄存器配置。DSZ -> DCON[21:20]。
    一次读/写操作中,读/写多少个字节。
    00 = Byte 01 = Half word
    10 = Word 11 = reserved

    3.7 DMA寄存器

    在这里插入图片描述

    3.7.1 DCON寄存器其他几个重要位

    DCONnBitdecsription
    SYNC[30]0: DREQ 和 DACK的时钟同步源为PCLK(APB clock)。1: DREQ 和 DACK的时钟同步源为HCLK(AHB clock)。
    INT[29]使能DMA中断
    HWSRCSEL[26:24]硬件触发DMA源
    SWHW_SEL[23]选择软件触发还是硬件触发
    RELOAD[22]配置当 CURR_TC为0时,是否自动再次重载

    四、使用DMA

    4.1 软件触发DMA

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define MEM_CPY_NO_DMA  0
    #define MEM_CPY_DMA     1
    
    #define BUF_SIZE  (512*1024)
    
    #define DMA0_BASE_ADDR  0x4B000000
    #define DMA1_BASE_ADDR  0x4B000040
    #define DMA2_BASE_ADDR  0x4B000080
    #define DMA3_BASE_ADDR  0x4B0000C0
    
    struct s3c_dma_regs {
    	unsigned long disrc;
    	unsigned long disrcc;
    	unsigned long didst;
    	unsigned long didstc;
    	unsigned long dcon;
    	unsigned long dstat;
    	unsigned long dcsrc;
    	unsigned long dcdst;
    	unsigned long dmasktrig;
    };
    
    
    static int major = 0;
    
    static char *src;
    static u32 src_phys;
    
    static char *dst;
    static u32 dst_phys;
    
    static struct class *cls;
    
    static volatile struct s3c_dma_regs *dma_regs;
    
    static DECLARE_WAIT_QUEUE_HEAD(dma_waitq);
    /* 中断事件标志, 中断服务程序将它置1,ioctl将它清0 */
    static volatile int ev_dma = 0;
    
    static int s3c_dma_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
    {
    	int i;
    
    	memset(src, 0xAA, BUF_SIZE);
    	memset(dst, 0x55, BUF_SIZE);
    	
    	switch (cmd)
    	{
    		case MEM_CPY_NO_DMA :
    		{
    			for (i = 0; i < BUF_SIZE; i++)
    				dst[i] = src[i];
    			if (memcmp(src, dst, BUF_SIZE) == 0)
    			{
    				printk("MEM_CPY_NO_DMA OK\n");
    			}
    			else
    			{
    				printk("MEM_CPY_DMA ERROR\n");
    			}
    			break;
    		}
    
    		case MEM_CPY_DMA :
    		{
    			ev_dma = 0;
    			
    			/* 把源,目的,长度告诉DMA */
    			dma_regs->disrc      = src_phys;        /* 源的物理地址 */
    			dma_regs->disrcc     = (0<<1) | (0<<0); /* 源位于AHB总线, 源地址递增 */
    			dma_regs->didst      = dst_phys;        /* 目的的物理地址 */
    			dma_regs->didstc     = (0<<2) | (0<<1) | (0<<0); /* 目的位于AHB总线, 目的地址递增 */
    			dma_regs->dcon       = (1<<30)|(1<<29)|(0<<28)|(1<<27)|(0<<23)|(0<<20)|(BUF_SIZE<<0);  /* 使能中断,单个传输,软件触发, */
    
    			/* 启动DMA */
    			dma_regs->dmasktrig  = (1<<1) | (1<<0);
    
    			/* 如何知道DMA什么时候完成? */
    			/* 休眠 */
    			wait_event_interruptible(dma_waitq, ev_dma);
    
    			if (memcmp(src, dst, BUF_SIZE) == 0)
    			{
    				printk("MEM_CPY_DMA OK\n");
    			}
    			else
    			{
    				printk("MEM_CPY_DMA ERROR\n");
    			}
    			
    			break;
    		}
    	}
    
    	return 0;
    }
    
    static struct file_operations dma_fops = {
    	.owner  = THIS_MODULE,
    	.ioctl  = s3c_dma_ioctl,
    };
    
    static irqreturn_t s3c_dma_irq(int irq, void *devid)
    {
    	/* 唤醒 */
    	ev_dma = 1;
        wake_up_interruptible(&dma_waitq);   /* 唤醒休眠的进程 */
    	return IRQ_HANDLED;
    }
    
    static int s3c_dma_init(void)
    {
    	if (request_irq(IRQ_DMA3, s3c_dma_irq, 0, "s3c_dma", 1))
    	{
    		printk("can't request_irq for DMA\n");
    		return -EBUSY;
    	}
    	
    	/* 分配SRC, DST对应的缓冲区 */
    	src = dma_alloc_writecombine(NULL, BUF_SIZE, &src_phys, GFP_KERNEL);
    	if (NULL == src)
    	{
    		printk("can't alloc buffer for src\n");
    		free_irq(IRQ_DMA3, 1);
    		return -ENOMEM;
    	}
    	
    	dst = dma_alloc_writecombine(NULL, BUF_SIZE, &dst_phys, GFP_KERNEL);
    	if (NULL == dst)
    	{
    		free_irq(IRQ_DMA3, 1);
    		dma_free_writecombine(NULL, BUF_SIZE, src, src_phys);
    		printk("can't alloc buffer for dst\n");
    		return -ENOMEM;
    	}
    
    	major = register_chrdev(0, "s3c_dma", &dma_fops);
    
    	/* 为了自动创建设备节点 */
    	cls = class_create(THIS_MODULE, "s3c_dma");
    	class_device_create(cls, NULL, MKDEV(major, 0), NULL, "dma"); /* /dev/dma */
    
    	dma_regs = ioremap(DMA3_BASE_ADDR, sizeof(struct s3c_dma_regs));
    		
    	return 0;
    }
    
    static void s3c_dma_exit(void)
    {
    	iounmap(dma_regs);
    	class_device_destroy(cls, MKDEV(major, 0));
    	class_destroy(cls);
    	unregister_chrdev(major, "s3c_dma");
    	dma_free_writecombine(NULL, BUF_SIZE, src, src_phys);
    	dma_free_writecombine(NULL, BUF_SIZE, dst, dst_phys);	
    	free_irq(IRQ_DMA3, 1);
    }
    
    module_init(s3c_dma_init);
    module_exit(s3c_dma_exit);
    
    MODULE_LICENSE("GPL");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176

    4.2 硬件源触发DMA

    参见:《Linux驱动:使用音频设备驱动框架-OSS构建音频设备驱动》

  • 相关阅读:
    数据结构系列学习(九) - 循环队列(Circular_Queue)
    SRC之若依系统弱口令
    【IDEA中集成SpringBoot源码环境详细步骤讲解】
    域名讲解(一)域名基础概念
    web网页设计期末课程大作业 HTML+CSS+JavaScript重庆火锅(代码质量好)
    初中化学骨干教师培训需求调查分析--以贵州师范学院国培计划为例
    运营商“商业成功”更上层楼—5G-A核心基因协同进化
    Docker TimeScaleDB
    SQL Server教程 - SQL Server 代理(Agent)
    京东大数据:2023年Q3美妆行业数据分析报告
  • 原文地址:https://blog.csdn.net/qq_40709487/article/details/127838747