Chinaunix首页 | 论坛 | 博客
  • 博客访问: 41311
  • 博文数量: 21
  • 博客积分: 840
  • 博客等级: 准尉
  • 技术积分: 225
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-24 00:24
文章分类
文章存档

2010年(21)

我的朋友

分类: LINUX

2010-05-24 00:41:45

 

关于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

阅读(1000) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:硬件设计的几个基本问题

给主人留下些什么吧!~~