启用DMA的UART Tx模式

3

我已经编写了一个简单的设备驱动程序,用于启用DMA并启用中断的UART传输模式。

我使用的硬件是加载了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寄存器的基地址0x4a056000,请求的中断号为45,即sDMA中断的第1行。 初始化UART寄存器并启用DMA后,用户现在调用写函数将100字节的数据复制到内核空间中的缓冲区。

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

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寄存器并在软件触发模式下启用通道。

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");
}

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

你能添加你的ISR代码吗?另外,你说通道在dma_init()调用后就启用了,但是dma_init()之后只有一个返回。 - skrrgwasme
@ScottLawson:感谢您的回复。但我已经解决了这些问题。首先,由于这是一个字符设备,DMA传输必须每个DMA请求传输1个元素。我之前配置它每个DMA请求传输1个块。其次,根据元素传输的编程指南,不需要配置DMA4_CEN和DMA4_CFN寄存器。但只有在配置这些寄存器时才能正常工作。 - ddpd
太棒了!你应该将这个作为答案发布并接受它。在SO上回答自己的问题是被鼓励的。 - skrrgwasme
1个回答

4

经过多次尝试和排除,我已经解决了问题。首先,由于这是一个字符设备,DMA传输必须每个DMA请求传输1个元素。我之前配置的是每个DMA请求传输1个块。其次,根据元素传输的编程指南,不需要配置DMA4_CEN和DMA4_CFN寄存器。但是只有在配置了这些寄存器后才能正常工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接