关于mx27 DMA 驱动
我们先来看一下涉及的数据结构: /*! This defines the DMA channel parameters */ typedef struct mxc_dma_channel { unsigned int active:1; /*!< When there has a active tranfer, it is set to 1 */ unsigned int lock; /*!< Defines the channel is allocated or not */ int curr_buf; /*!< Current buffer */ mxc_dma_mode_t mode; /*!< Read or Write */ unsigned int channel; /*!< Channel info */ unsigned int dynamic:1; /*!< Channel not statically allocated when 1 */ char *dev_name; /*!< Device name */ void *private; /*!< Private structure for platform */ mxc_dma_callback_t cb_fn; /*!< The callback function */ void *cb_args; /*!< The argument of callback function */ } mxc_dma_channel_t; 一看就知道这个是描述DMA通道的,这里的active和dynamic还用了位域定义,有这个必要吗?然后在dma_mx2.c的最前面定义了这么一个静态数组: static mxc_dma_channel_t g_dma_channels[MAX_DMA_CHANNELS];
/*! * Structure of dma buffer descriptor */ typedef struct { unsigned long state; /*!< dma bd state */ int mode; /*!< the dma mode of this bd */ unsigned long count; /*!< the length of the dma transfer */ unsigned long src_addr; /*!< the source address of the dma transfer */ unsigned long dst_addr; /*!< the destination address of the dma transfer */ } mx2_dma_bd_t;
/*! * This structure containing the private information for MX2 */ typedef struct mx2_dma_priv_s { unsigned int dma_chaining:1; /* 1: using headware dma chaining feature */ unsigned int ren:1; /* 1: dma start besed on request signal */ unsigned long trans_bytes; /* To store the transfered data bytes in this transfer */ mx2_dma_info_t *dma_info; /* To store the pointer for dma parameter for reading and wirting */ int bd_rd; /* the read index of bd ring */ int bd_wr; /* the write index of bd ring */ atomic_t bd_used; /* the valid bd number in bd ring */ mx2_dma_bd_t *bd_ring; /* the pointer of bd ring */ unsigned long dma_base; /* register base address of this channel */ int dma_irq; /* irq number of this channel */ } mx2_dma_priv_t; 这个BD和FEC里的BD可是两个东西,这里的BD只是纯软件的定义,不像FEC里的BD有相应的硬件寄存器。这里也用了两个位域dma_chaining和ren,接下来dma_mx2.c中有定义: static mx2_dma_priv_t g_dma_privates[MXC_DMA_CHANNELS]; static mx2_dma_bd_t g_dma_bd_table[MXC_DMA_CHANNELS][MAX_BD_SIZE]; 其中MXC_DMA_CHANNELS为16,MAX_BD_SIZE为64
下面来看一下这个数据结构: /*! This defines the list of device ID's for DMA */ typedef enum mxc_dma_device { MXC_DMA_UART1_RX, MXC_DMA_UART1_TX, MXC_DMA_UART2_RX, MXC_DMA_UART2_TX, MXC_DMA_UART3_RX, MXC_DMA_UART3_TX, MXC_DMA_UART4_RX, MXC_DMA_UART4_TX, MXC_DMA_UART5_RX, MXC_DMA_UART5_TX, MXC_DMA_UART6_RX, MXC_DMA_UART6_TX, MXC_DMA_MMC1_WIDTH_1, MXC_DMA_MMC1_WIDTH_4, MXC_DMA_MMC2_WIDTH_1, MXC_DMA_MMC2_WIDTH_4, MXC_DMA_SSI1_8BIT_RX0, MXC_DMA_SSI1_8BIT_TX0, MXC_DMA_SSI1_16BIT_RX0, MXC_DMA_SSI1_16BIT_TX0, MXC_DMA_SSI1_24BIT_RX0, MXC_DMA_SSI1_24BIT_TX0, MXC_DMA_SSI1_8BIT_RX1, MXC_DMA_SSI1_8BIT_TX1, MXC_DMA_SSI1_16BIT_RX1, MXC_DMA_SSI1_16BIT_TX1, MXC_DMA_SSI1_24BIT_RX1, MXC_DMA_SSI1_24BIT_TX1, MXC_DMA_SSI2_8BIT_RX0, MXC_DMA_SSI2_8BIT_TX0, MXC_DMA_SSI2_16BIT_RX0, MXC_DMA_SSI2_16BIT_TX0, MXC_DMA_SSI2_24BIT_RX0, MXC_DMA_SSI2_24BIT_TX0, MXC_DMA_SSI2_8BIT_RX1, MXC_DMA_SSI2_8BIT_TX1, MXC_DMA_SSI2_16BIT_RX1, MXC_DMA_SSI2_16BIT_TX1, MXC_DMA_SSI2_24BIT_RX1, MXC_DMA_SSI2_24BIT_TX1, MXC_DMA_FIR_RX, MXC_DMA_FIR_TX, MXC_DMA_CSPI1_RX, MXC_DMA_CSPI1_TX, MXC_DMA_CSPI2_RX, MXC_DMA_CSPI2_TX, MXC_DMA_CSPI3_RX, MXC_DMA_CSPI3_TX, MXC_DMA_ATA_RX, MXC_DMA_ATA_TX, MXC_DMA_MEMORY, MXC_DMA_DSP_PACKET_DATA0_RD, MXC_DMA_DSP_PACKET_DATA0_WR, MXC_DMA_DSP_PACKET_DATA1_RD, MXC_DMA_DSP_PACKET_DATA1_WR, MXC_DMA_DSP_LOG0_CHNL, MXC_DMA_DSP_LOG1_CHNL, MXC_DMA_DSP_LOG2_CHNL, MXC_DMA_DSP_LOG3_CHNL, MXC_DMA_CSI_RX, MXC_DMA_TEST_RAM2D2RAM, MXC_DMA_TEST_RAM2RAM2D, MXC_DMA_TEST_RAM2D2RAM2D, MXC_DMA_TEST_RAM2RAM, MXC_DMA_TEST_HW_CHAINING, MXC_DMA_TEST_SW_CHAINING } mxc_dma_device_t; 这里枚举了MX27系统中的预先定义好的ID,而与这些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_RAM2D2RAM, &ram2d2ram_dma_info}, {MXC_DMA_TEST_HW_CHAINING, &hw_chaining_dma_info}, {MXC_DMA_TEST_SW_CHAINING, &sw_chaining_dma_info}, {MXC_DMA_ATA_RX, &ata_rx_dma_info}, {MXC_DMA_ATA_TX, &ata_tx_dma_info}, {MXC_DMA_UART1_RX, &uart1_rx_dma_info}, {MXC_DMA_UART1_TX, &uart1_tx_dma_info}, {MXC_DMA_UART2_RX, &uart2_rx_dma_info}, {MXC_DMA_UART2_TX, &uart2_tx_dma_info}, {MXC_DMA_UART3_RX, &uart3_rx_dma_info}, {MXC_DMA_UART3_TX, &uart3_tx_dma_info}, {MXC_DMA_UART4_RX, &uart4_rx_dma_info}, {MXC_DMA_UART4_TX, &uart4_tx_dma_info}, {MXC_DMA_UART5_RX, &uart5_rx_dma_info}, {MXC_DMA_UART5_TX, &uart5_tx_dma_info}, {MXC_DMA_UART6_RX, &uart6_rx_dma_info}, {MXC_DMA_UART6_TX, &uart6_tx_dma_info}, {MXC_DMA_SSI1_16BIT_RX0, &ssi1_16bit_rx0_dma_info}, {MXC_DMA_SSI1_16BIT_TX0, &ssi1_16bit_tx0_dma_info}, {MXC_DMA_SSI2_16BIT_RX0, &ssi2_16bit_rx0_dma_info}, {MXC_DMA_SSI2_16BIT_TX0, &ssi2_16bit_tx0_dma_info}, {MXC_DMA_MMC1_WIDTH_1, &mmc1_width1_dma_info}, {MXC_DMA_MMC1_WIDTH_4, &mmc1_width4_dma_info}, {MXC_DMA_MMC2_WIDTH_1, &mmc2_width1_dma_info}, {MXC_DMA_MMC2_WIDTH_4, &mmc2_width4_dma_info}, {MXC_DMA_CSI_RX, &csi_rx_dma_info}, }; 我们选取MMC的属性描述看下: static mx2_dma_info_t mmc1_width4_dma_info = { .dma_chan = MXC_DMA_DYNAMIC_CHANNEL, .mode = 0, .rto_en = 0, .dir = 0, .dma_chaining = 0,.ren = 1, .burstLength = 0,.request = DMA_REQ_SDHC1,.busuntils = 0, .sourceType = DMA_TYPE_FIFO,.sourcePort = DMA_MEM_SIZE_32, .destType = DMA_TYPE_LINEAR,.destPort = DMA_MEM_SIZE_32, .per_address = (SDHC1_BASE_ADDR + 0x38), .M2D_Valid = 0, }; 这些属性怎么与channel挂接起来呢,流程是这样的,当request的时候传入一个ID,然后根据这个ID查找到对应的属性描述,然后现将这个属性描述挂接到channel的info指针里去。
2: 我们来看下初始化函数 int __init mxc_dma_init(void) { int i; mxc_dma_channel_t *dma = g_dma_channels; mx2_dma_priv_t *private = g_dma_privates;
memset(dma, 0, sizeof(mxc_dma_channel_t) * MXC_DMA_CHANNELS); for (i = 0; i < MXC_DMA_CHANNELS; i++, dma++, private++) { dma->channel = i; dma->private = private; private->dma_base = (unsigned int)(IO_ADDRESS(DMA_BASE_ADDR + DMA_CH_BASE(i))); private->dma_irq = i + MXC_DMA_INTR_0; /*Dma channel interrupt number */ private->bd_ring = &g_dma_bd_table[i][0]; } /*这里,最前面定义的三个静态变量就通过指针连接起来了,并且这里要注意的是第个DMA通道都有独立的中断号(i+MXC_DMA_INT_0,即是i+32)*/ mxc_dma_load_info(g_dma_channels);/*这个只是设置16个通道的dma->dynamic为1*/
dma_clk = clk_get(NULL, "dma_clk"); clk_enable(dma_clk);
__raw_writel(0x2, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DCR); /*reset DMA; */
disable_dma_clk();
/*use module init because create_proc after init_dma */ g_proc_dir = create_proc_entry("dma", 0, NULL); g_proc_dir->read_proc = (read_proc_t *) mxc_get_dma_list; g_proc_dir->data = NULL;
#ifdef DMA_PM /* Register the device with power management. */ dma_pm = pm_register(PM_DMA_DEV, PM_SYS_UNKNOWN, dma_pm_handler); #endif
return 0; } 其实这里我也很奇怪,就是为什么dma->dynamic都为1,也就是为什么在dma.c里定义static dma_info_entry_t active_dma_info[] 的时候将所有的通道都置为MXC_DMA_DYNAMIC_CHANNEL,那是不是说这些通道在有request的时候才分配给使用这样就是dynamic?那什么样的情况算是static呢?
接下来应该看一下提供给外部模块的request函数: /*! * This function is generally called by the driver at open time. * The DMA driver would do any initialization steps that is required * to get the channel ready for data transfer. * * @param channel_id a pre-defined id. The peripheral driver would specify * the id associated with its peripheral. This would be * used by the DMA driver to identify the peripheral * requesting DMA and do the necessary setup on the * channel associated with the particular peripheral. * The DMA driver could use static or dynamic DMA channel * allocation. * @param dev_name module name or device name * @return returns a negative number on error if request for a DMA channel did not * succeed, returns the channel number to be used on success. */ 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);/*每个id对应有dma.c里定义好的属性,这个在上面有一些描述*/ 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 bd也很简单,就是将dma->private->bd_used置为0,然后将64个BD的状态置为初始的0*/ 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; } 要详细展开的是__init_dma_channel这个函数: /*!@brief setup interrupt and setup dma channel by dma parameter */ static inline int __init_dma_channel(mxc_dma_channel_t * chan, mx2_dma_info_t * dma_info) { mx2_dma_priv_t *dma_private = (mx2_dma_priv_t *) chan->private; dma_regs_t *dma_base; int ret; mask_dma_interrupt(chan->channel); ret = request_irq(dma_private->dma_irq, dma_irq_handler, SA_INTERRUPT | SA_SHIRQ, chan->dev_name, (void *)chan); if (ret) { printk(KERN_ERR "%s: unable to request IRQ %d for DMA channel\n", chan->dev_name, dma_private->dma_irq); return ret; } enable_dma_clk(); dma_base = (dma_regs_t *) (dma_private->dma_base); __raw_writel(0, &(dma_base->Ctl)); ret = 0; if ((ret = setup_dma_channel(chan, dma_info))) { free_irq(dma_private->dma_irq, (void *)chan); } disable_dma_clk(); return 0; } 申请中断,注意的是每个DMA通道都有自己的硬件中断号,但每个通道的中断函数函数都是同一个函数,也就是一个中断函数处理了多个中断线上的中断,这要与多个中断处理函数共享一个硬件中断线区别开来。这里我也还有一个疑问,就是当其中一个DMA通道上产生中断后,进入中断处理函数,那么这时还会产生其它DMA通道上的中断么?也即是DMA中断处理函数会被其它的DMA通道所中断么?这个问题先保留在这。 setup_dma_channel函数有点长,其实做的事不复杂,就是将ID对应的DMA通道属性设置入MX27的硬件寄存器中。 返回 返回
下面看下config: /*! * This function would just configure the buffers specified by the user into * dma channel. The caller must call mxc_dma_enable to start this transfer. * * @param channel_num the channel number returned at request time. This * would be used by the DMA driver to identify the calling * driver and do the necessary cleanup on the channel * associated with the particular peripheral * @param dma_buf an array of physical addresses to the user defined * buffers. The caller must guarantee the dma_buf is * available until the transfer is completed. * @param num_buf number of buffers in the array * @param mode specifies whether this is READ or WRITE operation * @return This function returns a negative number on error if buffer could not be * added with DMA for transfer. On Success, it returns 0 */ 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,这个上面的todo说的是这个fill_dma_bd不完善的地方,当请求的bd数大于64的时候就会失败。来看下fill_dma_bd: /*!@brief add dma buffer into buffer descriptor ring */ 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; }/*因为最前面定义的最大的BD数为64*/ /*在我们使用的mx27系统中,一般的DMA都只会用到1个BD,也即是config函数传入的num_buf大多都为1*/ 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)); #ifdef SOUND_DMA_PATCH #else 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_dma
|