linux-device-driver之启用 DMA 的 UART Tx 模式
我已经为传输模式下的 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 寄存器 0x4a056000 的基地址,请求的 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 寄存器。但是观察到只有配置了这些寄存器才能工作
1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。