zoukankan      html  css  js  c++  java
  • Marvelllinux研究—dma.c源代码分析

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

     

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

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

    更新时间:2007-7-16

     

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

     

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

     

     31 static struct dma_channel {
     32         char *name;
     33         void (*irq_handler)(int, void *, struct pt_regs *);
     34         void *data;
     35 } dma_channels[PXA_DMA_CHANNELS];

     

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

     

     38 int pxa_request_dma (char *name, pxa_dma_prio prio,
     39                          void (*irq_handler)(int, void *, struct pt_regs *),
     40                          void *data)
     41 {
     42         unsigned long flags;
     43         int i, 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         return i;
     81 }

     

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

     

    这里的优先级和开发手册中所说的略有差别,在开发手册中(11.3.1.1)里说,从硬件的角度,优先级分为四等,0等优先级最高,3等优先级最低。在代码中,优先级只分为高中低三等,高优先级和中优先级的通道数为8个,低优先级的通道数为16个。

     

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

     

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

     

    83 void pxa_free_dma (int dma_ch)
     84 {
     85         unsigned long flags;
     86
     87         if (!dma_channels[dma_ch].name) {
     88                 printk (KERN_CRIT
     89                         "%s: trying to free channel %d which 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 static irqreturn_t dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
    101 {
    102         int i, dint = DINT;
    103
    104         for (i = 0; i < PXA_DMA_CHANNELS; i++) {
    105                 if (dint & (1 << i)) {
    106                         struct dma_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         return IRQ_HANDLED;
    120 }

     

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

     

    123 void mhn_init_dmac(void)
    124 {
    125         int i;
    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                 *((volatile uint32_t *)&DRCMR64 + i) = 0x0;
    143
    144         /* clear all the 32 DMA descriptors */
    145         for (i = 0; i < 32 * 4; i++)
    146                 *((volatile uint32_t *)&DDADR0 + i) = 0x0;
    147 }

     

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

     

    150 static int __init pxa_dma_init (void)
    151 {
    152         int ret;
    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         return ret;
    158 }

     

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

     

    补充说明几点:

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

    struct dma_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;
    };

    struct dma_struct {
            struct scatterlist buf;                /* single DMA                 */
            int            sgcount;        /* number of DMA SG              */
            struct scatterlist *sg;                /* DMA Scatter-Gather List    */

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

            unsigned int   lock;           /* Device is allocated             */
            const char     *device_id;     /* Device name                  */

            unsigned int   dma_base;       /* Controller base address      */
            int            dma_irq;        /* Controller IRQ                */
            struct scatterlist cur_sg;     /* Current controller buffer        */
            unsigned int   state;

            struct dma_ops *d_ops;
    };

     

     

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

     672 static void pxa_uart_receive_dma_start(struct uart_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                         goto out;
     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                         goto rxdma_err_alloc;
     975         }    

     

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

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

     

    ~~end~~

     

     

     

     
  • 相关阅读:
    asterisk配置SIP服务器
    虚拟机桥接网卡下配置centOS静态IP
    在centOS5.9安装asterisk
    Cut 命令截取不同空格的string
    shell 中 贪婪匹配 和 非贪婪匹配
    shell 一些例子
    linux 系统时间 EST CST
    awk简单应用
    python3.5-ssh免输入密码过程
    GitHub个人使用入门
  • 原文地址:https://www.cnblogs.com/zhangyunlin/p/6167705.html
Copyright © 2011-2022 走看看