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

本文深入剖析了Marvell Linux平台上的DMA模块源代码,详细介绍了dma.c文件中的关键结构和函数,包括DMA通道的注册与释放过程,以及DMA中断处理机制。此外,还展示了串口DMA接收启动的具体实现。
导读:
  Marvell-linux研究—dma.c源代码分析
  
  转载时请注明出处和作者联系方式:http://blog.youkuaiyun.com/absurd
  作者联系方式:李先静
  更新时间: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
  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-1i >= 0i--) {
  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 = 0i
  105 if (dint &(1 <   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 = 0i
  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 = 0i <64i++)
  138 DRCMR(i) = 0x0
  139
  140 /* clear DRCMR64 ~ DRCMR99 */
  141 for (i = 0i <36i++)
  142 *(( volatile uint32_t *)&DRCMR64 + i) = 0x0
  143
  144 /* clear all the 32 DMA descriptors */
  145 for (i = 0i <32 * 4i++)
  146 *(( volatile uint32_t *)&DDADR0 + i) = 0x0
  147 }
  
  该函数初始化所有通道的DMA寄存器,比如DCSR、DINT、DRCMR和DDADR等。
  
  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操作可以独立于具体硬件平台,这样驱动程序具有更好的可移植性,但不清楚什么原因,marvell的DMA实现并没有按照这个标准的方式去做。ARM对DMA的抽象如下:
   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 }
  
  675在marvell平台上,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~~
  
  
  
  
  Trackback: http://tb.blog.youkuaiyun.com/TrackBack.aspx?PostId=1694095

本文转自
http://blog.youkuaiyun.com/absurd/archive/2007/07/16/1694095.aspx
FAILED: drivers/librte_net_mvneta.so.23.0 aarch64-marvell-linux-gnu-gcc -o drivers/librte_net_mvneta.so.23.0 drivers/librte_net_mvneta.so.23.0.p/meson-generated_.._rte_net_mvneta.pmd.c.o drivers/libtmp_rte_net_mvneta.a.p/net_mvneta_mvneta_ethdev.c.o drivers/libtmp_rte_net_mvneta.a.p/net_mvneta_mvneta_rxtx.c.o -Wl,--as-needed -Wl,--no-undefined -Wl,-O1 -shared -fPIC -Wl,--start-group -Wl,-soname,librte_net_mvneta.so.23 -Wl,--no-as-needed -pthread -lm -ldl lib/librte_ethdev.so.23.0 lib/librte_eal.so.23.0 lib/librte_kvargs.so.23.0 lib/librte_telemetry.so.23.0 lib/librte_net.so.23.0 lib/librte_mbuf.so.23.0 lib/librte_mempool.so.23.0 lib/librte_ring.so.23.0 lib/librte_meter.so.23.0 drivers/librte_bus_pci.so.23.0 lib/librte_pci.so.23.0 drivers/librte_bus_vdev.so.23.0 lib/librte_cfgfile.so.23.0 drivers/librte_common_mvep.so.23.0 /mydisk/respo/enterprise_gateway/staging_dir/target-aarch64-marvell-linux-gnu-cn25g-vpp_v1/usr/lib/libmusdk.a -Wl,--end-group '-Wl,-rpath,$ORIGIN/../lib:$ORIGIN/' -Wl,-rpath-link,/mydisk/respo/enterprise_gateway/build_dir/target-aarch64-marvell-linux-gnu-cn25g-vpp_v1/dpdk-22.11-SDK12.25.01/build/lib -Wl,-rpath-link,/mydisk/respo/enterprise_gateway/build_dir/target-aarch64-marvell-linux-gnu-cn25g-vpp_v1/dpdk-22.11-SDK12.25.01/build/drivers /mydisk/respo/enterprise_gateway/staging_dir/marvell-tools-249.0/bin/../lib/gcc/aarch64-marvell-linux-gnu/7.3.0/../../../../aarch64-marvell-linux-gnu/bin/ld: /mydisk/respo/enterprise_gateway/staging_dir/target-aarch64-marvell-linux-gnu-cn25g-vpp_v1/usr/lib/libmusdk.a(libmusdk_la-sys_dma.o): relocation R_AARCH64_ADR_PREL_PG_HI21 against symbol `__dma_size' which may bind externally can not be used when making a shared object; recompile with -fPIC /mydisk/respo/enterprise_gateway/staging_dir/target-aarch64-marvell-linux-gnu-cn25g-vpp_v1/usr/lib/libmusdk.a(libmusdk_la-sys_dma.o): In function `mv_sys_dma_mem_get_info': sys_dma.c:(.text+0x20): dangerous relocation: unsupported relocation /mydisk/respo/enterprise_gateway/staging_dir/marvell-tools-249.0/bin/../lib/gcc/aarch64-marvell-linux-gnu/7.3.0/../../../../aarch64-marvell-linux-gnu/bin/ld: /mydisk/respo/enterprise_gateway/staging_dir/target-aarch64-marvell-linux-gnu-cn25g-vpp_v1/usr/lib/libmusdk.a(libmusdk_la-sys_dma.o): relocation R_AARCH64_ADR_PREL_PG_HI21 against symbol `__dma_size' which may bind externally can not be used when making a shared object; recompile with -fPIC /mydisk/respo/enterprise_gateway/staging_dir/target-aarch64-marvell-linux-gnu-cn25g-vpp_v1/usr/lib/libmusdk.a(libmusdk_la-sys_dma.o): In function `mv_sys_dma_mem_init': sys_dma.c:(.text+0x138): dangerous relocation: unsupported relocation /mydisk/respo/enterprise_gateway/staging_dir/marvell-tools-249.0/bin/../lib/gcc/aarch64-marvell-linux-gnu/7.3.0/../../../../aarch64-marvell-linux-gnu/bin/ld: /mydisk/respo/enterprise_gateway/staging_dir/target-aarch64-marvell-linux-gnu-cn25g-vpp_v1/usr/lib/libmusdk.a(libmusdk_la-sys_dma.o): relocation R_AARCH64_ADR_PREL_PG_HI21 against symbol `__dma_size' which may bind externally can not be used when making a shared object; recompile with -fPIC /mydisk/respo/enterprise_gateway/staging_dir/target-aarch64-marvell-linux-gnu-cn25g-vpp_v1/usr/lib/libmusdk.a(libmusdk_la-sys_dma.o): In function `mv_sys_dma_virt_is_valid': sys_dma.c:(.text+0xbe8): dangerous relocation: unsupported relocation /mydisk/respo/enterprise_gateway/staging_dir/marvell-tools-249.0/bin/../lib/gcc/aarch64-marvell-linux-gnu/7.3.0/../../../../aarch64-marvell-linux-gnu/bin/ld: /mydisk/respo/enterprise_gateway/staging_dir/target-aarch64-marvell-linux-gnu-cn25g-vpp_v1/usr/lib/libmusdk.a(libmusdk_la-sys_dma.o): relocation R_AARCH64_ADR_PREL_PG_HI21 against symbol `__dma_size' which may bind externally can not be used when making a shared object; recompile with -fPIC /mydisk/respo/enterprise_gateway/staging_dir/target-aarch64-marvell-linux-gnu-cn25g-vpp_v1/usr/lib/libmusdk.a(libmusdk_la-sys_dma.o): In function `mv_sys_dma_phys_is_valid': sys_dma.c:(.text+0xc18): dangerous relocation: unsupported relocation /mydisk/respo/enterprise_gateway/staging_dir/marvell-tools-249.0/bin/../lib/gcc/aarch64-marvell-linux-gnu/7.3.0/../../../../aarch64-marvell-linux-gnu/bin/ld: /mydisk/respo/enterprise_gateway/staging_dir/target-aarch64-marvell-linux-gnu-cn25g-vpp_v1/usr/lib/libmusdk.a(libmusdk_la-uio_utils.o): relocation R_AARCH64_ADR_PREL_PG_HI21 against symbol `alphasort@@GLIBC_2.17' which may bind externally can not be used when making a shared object; recompile with -fPIC /mydisk/respo/enterprise_gateway/staging_dir/marvell-tools-249.0/bin/../lib/gcc/aarch64-marvell-linux-gnu/7.3.0/../../../../aarch64-marvell-linux-gnu/bin/ld: /mydisk/respo/enterprise_gateway/staging_dir/target-aarch64-marvell-linux-gnu-cn25g-vpp_v1/usr/lib/libmusdk.a(libmusdk_la-uio_utils.o)(.text+0x264): unresolvable R_AARCH64_ADR_PREL_PG_HI21 relocation against symbol `alphasort@@GLIBC_2.17' /mydisk/respo/enterprise_gateway/staging_dir/marvell-tools-249.0/bin/../lib/gcc/aarch64-marvell-linux-gnu/7.3.0/../../../../aarch64-marvell-linux-gnu/bin/ld: final link failed: Bad value collect2: error: ld returned 1 exit status [88/1221] Generating rte_net_cnxk.pmd.c with a custom command ninja: build stopped: subcommand failed. Found runner: /usr/bin/ninja 这是什么编译错误
最新发布
12-13
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值