diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 52bb21205bb682..5ab8b05e87ac28 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -41,6 +41,7 @@ struct uart_8250_dma { dma_cookie_t tx_cookie; void *rx_buf; + void *tx_buf; size_t rx_size; size_t tx_size; diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index 890fa7ddaa7f36..5988fc10b6c520 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "8250.h" @@ -65,6 +66,9 @@ int serial8250_tx_dma(struct uart_8250_port *p) struct circ_buf *xmit = &p->port.state->xmit; struct dma_async_tx_descriptor *desc; int ret; + size_t chunk1; + size_t chunk2; + int head; if (dma->tx_running) return 0; @@ -75,10 +79,18 @@ int serial8250_tx_dma(struct uart_8250_port *p) return 0; } - dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + head = xmit->head; + chunk1 = CIRC_CNT_TO_END(head, xmit->tail, UART_XMIT_SIZE); + memcpy(dma->tx_buf, xmit->buf + xmit->tail, chunk1); + + chunk2 = CIRC_CNT(head, xmit->tail, UART_XMIT_SIZE) - chunk1; + if (chunk2 > 0) { + memcpy(dma->tx_buf + chunk1, xmit->buf, chunk2); + } + dma->tx_size = chunk1 + chunk2; desc = dmaengine_prep_slave_single(dma->txchan, - dma->tx_addr + xmit->tail, + dma->tx_addr, dma->tx_size, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { @@ -217,20 +229,27 @@ int serial8250_request_dma(struct uart_8250_port *p) } /* TX buffer */ + dma->tx_buf = kmalloc(UART_XMIT_SIZE, GFP_KERNEL); + if (!dma->tx_buf) { + ret = -ENOMEM; + goto free_coherent_rx; + } + dma->tx_addr = dma_map_single(dma->txchan->device->dev, - p->port.state->xmit.buf, + dma->tx_buf, UART_XMIT_SIZE, DMA_TO_DEVICE); if (dma_mapping_error(dma->txchan->device->dev, dma->tx_addr)) { - dma_free_coherent(dma->rxchan->device->dev, dma->rx_size, - dma->rx_buf, dma->rx_addr); ret = -ENOMEM; - goto err; + goto free_coherent_rx; } dev_dbg_ratelimited(p->port.dev, "got both dma channels\n"); return 0; +free_coherent_rx: + dma_free_coherent(dma->rxchan->device->dev, dma->rx_size, + dma->rx_buf, dma->rx_addr); err: dma_release_channel(dma->txchan); release_rx: @@ -257,6 +276,8 @@ void serial8250_release_dma(struct uart_8250_port *p) dmaengine_terminate_sync(dma->txchan); dma_unmap_single(dma->txchan->device->dev, dma->tx_addr, UART_XMIT_SIZE, DMA_TO_DEVICE); + kfree(dma->tx_buf); + dma->tx_buf = NULL; dma_release_channel(dma->txchan); dma->txchan = NULL; dma->tx_running = 0;