S3C2440 DMA 驱动示例

将 DMA 抽象为一个字符设备,在初始化函数中调用

void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)

函数来分配两段物理地址连续的空间,一段作为源空间,一段作为目的空间。

然后将物理地址进行 ioremap 供驱动使用,最后调用 register_chrdev 来注册这个字符设备。

DMA 的 regs:

#define DMA0_BASE_ADDR  0x4B000000
#define DMA1_BASE_ADDR  0x4B000040
#define DMA2_BASE_ADDR  0x4B000080
#define DMA3_BASE_ADDR  0x4B0000C0

struct s3c_dma_regs {
    unsigned long disrc;
    unsigned long disrcc;
    unsigned long didst;
    unsigned long didstc;
    unsigned long dcon;
    unsigned long dstat;
    unsigned long dcsrc;
    unsigned long dcdst;
    unsigned long dmasktrig;
};

配置 DMA(通过 ioctl 调用)

ev_dma = 0;

    /* 把源,目的,长度告诉 DMA */
    dma_regs->disrc      = src_phys;            /* 源的物理地址 */
    dma_regs->disrcc     = (0<<1) | (0<<0);     /* 源位于 AHB 总线, 源地址递增 */
    dma_regs->didst      = dst_phys;            /* 目的的物理地址 */
    dma_regs->didstc     = (0<<2) | (0<<1) | (0<<0);     /* 目的位于 AHB 总线, 目的地址递增 */
    dma_regs->dcon       = (1<<30)|(1<<29)|(0<<28)|(1<<27)|(0<<23)|(0<<20)|(BUF_SIZE<<0);  /* 使能中断,单个传输,软件触发, */
    
    /* 启动 DMA */
    dma_regs->dmasktrig  = (1<<1) | (1<<0);
    
    /* 如何知道 DMA 什么时候完成 ? */
    /* 休眠 */
    wait_event_interruptible(dma_waitq, ev_dma);
    
    if (memcmp(src, dst, BUF_SIZE) == 0)
    {
        printk("MEM_CPY_DMA OK\n");
    }
    else
    {
        printk("MEM_CPY_DMA ERROR\n");
    }

当 DMA 开始工作时会休眠一段时间,直到复制完成后触发中断来唤醒。

static irqreturn_t s3c_dma_irq(int irq, void *devid)
{
    /* 唤醒 */
    ev_dma = 1;
    wake_up_interruptible(&dma_waitq);   /* 唤醒休眠的进程 */
    return IRQ_HANDLED;
}

可能这样的实验并不会看出 DMA 的作用,我们可以与普通的复制做一下速度上的对比。例如用 memcpy 来与 DMA PK 一下速度就能看出效果来。

相关推荐