Chinaunix首页 | 论坛 | 博客
  • 博客访问: 110455
  • 博文数量: 24
  • 博客积分: 1410
  • 博客等级: 上尉
  • 技术积分: 270
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-30 18:17
文章分类

全部博文(24)

文章存档

2010年(21)

2009年(3)

我的朋友

分类: LINUX

2010-02-03 16:42:54

四,我们顺着上一篇讲到的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)) < 0) {

         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 < 0)) {

         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 < 1)) {

         return -EINVAL;

     }

 

     if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) {

         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 < num; i++) {

         wr = priv->bd_wr;

         p = priv->bd_ring + wr;

         p->mode = mode;

         p->count = buf[i].num_of_bytes;

         p->src_addr = buf[i].src_addr;

         p->dst_addr = buf[i].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_bdpriv->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 < 0)) {

         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)) <= 0) {

         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/
------------------------------------------
阅读(1878) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~