Search code examples
linux-device-driverembedded-linuxuartdma

UART Tx mode with DMA enabled


I have written a simple device driver for UART in transmission mode with DMA enabled along with interrupt. The hardware which I am using is omap 4460 pandaboard with Linux 3.4 loaded in it.

Below I am sharing the relevant parts of the code. In the open phase:

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

where UART4_DMA_REG is the base address of DMA registers 0x4a056000 and requested irq is 45 which line 1 of sDMA interrupt. After this UART registers are initialised and DMA is enabled. Now the user calls the write function to copy 100 bytes of data to buffer in kernel space.

The code below shows the write function:

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(); This function initializes the DMA registers and enables the channel in the Software Trigger mode.

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

Now the question is: As soon as the channel is enabled (just after the call dma_init()), ISR(handler) is called and enters into infinite loop. What should my ISR contain in write mode?


Solution

  • I have sorted out the issues after so many hit and trial. Firstly, since this is a character device, the DMA transfer has to take place 1 element per DMA request. I had configured it to transfer 1 Block per DMA request earlier. Secondly, As per the programming guide for element transfer, it is not necessary to configure DMA4_CEN and DMA4_CFN registers. But it is observed to be working only if these registers are configured