Marvell-linux研究—dma.c源代码分析

Marvell-linux研究—dma.c源代码分析

转载时请注明出处和作者联系方式:http://blog.csdn.net/absurd

作者联系方式:李先静 <xianjimli at hotmail dot com>

更新时间:<chsdate w:st="on" isrocdate="False" islunardate="False" day="16" month="7" year="2007"><span lang="EN-US"><font face="Times New Roman">2007-7-16</font></span></chsdate>

由于在传输大块数据的过程中无须CPU干预(当然在开始、出错和结束时仍然需要),所以与轮询和中断相比,DMA传输效率要高得多。另外,Marvell平台上提供了所谓的memory switch,总线有更高的利用率,DMA就更能显出它的优势了。

下面我们看看mach-pxa/dma.c中的代码:

31 staticstructdma_channel {
32 char*name;
33 void(*irq_handler)(int, void*, structpt_regs *);
34 void*data;
35 } dma_channels[PXA_DMA_CHANNELS];

该结构用于保存已注册的DMA中断处理函数,成员name表示该通道的名称,它只是起说明的作用,没有什么实际用途。成员irq_handler是所注册的中断处理函数,当该通道发生中断时,该函数被调用。成员data是中断处理函数irq_handler的调用上下文,当中断处理函数被调用时,其作为第二个参数传入。

38 intpxa_request_dma (char*name, pxa_dma_prio prio,
39 void(*irq_handler)(int, void*, structpt_regs *),
40 void*data)
41 {
42 unsignedlongflags;
43 inti, found = 0;
44
45 /*basic sanity checks */
46 if(!name || !irq_handler)
47 return-EINVAL;
48
49 local_irq_save(flags);
50
51 /*try grabbing a DMA channel with the requested priority */
52 for(i = prio; i < prio + PXA_DMA_NBCH(prio); i++) {
53 if(!dma_channels[i].name) {
54 found = 1;
55 break;
56 }
57 }
58
59 if(!found) {
60 /*requested prio group is full, try hier priorities */
61 for(i = prio-1; i >= 0; i--) {
62 if(!dma_channels[i].name) {
63 found = 1;
64 break;
65 }
66 }
67 }
68
69 if(found) {
70 DCSR(i) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
71 dma_channels[i].name = name;
72 dma_channels[i].irq_handler = irq_handler;
73 dma_channels[i].data = data;
74 } else{
75 printk (KERN_WARNING "No more available DMA channels for %s/n", name);
76 i = -ENODEV;
77 }
78
79 local_irq_restore(flags);
80 returni;
81 }

注册一个DMA通道,其参数有名称、优先级、中断处理函数和中断处理函数的调用上下文。

这里的优先级和开发手册中所说的略有差别,在开发手册中(<chsdate w:st="on" isrocdate="False" islunardate="False" day="30" month="12" year="1899">11.3.1</chsdate>.1)里说,从硬件的角度,优先级分为四等,0等优先级最高,3等优先级最低。在代码中,优先级只分为高中低三等,高优先级和中优先级的通道数为8个,低优先级的通道数为16个。

dma_channels数组中,按优先级从高到低排列,在注册时,先看在所请求的优先级中是否有空位,如果有,就使用该空位,如果没有,就从更高优先级中去找,直到找一个空位,或者发现没有空位可用,则中断循环。

如果找到合适的空位,则重置该通道的状态。和我们前面几次分析中所提到的一样,70行中的代码并非是要设置DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR几个位域,而是在对应的位域上写1去清除它。

83 voidpxa_free_dma (intdma_ch)
84 {
85 unsignedlongflags;
86
87 if(!dma_channels[dma_ch].name) {
88 printk (KERN_CRIT
89 "%s: trying to free channel %dwhich is already freed/n",
90 __FUNCTION__, dma_ch);
91 return;
92 }
93
94 local_irq_save(flags);
95 DCSR(dma_ch) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
96 dma_channels[dma_ch].name = NULL;
97 local_irq_restore(flags);
98 }

该函数用于注销DMA通道,它重置对应的DCSR寄存器,并把name置空。

100 staticirqreturn_t dma_irq_handler(intirq, void*dev_id, structpt_regs *regs)
101 {
102 inti, dint = DINT;
103
104 for(i = 0; i < PXA_DMA_CHANNELS; i++) {
105 if(dint & (1<< i)) {
106 structdma_channel *channel = &dma_channels[i];
107 if(channel->name && channel->irq_handler) {
108 channel->irq_handler(i, channel->data, regs);
109 } else{
110 /*
111 * IRQ for an unregistered DMA channel:
112 * let's clear the interrupts and disable it.
113 */
114 printk (KERN_WARNING "spurious IRQ for DMA channel %d/n", i);
115 DCSR(i) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
116 }
117 }
118 }
119 returnIRQ_HANDLED;
120 }

该函数是DMA中断处理函数的总入口,它在一个循环中检查所有DMA通道,如果对应通道发生中断,而且有人注册了该通道,它则调用注册的中断处理函数。如果没有人注册该通道,它就重置对应的DCSR寄存器。

123 voidmhn_init_dmac(void)
124 {
125 inti;
126
127 for(i = 0; i < PXA_DMA_CHANNELS; i++) {
128 /*clear all write-1-to-clear bits */
129 DCSR(i) |= (DCSR_BUSERR | DCSR_STARTINTR | DCSR_ENDINTR |
130 DCSR_RASINTR | DCSR_EORINTR);
131 DCSR(i) = 0x0;
132 }
133
134 DINT = 0;
135
136 /*clear DRCMR0 ~ DRCMR63 */
137 for(i = 0; i < 64; i++)
138 DRCMR(i) = 0x0;
139
140 /*clear DRCMR64 ~ DRCMR99 */
141 for(i = 0; i < 36; i++)
142 *((volatileuint32_t*)&DRCMR64 + i) = 0x0;
143
144 /*clear all the 32 DMA descriptors */
145 for(i = 0; i < 32* 4; i++)
146 *((volatileuint32_t*)&DDADR0 + i) = 0x0;
147 }

该函数初始化所有通道的DMA寄存器,比如DCSRDINTDRCMRDDADR等。

150 staticint__init pxa_dma_init (void)
151 {
152 intret;
153
154 ret = request_irq (IRQ_DMA, dma_irq_handler, 0, "DMA", NULL);
155 if(ret)
156 printk (KERN_CRIT "Wow!Can't register IRQ for DMA/n");
157 returnret;
158 }

初始化DMA,向系统注册一个中断处理函数。

补充说明几点:

1. ARM平台对DMA操作做了一次抽象,它让DMA操作可以独立于具体硬件平台,这样驱动程序具有更好的可移植性,但不清楚什么原因,marvellDMA实现并没有按照这个标准的方式去做。ARMDMA的抽象如下:

structdma_ops {
int(*request)(dmach_t, dma_t *); /*optional */
void (*free)(dmach_t, dma_t *);/*optional */
void (*enable)(dmach_t, dma_t *);/*mandatory */
void (*disable)(dmach_t, dma_t *); /*mandatory */
int(*residue)(dmach_t, dma_t *); /*optional */
int(*setspeed)(dmach_t, dma_t *, int);/*optional */
char *type;
};

structdma_struct {
structscatterlist buf;/*single DMA */
intsgcount;/*number of DMA SG*/
structscatterlist *sg;/*DMA Scatter-Gather List*/

unsignedint active:1; /*Transfer active*/
unsignedint invalid:1;/*Address/Count changed */
unsignedint using_sg:1; /*using scatter list?*/
dmamode_t dma_mode; /*DMA mode*/
intspeed;/*DMA speed*/

unsignedint lock; /*Device is allocated */
constchar *device_id; /*Device name*/

unsignedint dma_base; /*Controller base address*/
intdma_irq;/*Controller IRQ*/
structscatterlist cur_sg; /*Current controller buffer*/
unsignedint state;

structdma_ops *d_ops;
};

2. 前面的代码没有涉及DMA的使用方法,这里我们看一段串口中代码,以补其不足。

672 staticvoidpxa_uart_receive_dma_start(structuart_pxa_port *up)
673 {
674 dbg("enter");
675 DCSR(up->rxdma)= DCSR_NODESC;// | DCSR_EORSTOPEN | DCSR_EORIRQEN;
676 DSADR(up->rxdma) = up->port.mapbase;
677 DTADR(up->rxdma) = up->rxdma_addr_phys;
678 DCMD(up->rxdma) = DCMD_INCTRGADDR | DCMD_FLOWSRC | DCMD_ENDIRQEN | DCMD_WIDTH1 | DCMD_BURST16 | DMA_BLOCK;
679 DCSR(up->rxdma) |= DCSR_RUN;
680 dbg("exit");
681 }

675marvell平台上,DMA有两种工作方式,一种可以传输多个不连续地址的buffer,称之为描述符方式传输。另外一种一次只能传输一个buffer,称为非描述符方式。这里设置为非描述符方式。

676 设置源地址,其为串口的FIFO

677 设置目标地址,其为物理内存地址。

678 设置命令寄存器。目标地址是内存,所以要加上DCMD_INCTRGADDR标志要求自动增加目标地址。而源地址是FIFO不需要显式的改变地址,所以不需要设置DCMD_INCSRCADDR标志。目标地址是内存,所以无需要流控。而源地址是FIFO,所以要设置源端流控DCMD_FLOWSRC标志。DCMD_ENDIRQEN标志允许传输完成时发现中断,DCMD_WIDTH1指明一个字节宽度,DCMD_BURST16指明一次传输16个字节,DMA_BLOCK指明传输数据的长度。

679 启动传输。

951 if(0== up->rxdma) {
952 up->rxdma =
953 pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_uart_receive_dma, up);
954 if(up->rxdma < 0)
955 gotoout;
956 }

971 if(NULL== up->rxdma_addr) {
972 up->rxdma_addr = dma_alloc_coherent(NULL, DMA_BLOCK, &up->rxdma_addr_phys, GFP_KERNEL);
973 if(!up->rxdma_addr)
974 gotorxdma_err_alloc;
975 }

951-956 注册DMA通道,pxa_uart_receive_dma为中断处理函数。

971-975 分配用于DMA传输的内存。

~~end~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值