linux-device-driver之启用 DMA 的 UART Tx 模式

zdz8207 阅读:26 2025-02-15 21:57:57 评论:0

我已经为传输模式下的 UART 编写了一个简单的设备驱动程序,启用了 DMA 和中断。 我使用的硬件是装有 Linux 3.4 的 omap 4460 pandaboard。

下面我分享代码的相关部分。 在开放阶段:

    dma_map = ioremap(UART4_DMA_REG,0x1350); 
    if(dma_map == NULL) { 
        printk(KERN_INFO " unable to io_remap DMA region\n"); 
        return -ENOMEM; 
    }    
 
    printk(KERN_INFO "DMA mapping successful\n"); 
 
    irq_val = request_irq(45,uart_handler,IRQF_DISABLED,"uart_int",NULL); 
    if(irq_val) { 
        printk(KERN_INFO "cannot assign the requested irq\n"); 
        return -1; 
    } 
    else { 
        printk(KERN_INFO "Requested irq successful\n");  
    } 

其中 UART4_DMA_REG 是 DMA 寄存器 0x​​4a056000 的基地址,请求的 irq 是 45,sDMA 中断的第 1 行。 在初始化 UART 寄存器并启用 DMA 之后。 现在用户调用write函数将100字节的数据复制到内核空间的buffer中。

下面的代码显示了写函数:

ssize_t uart_write(struct file *filp,const char __user *buff, size_t count, loff_t *offp) 
{ 
    int no_of_bytes; 
    int maxbytes; 
    struct device *udevice = &devi; 
    int ret_mask; 
 
    char *kbuf = kmalloc(100,GFP_KERNEL|GFP_DMA);  
    maxbytes = BUFF_SIZE - *offp; 
    if(count > maxbytes)//overflow of buffer 
        no_of_bytes = maxbytes; 
    else 
        no_of_bytes = count;     
    if(no_of_bytes == 0) 
        printk(KERN_INFO "Nothing is there to write to device\n"); 
 
    bytes_written = no_of_bytes - copy_from_user(kbuf,buff,no_of_bytes);//copy_from_user()returns remaining bytes. 
    printk(KERN_INFO "Write Completed\n"); 
    Uindex = 0; 
    *offp += bytes_written; 
 
    ret_mask = dma_set_coherent_mask(udevice,DMA_BIT_MASK(32)); 
    if(!ret_mask) 
        printk(KERN_INFO "set mask success \n"); 
    else 
        printk(KERN_INFO "SET MASK NOT SUCCESS \n"); 
 
    bus_addr = dma_map_single(udevice,kbuf,size,DMA_TO_DEVICE);  
    printk(KERN_INFO "dma_map_single completed"); 
    dma_init();  
    return bytes_written; 
} 

dma_init(); 此函数初始化 DMA 寄存器并在软件触发模式下启用 channel 。

void dma_init() 
{ 
    unsigned int ccr_val; 
    unsigned int csdp_val; 
    irq_line = 1; //for tx line 1 is considered 
    dma_cha_line = 0; //for tx line 0 is considered 
 
    /* Interrupt Enabled in DMA4_IRQENABLE_Lj and DMA4_CICRi registers */        
    iowrite32(0x1,(dma_map + 0x0018 + (4 * irq_line)));//to unmask the interrupt DMA4_IRQENABLE_Lj   
    iowrite32(0x8,(dma_map + 0x0088 + (0x60 * dma_cha_line)));//condition to generate interrupt CICR reg 
 
    /* Set the Read Port & Write Port access in CSDP */ 
    csdp_val = ioread32(dma_map + 0x0090 + (0x60 * dma_cha_line)); 
    csdp_val &= ~(0x3 << 7);//Source  
    csdp_val &= ~(0x3 << 14);//Destination 
    csdp_val &= ~(0x3 << 16);//Writing mode without posted 
    csdp_val &= ~(0x1 << 21);//little endian source 
    csdp_val &= ~(0x1 << 19);//little endian destination 
    csdp_val &= ~(0x1 << 13);//destination not packed 
    csdp_val &= ~(0x1 << 6);//source not packed 
    csdp_val &= ~(0x3);//ES is set to 8 bits     
    iowrite32(csdp_val,(dma_map + 0x0090 + (0x60 * dma_cha_line))); 
 
    /* CEN register configuration */ 
    iowrite32(100,(dma_map + 0x0094 +(0x60 * dma_cha_line)));//EN is set to 1    
 
    /* CFN register configuration */ 
    iowrite32(1,(dma_map + 0x0098 +(0x60 * dma_cha_line)));//FN is set to 1  
 
    /* Set the Channel Source & Destination start address */ 
    iowrite32(bus_addr,(dma_map + 0x009C + (0x60 * dma_cha_line)));//Source 
    iowrite32(io_map,(dma_map + 0x00a0 + (0x60 * dma_cha_line)));//Destination 
 
    /* CCR configuration */  
    ccr_val = ioread32(dma_map + 0x0080 + (0x60 * dma_cha_line));        
    /* Set the Read Port & Write Port addressing mode in CCR */ 
    /* 
    ccr_val &= ~(0x3 << 12);//Source - constant address mode  
    ccr_val |= (0x1 << 14);//Destination - post incremented address mode-set 14th bit and clear 15th bit 
    ccr_val &= ~(0x1 << 15);     
    */ 
    ccr_val |= (0x1 << 12);//source - post incremented address mode-set 12th bit and clear 13th bit 
    ccr_val &= ~(0x1 << 13);     
    ccr_val &= ~(0x3 << 14);//destination- constant address mode - clear 14 and 15th bit  
    ccr_val |=  (0x1 << 26);//high priority on write 
    ccr_val &=  ~(0x1 << 6);//low priority on read  
    ccr_val &= ~(0x1f);//CCR[4:0] 
    ccr_val &=  ~(0x3 << 19);//CCR [19:20] to 0 
    ccr_val |= (0x1 << 7);// Set the channel enable bit in CCR  
    iowrite32(ccr_val,(dma_map + 0x0080 + (0x60 * dma_cha_line))); 
 
    /*CSEI,CSFI,CDEI,CDFI*/ 
    iowrite32(1,(dma_map + 0x00a4 +(0x60 * dma_cha_line))); 
    iowrite32(1,(dma_map + 0x00a8 +(0x60 * dma_cha_line))); 
    iowrite32(1,(dma_map + 0x00ac +(0x60 * dma_cha_line))); 
    iowrite32(1,(dma_map + 0x00b0 +(0x60 * dma_cha_line)));  
 
 
    printk(KERN_INFO "DMA registers configured\n"); 
} 

现在的问题是:一旦启用 channel (就在调用 dma_init() 之后),ISR(handler) 就会被调用并进入无限循环。我的 ISR 在写模式下应该包含什么?

请您参考如下方法:

经过这么多次的打击和试用,我已经解决了这些问题。首先,由于这是一个字符设备,DMA 传输必须在每个 DMA 请求中进行 1 个元素。我已将其配置为之前每个 DMA 请求传输 1 个 block 。其次,根据元素传输编程指南,没有必要配置 DMA4_CEN 和 DMA4_CFN 寄存器。但是观察到只有配置了这些寄存器才能工作


标签:linux
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

关注我们

一个IT知识分享的公众号