diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 287153d325365a..b972510428e275 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -51,6 +51,9 @@ struct uart_8250_dma { unsigned char tx_running; unsigned char tx_err; unsigned char rx_running; + + unsigned int dma_tx_nents; + struct scatterlist tx_sgl[2]; }; struct old_serial_port { diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index a8dba4a0a8fb70..fa809bb983c635 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -62,10 +62,13 @@ static void __dma_rx_complete(void *param) int serial8250_tx_dma(struct uart_8250_port *p) { struct uart_8250_dma *dma = p->dma; + struct scatterlist *sgl = dma->tx_sgl; struct circ_buf *xmit = &p->port.state->xmit; struct dma_async_tx_descriptor *desc; struct uart_port *up = &p->port; int ret; + size_t chunk1, chunk2; + int head, tail; if (dma->tx_running) { if (up->x_char) { @@ -83,7 +86,24 @@ 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 = READ_ONCE(xmit->head); + tail = READ_ONCE(xmit->tail); + dma->dma_tx_nents = 1; + chunk1 = CIRC_CNT_TO_END(head, tail, UART_XMIT_SIZE); + chunk2 = CIRC_CNT(head, tail, UART_XMIT_SIZE) - chunk1; + if (chunk2 == 0) { + sg_init_one(sgl, xmit->buf + tail, chunk1); + } else { + dma->dma_tx_nents++; + sg_init_table(sgl, dma->dma_tx_nents); + sg_set_buf(&sgl[0], xmit->buf + tail, chunk1); + sg_set_buf(&sgl[1], xmit->buf, chunk2); + sg_dma_address(&sgl[1]) = dma->tx_addr; + sg_dma_len(&sgl[1]) = chunk2; + } + sg_dma_address(&sgl[0]) = dma->tx_addr + tail; + sg_dma_len(&sgl[0]) = chunk1; + dma->tx_size = chunk1 + chunk2; serial8250_do_prepare_tx_dma(p); diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c index a2a03acb04ad3e..494bf6399a0c90 100644 --- a/drivers/tty/serial/8250/8250_mid.c +++ b/drivers/tty/serial/8250/8250_mid.c @@ -93,6 +93,8 @@ static int tng_handle_irq(struct uart_port *p) err = hsu_dma_get_status(chip, mid->dma_index * 2 + 1, &status); if (err > 0) { serial8250_rx_dma_flush(up); + /* Immediately after flushing arm DMA again */ + if (up->dma) up->dma->rx_dma(up); ret |= 1; } else if (err == 0) ret |= hsu_dma_do_irq(chip, mid->dma_index * 2 + 1, status); @@ -242,6 +244,29 @@ static void mid8250_set_termios(struct uart_port *p, serial8250_do_set_termios(p, termios, old); } +static int mid8250_startup(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + int ret; + + ret = serial8250_do_startup(port); + + /* Arm Rx DMA at ->startup() time */ + + if (up->dma) up->dma->rx_dma(up); + + return ret; +} + +static void mid8250_shutdown(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + if (up->dma) serial8250_rx_dma_flush(up); + + serial8250_do_shutdown(port); +} + static bool mid8250_dma_filter(struct dma_chan *chan, void *param) { struct hsu_dma_slave *s = param; @@ -316,6 +341,8 @@ static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) uart.port.uartclk = mid->board->base_baud * 16; uart.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE; uart.port.set_termios = mid8250_set_termios; + uart.port.startup = mid8250_startup; + uart.port.shutdown = mid8250_shutdown; uart.port.mapbase = pci_resource_start(pdev, bar); uart.port.membase = pcim_iomap(pdev, bar, 0); diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 39b35a61958c04..6ea56231f070cd 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2418,6 +2418,13 @@ int serial8250_do_startup(struct uart_port *port) if (msg) { dev_warn_ratelimited(port->dev, "%s\n", msg); up->dma = NULL; + } else { + /* + * change tty_io write() to not split + * large writes into 2K chunks + */ + set_bit(TTY_NO_WRITE_SPLIT, + &port->state->port.tty->flags); } }