diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 6473361525d1f7..299f093b0f312c 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -49,6 +49,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 890fa7ddaa7f36..ecc86b4cf400fc 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -62,9 +62,12 @@ 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; int ret; + size_t chunk1, chunk2; + int head, tail; if (dma->tx_running) return 0; @@ -75,12 +78,28 @@ 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; - desc = dmaengine_prep_slave_single(dma->txchan, - dma->tx_addr + xmit->tail, - dma->tx_size, DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + desc = dmaengine_prep_slave_sg(dma->txchan, sgl, dma->dma_tx_nents, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { ret = -EBUSY; goto err; diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c index efa0515139f8ec..f79c07f0685a67 100644 --- a/drivers/tty/serial/8250/8250_mid.c +++ b/drivers/tty/serial/8250/8250_mid.c @@ -88,6 +88,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); @@ -232,6 +234,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; @@ -306,6 +331,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 46e2079ad1aa20..2fd407a62ef54a 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2404,6 +2404,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); } }