uart_pxa_dma_init








	if (up->dma_enable) {
		uart_pxa_dma_init(up);
		up->rx_stop = 0;
		pxa_uart_receive_dma_start(up);
		up->ier = UART_IER_DMAE | UART_IER_UUE | UART_IER_RTOIE;
		tasklet_init(&up->tklet, uart_task_action, (unsigned long)up);
	}



static void uart_pxa_dma_init(struct uart_pxa_port *up)
{

	if (0 == up->rxdma) {
		up->rxdma = pxa_request_dma(up->name, DMA_PRIO_LOW,
				pxa_uart_receive_dma, up);
		if (up->rxdma < 0)  // 分配 rx channel 号
			goto out;
	}

	if (0 == up->txdma) {
		up->txdma = pxa_request_dma(up->name, DMA_PRIO_LOW,
				pxa_uart_transmit_dma, up);  // 分配 tx channel 号
		if (up->txdma < 0)
			goto err_txdma;
	}

	if (NULL == up->txdma_addr) {
		up->txdma_addr = dma_alloc_coherent(NULL, DMA_BLOCK,
				&up->txdma_addr_phys, GFP_KERNEL); // dma分配 得到虚拟地址和物理地址
		if (!up->txdma_addr)
			goto txdma_err_alloc;
	}

	if (NULL == up->rxdma_addr) {
		up->rxdma_addr = dma_alloc_coherent(NULL, DMA_BLOCK,
				&up->rxdma_addr_phys, GFP_KERNEL);
		if (!up->rxdma_addr)
			goto rxdma_err_alloc;
	}

#ifdef CONFIG_PM
	up->buf_save = kmalloc(DMA_BLOCK, GFP_KERNEL);
	if (!up->buf_save)
		goto buf_err_alloc;
#endif

	writel(up->rxdma | DRCMR_MAPVLD, up->rxdrcmr);  // 告诉dma channel已经映射
	writel(up->txdma | DRCMR_MAPVLD, up->txdrcmr);

	return;

#ifdef CONFIG_PM
buf_err_alloc:
	dma_free_coherent(NULL, DMA_BLOCK, up->rxdma_addr,
			up->rxdma_addr_phys);
	up->rxdma_addr = NULL;
#endif
rxdma_err_alloc:
	dma_free_coherent(NULL, DMA_BLOCK, up->txdma_addr,
			up->txdma_addr_phys);
	up->txdma_addr = NULL;
txdma_err_alloc:
	pxa_free_dma(up->txdma);
	up->txdma = 0;
err_txdma:
	pxa_free_dma(up->rxdma);
	up->rxdma = 0;
out:
	return;
}


下面到pxa_uart_receive_dma_start这个函数:

static void pxa_uart_receive_dma_start(struct uart_pxa_port *up)
{
	DCSR(up->rxdma)  = DCSR_NODESC;  // 采用没有描述符的方式
	DSADR(up->rxdma) = up->port.mapbase;  // source
	DTADR(up->rxdma) = up->rxdma_addr_phys;  // target
	DCMD(up->rxdma) = DCMD_INCTRGADDR | DCMD_FLOWSRC | DCMD_ENDIRQEN |
		DCMD_WIDTH1 | DCMD_BURST16 | DMA_BLOCK;
	DCSR(up->rxdma) |= DCSR_RUN;
}

DMA Command Registers (DMA_CMDX)这个寄存器设置为: target addr 自动加1, 流控根据source, 当len为0时产生中断, 宽度为1byte, burst为16bytes, 块为4096

最后一个DCSR就是让dma run起来了,寄存器为DMA Channel Control/Status Registers, up-> rxdma 传递了通道号,通道号在plat-pxa/dma.c文件中pxa_request_dma申请,实际上就对应到了具体的0-15的某个寄存器,下面dma就run起来了。

        然后开始初始化tasklet,来在中断上下文中断触发后马上执行uart_task_action函数。

        然后继续写uart寄存器使能uart dma,使能uart,使能接受超时中断,然后在后面会再次清除中断相关寄存器,这样serial_pxa_startup就完成了。

        下面我们重点看看uart_task_action函数,此函数最后会调用pxa_uart_transmit_dma_start这个函数,此函数的实现如下:

static void pxa_uart_transmit_dma_start(struct uart_pxa_port *up, int count)
{
	unsigned long flags;

	if (!(DCSR(up->txdma) & DCSR_STOPSTATE))
		return;

	DCSR(up->txdma)  = DCSR_NODESC;
	DSADR(up->txdma) = up->txdma_addr_phys;
	DTADR(up->txdma) = up->port.mapbase;
	DCMD(up->txdma) = DCMD_INCSRCADDR | DCMD_FLOWTRG | DCMD_ENDIRQEN |
		DCMD_WIDTH1 | DCMD_BURST16 | count;

	local_irq_save(flags);

#ifdef CONFIG_PXA95x
	dvfm_disable_lowpower(up->dvfm_dev_idx[PXA_UART_TX]);
#elif defined(CONFIG_WAKELOCK) && defined(CONFIG_CPU_PXA910)
	wake_lock(&up->idle_lock[PXA_UART_TX]);
#else
	pm_qos_update_request(&up->qos_idle[PXA_UART_TX],
					PM_QOS_CONSTRAINT);
#endif

	DCSR(up->txdma) |= DCSR_RUN;

	local_irq_restore(flags);
}

这个地方主要的不同就是source和target不同,DCMD这块是inc src addr和target流控,其他设置是一样的。这里要注意的是流控始终都是uart本身作为流控,从而避免uart慢引起的问题。

下面我们再看看serial_pxa_start_tx函数,这个函数同样也是判断了dma是否使能了,如果使能了将使用dma的方式,同样也是调用了tasklet_schedule函数来触发uart_task_action此函数的调用,从而再调用pxa_uart_transmit_dma_start这个函数。

static void serial_pxa_start_tx(struct uart_port *port)
{
	struct uart_pxa_port *up = (struct uart_pxa_port *)port;

	if (up->dma_enable) {
		up->tx_stop = 0;
		tasklet_schedule(&up->tklet);
	} else {
		if (!(up->ier & UART_IER_THRI)) {
			up->ier |= UART_IER_THRI;
			serial_out(up, UART_IER, up->ier);
		}
	}
}

uart_task_action这个函数的实现如下:

static void uart_task_action(unsigned long data)
{
	struct uart_pxa_port *up = (struct uart_pxa_port *)data;
	struct circ_buf *xmit = &up->port.state->xmit;
	unsigned char *tmp = up->txdma_addr;
	unsigned long flags;
	int count = 0, c;

	/* if the tx is stop, just return.*/
	if (up->tx_stop)
		return;

	if ((DCSR(up->txdma) & DCSR_RUN))
		return;

	spin_lock_irqsave(&up->port.lock, flags);
	while (1) {
		c = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
		if (c <= 0)
			break;

		memcpy(tmp, xmit->buf + xmit->tail, c);
		xmit->tail = (xmit->tail + c) & (UART_XMIT_SIZE - 1);
		tmp += c;
		count += c;
		up->port.icount.tx += c;
	}
	spin_unlock_irqrestore(&up->port.lock, flags);

	tmp = up->txdma_addr;
	up->tx_stop = 0;

	pr_debug("count =%d", count);
	pxa_uart_transmit_dma_start(up, count);
}

此函数的重要作用就是把buf填充到DMA所分配的虚拟地址up->txdma_addr中,最大为4096bytes,当while(1)把字节都加进来后,然后就开始dma操作了。而在dma中我们将不是使用up->txdma_addr这个虚拟地址,而直接使用物理地址up->txdma_addr_phys来作为源地址,然后让这个地址自动+1,从而来发送这块区域的buf,直到count到0后再停止。


你可能感兴趣的:(struct,command,null,UP,action,byte)