S3C2440 DMA 驱动示例

发布时间: 2024-07-23
来源: 电子工程世界

将 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 OKn");

    }

    else

    {

    printk("MEM_CPY_DMA ERRORn");

    }

当 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 一下速度就能看出效果来。


文章来源于: 电子工程世界 原文链接

本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。