static unsigned int dma_handle_tx()

in serial/pch_uart.c [918:1036]


static unsigned int dma_handle_tx(struct eg20t_port *priv)
{
	struct uart_port *port = &priv->port;
	struct circ_buf *xmit = &port->state->xmit;
	struct scatterlist *sg;
	int nent;
	int fifo_size;
	struct dma_async_tx_descriptor *desc;
	int num;
	int i;
	int bytes;
	int size;
	int rem;

	if (!priv->start_tx) {
		dev_info(priv->port.dev, "%s:Tx isn't started. (%lu)\n",
			__func__, jiffies);
		pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT);
		priv->tx_empty = 1;
		return 0;
	}

	if (priv->tx_dma_use) {
		dev_dbg(priv->port.dev, "%s:Tx is not completed. (%lu)\n",
			__func__, jiffies);
		pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT);
		priv->tx_empty = 1;
		return 0;
	}

	fifo_size = max(priv->fifo_size, 1);
	if (pop_tx_x(priv, xmit->buf)) {
		pch_uart_hal_write(priv, xmit->buf, 1);
		port->icount.tx++;
		fifo_size--;
	}

	bytes = min((int)CIRC_CNT(xmit->head, xmit->tail,
			     UART_XMIT_SIZE), CIRC_CNT_TO_END(xmit->head,
			     xmit->tail, UART_XMIT_SIZE));
	if (!bytes) {
		dev_dbg(priv->port.dev, "%s 0 bytes return\n", __func__);
		pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT);
		uart_write_wakeup(port);
		return 0;
	}

	if (bytes > fifo_size) {
		num = bytes / fifo_size + 1;
		size = fifo_size;
		rem = bytes % fifo_size;
	} else {
		num = 1;
		size = bytes;
		rem = bytes;
	}

	dev_dbg(priv->port.dev, "%s num=%d size=%d rem=%d\n",
		__func__, num, size, rem);

	priv->tx_dma_use = 1;

	priv->sg_tx_p = kmalloc_array(num, sizeof(struct scatterlist), GFP_ATOMIC);
	if (!priv->sg_tx_p) {
		dev_err(priv->port.dev, "%s:kzalloc Failed\n", __func__);
		return 0;
	}

	sg_init_table(priv->sg_tx_p, num); /* Initialize SG table */
	sg = priv->sg_tx_p;

	for (i = 0; i < num; i++, sg++) {
		if (i == (num - 1))
			sg_set_page(sg, virt_to_page(xmit->buf),
				    rem, fifo_size * i);
		else
			sg_set_page(sg, virt_to_page(xmit->buf),
				    size, fifo_size * i);
	}

	sg = priv->sg_tx_p;
	nent = dma_map_sg(port->dev, sg, num, DMA_TO_DEVICE);
	if (!nent) {
		dev_err(priv->port.dev, "%s:dma_map_sg Failed\n", __func__);
		return 0;
	}
	priv->orig_nent = num;
	priv->nent = nent;

	for (i = 0; i < nent; i++, sg++) {
		sg->offset = (xmit->tail & (UART_XMIT_SIZE - 1)) +
			      fifo_size * i;
		sg_dma_address(sg) = (sg_dma_address(sg) &
				    ~(UART_XMIT_SIZE - 1)) + sg->offset;
		if (i == (nent - 1))
			sg_dma_len(sg) = rem;
		else
			sg_dma_len(sg) = size;
	}

	desc = dmaengine_prep_slave_sg(priv->chan_tx,
					priv->sg_tx_p, nent, DMA_MEM_TO_DEV,
					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
	if (!desc) {
		dev_err(priv->port.dev, "%s:dmaengine_prep_slave_sg Failed\n",
			__func__);
		return 0;
	}
	dma_sync_sg_for_device(port->dev, priv->sg_tx_p, nent, DMA_TO_DEVICE);
	priv->desc_tx = desc;
	desc->callback = pch_dma_tx_complete;
	desc->callback_param = priv;

	desc->tx_submit(desc);

	dma_async_issue_pending(priv->chan_tx);

	return PCH_UART_HANDLED_TX_INT;
}