免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1337 | 回复: 0
打印 上一主题 下一主题

i.mx27下的DMA浅析(二) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-02-03 16:42 |只看该作者 |倒序浏览

四,我们顺着上一篇讲到的DMA流程,一步一步分析其余代码。
4.1 mxc_dma_request函数:
int mxc_dma_request(mxc_dma_device_t channel_id, char *dev_name)
{
     mxc_dma_channel_t *dma;
     mx2_dma_priv_t *dma_private = NULL;
     mx2_dma_info_t *dma_info = mxc_dma_get_info(channel_id);
     int index;
     int ret;

     if (dma_info == NULL) {
         return -EINVAL;
     }

     if ((index = get_dma_channel(dma_info->dma_chan))
         return -ENODEV;
     }

     dma = g_dma_channels + index;
     dma_private = (mx2_dma_priv_t *) dma->private;
     if (dma_private == NULL) {
         printk(KERN_ERR
                "request dma channel %d which is not initialize completed.!\n",
                index);
         ret = -EFAULT;
         goto exit;
     }

     dma->active = 0;
     dma_private->dma_info = NULL;
     dma->cb_fn = NULL;
     dma->cb_args = NULL;
     dma->dev_name = dev_name;
     dma->mode = dma_info->mode ? MXC_DMA_MODE_WRITE : MXC_DMA_MODE_READ;
     init_dma_bd(dma_private);

     if (!(ret = __init_dma_channel(dma, dma_info))) {
         dma_private->dma_info = dma_info;
         return index;
     }
      exit:
     put_dma_channel(index);
     return ret;
}
这个函数通过预定义的ID来找到一个空闲的通道号。
在/arch/arm/mach-mx27/dma.c里预定义了这些DEVICE_ID,如下:
static dma_info_entry_t active_dma_info[] = {
     {MXC_DMA_TEST_RAM2RAM, &ram2ram_dma_info},
     {MXC_DMA_TEST_RAM2D2RAM2D, &ram2d2ram2d_dma_info},
     {MXC_DMA_TEST_RAM2RAM2D, &ram2ram2d_dma_info},
     ……
};
可以发现我们这个例子时用到的MXC_DMA_TEST_RAM2RAM。
接下来根据找到的通道号把对应的全局变量g_dma_channels作些初始化(包括初始化DMA的寄存器,缓冲区,申请中断等等),这些都不难理解。
4.2.设置回调函数
int mxc_dma_callback_set(int channel_num, mxc_dma_callback_t callback,
               void *arg)
{
     mxc_dma_channel_t *dma;
     printk("mxc_dma_callback_set: enter\n");
     if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num
         return -EINVAL;
     }
     dma = g_dma_channels + channel_num;

     if (!dma->lock) {
         return -ENODEV;
     }

     if (dma->active) {
         return -EBUSY;
     }
     dma->cb_fn = callback;
     dma->cb_args = arg;
     return 0;

}
直接把回调函数指针callback及其参数赋给了对应的g_dma_channels的成员。
     dma->cb_fn = callback;
     dma->cb_args = arg;
可想而知这个callback一定会在中断函数dma_irq_handler里调用的。
4.3. 配置DMA通道
int mxc_dma_config(int channel_num, mxc_dma_requestbuf_t * dma_buf, int num_buf,
            mxc_dma_mode_t mode)
{
     mxc_dma_channel_t *dma;
     mx2_dma_priv_t *dma_private;

     if ((dma_buf == NULL) || (num_buf
         return -EINVAL;
     }

     if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num
         return -EINVAL;
     }

     dma = g_dma_channels + channel_num;
     dma_private = (mx2_dma_priv_t *) dma->private;
     if (dma_private == NULL) {
         printk(KERN_ERR
                "config dma %d which is not completed initialization \n",
                channel_num);
         return -EFAULT;
     }

     if (dma->lock == 0) {
         return -ENODEV;
     }

     /*TODO: dma chainning can not support on bi-dir channel */
     if (dma_private->dma_chaining && (dma->mode != mode)) {
         return -EINVAL;
     }

     /*TODO: fill dma buffer into driver .
      * If driver is no enought buffer to save them , it will return -EBUSY
      */
     if (fill_dma_bd(dma, dma_buf, num_buf, mode)) {
         return -EBUSY;
     }

     return 0;
}
真正做实际工作的是fill_dma_bd()函数
static inline int fill_dma_bd(mxc_dma_channel_t * dma,
                    mxc_dma_requestbuf_t * buf, int num,
                    mxc_dma_mode_t mode)
{
     int i, wr;
     unsigned long flags, mask;
     mx2_dma_priv_t *priv = dma->private;
     mx2_dma_bd_t *p, *q;
     if ((atomic_read(&(priv->bd_used)) + num) > MAX_BD_SIZE) {
         return -EBUSY;
     }

     for (i = 0; i
         wr = priv->bd_wr;
         p = priv->bd_ring + wr;
         p->mode = mode;
         p->count = buf.num_of_bytes;
         p->src_addr = buf.src_addr;
         p->dst_addr = buf.dst_addr;
         if (i == num - 1) {
              p->state = DMA_BD_ST_LAST | DMA_BD_ST_PEND;
         } else {
              p->state = DMA_BD_ST_PEND;
         }
         priv->bd_wr = (wr + 1) % MAX_BD_SIZE;
         atomic_inc(&(priv->bd_used));

          if (atomic_read(&(priv->bd_used)) != 2)
              continue;
         /* Disable interrupt of this channel */
         local_irq_save(flags);
         local_irq_disable();
         save_dma_interrupt(mask);
         mask_dma_interrupt(dma->channel);
         local_irq_restore(flags);
         /*TODO ::
          *  If channel is transfering and supports chain_buffer,
          *  when the new buffer is 2st buffer , repeat must be enabled
          */
         if (priv->dma_chaining && dma->active) {
              q = priv->bd_ring + priv->bd_rd;
              if (q && (q->state & DMA_BD_ST_BUSY)) {
                   if (atomic_read(&(priv->bd_used)) == 2) {
                       setup_dmac(dma);
                   }
              }
         }
         restore_dma_interrupt(mask);
     }
     return 0;
}
priv->bd_ring相当于一个环形数据结构,在这个结构里有几个已使用的dma_bd用priv->bd_used表示,priv->bd_wr是一个索引,指向最新加入(或者说是使用)的dma_bd,每一个dma_bd都存放着源地址src_addr,目标地址dst_addr和存取字节数count,可以把dma_bd理解成DMA要完成的一次小任务。
4.4. 使能DMA
int mxc_dma_enable(int channel_num)
{
     mxc_dma_channel_t *dma;
     mx2_dma_priv_t *priv;
     if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num
         return -EINVAL;
     }

     dma = g_dma_channels + channel_num;

     if (dma->lock == 0) {
         return -EINVAL;
     }

     priv = (mx2_dma_priv_t *) dma->private;
     if (priv == NULL) {
         printk(KERN_ERR "enable a uncompleted dma channel %d\n",
                channel_num);
         return -EFAULT;
     }

     if (dma->active) {
         return 0;
     }
     dma->active = 1;
     priv->trans_bytes = 0;

     enable_dma_clk();

     atomic_inc(&g_dma_actived);
     __clear_dma_interrupt(channel_num);

     setup_dmac(dma);
     disable_dma_clk();
     return 0;
}
它最终调用setup_dmac(dma)来开始DMA的传输。
static void setup_dmac(mxc_dma_channel_t * dma)
{
     mx2_dma_priv_t *priv = (mx2_dma_priv_t *) dma->private;
     dma_regs_t *dma_base = (dma_regs_t *) (priv->dma_base);
     mx2_dma_bd_t *p, *q;
     unsigned long ctrl_val;
     printk("setup_dmac: enter\n");
     if (dma->active == 0) {
         printk(KERN_ERR
                "dma channel %d is not enabled, when receiving this channel 's interrupt\n",
                dma->channel);
         return;
     }
     if (atomic_read(&(priv->bd_used))
         printk(KERN_ERR "dma channel %d is empty\n", dma->channel);
         dma->active = 0;
         atomic_dec(&g_dma_actived);
         return;
     }
     p = priv->bd_ring + priv->bd_rd;
     q = next_dma_bd(priv);
     if (!(p->state & DMA_BD_ST_BUSY)) {
         /*NOTICE:: This is first buffer or dma chain does not support chain-buffer. So CEN must clear & set again */
         ctrl_val =
             __raw_readl(&(dma_base->Ctl)) &
             (~(DMA_CTL_ACRPT | DMA_CTL_RPT | DMA_CTL_CEN));
         __raw_writel(ctrl_val, &(dma_base->Ctl));
         if (p->mode != dma->mode) {
              dma->mode = p->mode;   /* bi-dir channel do mode change */
              if (dma->mode == MXC_DMA_MODE_READ) {
                   DMA_CTL_SET_SMOD(ctrl_val,
                             priv->dma_info->sourceType);
                   DMA_CTL_SET_SSIZ(ctrl_val,
                             priv->dma_info->sourcePort);
                   DMA_CTL_SET_DMOD(ctrl_val,
                             priv->dma_info->destType);
                   DMA_CTL_SET_DSIZ(ctrl_val,
                             priv->dma_info->destPort);
              } else {
                   DMA_CTL_SET_SMOD(ctrl_val,
                             priv->dma_info->destType);
                   DMA_CTL_SET_SSIZ(ctrl_val,
                             priv->dma_info->destPort);
                   DMA_CTL_SET_DMOD(ctrl_val,
                             priv->dma_info->sourceType);
                   DMA_CTL_SET_DSIZ(ctrl_val,
                             priv->dma_info->sourcePort);
              }
         }
         __raw_writel(p->src_addr, &(dma_base->SourceAddr));
         __raw_writel(p->dst_addr, &(dma_base->DestAddr));
         __raw_writel(p->count, &(dma_base->Count));
         p->state |= DMA_BD_ST_BUSY;
         p->state &= ~(DMA_BD_ST_PEND);
         ctrl_val |= DMA_CTL_CEN;
         __raw_writel(ctrl_val, &(dma_base->Ctl));
         if (q && priv->dma_chaining) {   /*DO chain-buffer */
              __raw_writel(q->src_addr, &(dma_base->SourceAddr));
              __raw_writel(q->dst_addr, &(dma_base->DestAddr));
              __raw_writel(q->count, &(dma_base->Count));
              q->state |= DMA_BD_ST_BUSY;
              q->state &= ~(DMA_BD_ST_PEND);
              ctrl_val |= DMA_CTL_ACRPT | DMA_CTL_RPT | DMA_CTL_CEN;
              __raw_writel(ctrl_val, &(dma_base->Ctl));
         }
     } else {      /* Just dma channel which supports dma buffer can run to there */
         BUG_ON(!priv->dma_chaining);
         if (q) { /* p is tranfering, then q must be set into dma controller */
              /*WARNING:: [1] dangerous area begin.
               *      If the p is completed during MCU run in this erea, the dma channel is crashed.
               */
              __raw_writel(q->src_addr, &(dma_base->SourceAddr));
              __raw_writel(q->dst_addr, &(dma_base->DestAddr));
              __raw_writel(q->count, &(dma_base->Count));
              /*WARNING:: [2] dangerous area end */
              ctrl_val =
                  __raw_readl(&(dma_base->Ctl)) | (DMA_CTL_ACRPT |
                                      DMA_CTL_RPT |
                                      DMA_CTL_CEN);
              __raw_writel(ctrl_val, &(dma_base->Ctl));

              /* WARNING:: This is workaround and it is dangerous:
               *      the judgement is not safety.
               */
              if (!__get_dma_interrupt(dma->channel)) {
                   q->state |= DMA_BD_ST_BUSY;
                   q->state &= ~(DMA_BD_ST_PEND);
              } else {
                   /*Waiting re-enable is in ISR */
                   printk(KERN_ERR
                          "Warning:: The privous transfer is completed. Maybe the chain buffer is stopped.");
              }
         } else { /* Last buffer is transfering: just clear RPT bit */
              ctrl_val =
                  __raw_readl(&(dma_base->Ctl)) &
                  (~(DMA_CTL_ACRPT | DMA_CTL_RPT));
              __raw_writel(ctrl_val, &(dma_base->Ctl));
         }
     }
}
priv->bd_rd也是一个索引,指向当前要进行处理的一个dma_bd。
p = priv->bd_ring + priv->bd_rd是把当前的dma_bd取出来,接下来设置通道源地址/目标地址寄等存器,最后通过设置DMA_CTL_CEN位,使能这个通道,开始一次的DMA传输。
五,小结:这只是对i.mx27平台上的DMA工作流程作的一个大体上的浅析,很多细节都略过去了。有兴趣的朋友请自行研究。

------------------------------------------
本文乃原创!
转载请注明出处:http://sparklecliz.cublog.cn/
------------------------------------------


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/107046/showart_2170413.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP