Commit f58c252e authored by Ilpo Järvinen's avatar Ilpo Järvinen Committed by Greg Kroah-Hartman
Browse files

serial: 8250: fix XOFF/XON sending when DMA is used



When 8250 UART is using DMA, x_char (XON/XOFF) is never sent
to the wire. After this change, x_char is injected correctly.

Create uart_xchar_out() helper for sending the x_char out and
accounting related to it. It seems that almost every driver
does these same steps with x_char. Except for 8250, however,
almost all currently lack .serial_out so they cannot immediately
take advantage of this new helper.

The downside of this patch is that it might reintroduce
the problems some devices faced with mixed DMA/non-DMA transfer
which caused revert f967fc8f (Revert "serial: 8250_dma:
don't bother DMA with small transfers"). However, the impact
should be limited to cases with XON/XOFF (that didn't work
with DMA capable devices to begin with so this problem is not
very likely to cause a major issue, if any at all).

Fixes: 9ee4b83e ("serial: 8250: Add support for dmaengine")
Reported-by: default avatarGilles Buloz <gilles.buloz@kontron.com>
Tested-by: default avatarGilles Buloz <gilles.buloz@kontron.com>
Reviewed-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: default avatarIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://lore.kernel.org/r/20220314091432.4288-2-ilpo.jarvinen@linux.intel.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 1db536f9
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -64,10 +64,19 @@ int serial8250_tx_dma(struct uart_8250_port *p)
	struct uart_8250_dma		*dma = p->dma;
	struct circ_buf			*xmit = &p->port.state->xmit;
	struct dma_async_tx_descriptor	*desc;
	struct uart_port		*up = &p->port;
	int ret;

	if (dma->tx_running)
	if (dma->tx_running) {
		if (up->x_char) {
			dmaengine_pause(dma->txchan);
			uart_xchar_out(up, UART_TX);
			dmaengine_resume(dma->txchan);
		}
		return 0;
	} else if (up->x_char) {
		uart_xchar_out(up, UART_TX);
	}

	if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) {
		/* We have been called from __dma_tx_complete() */
+1 −3
Original line number Diff line number Diff line
@@ -1822,9 +1822,7 @@ void serial8250_tx_chars(struct uart_8250_port *up)
	int count;

	if (port->x_char) {
		serial_out(up, UART_TX, port->x_char);
		port->icount.tx++;
		port->x_char = 0;
		uart_xchar_out(port, UART_TX);
		return;
	}
	if (uart_tx_stopped(port)) {
+14 −0
Original line number Diff line number Diff line
@@ -641,6 +641,20 @@ static void uart_flush_buffer(struct tty_struct *tty)
	tty_port_tty_wakeup(&state->port);
}

/*
 * This function performs low-level write of high-priority XON/XOFF
 * character and accounting for it.
 *
 * Requires uart_port to implement .serial_out().
 */
void uart_xchar_out(struct uart_port *uport, int offset)
{
	serial_port_out(uport, offset, uport->x_char);
	uport->icount.tx++;
	uport->x_char = 0;
}
EXPORT_SYMBOL_GPL(uart_xchar_out);

/*
 * This function is used to send a high-priority XON/XOFF character to
 * the device
+2 −0
Original line number Diff line number Diff line
@@ -458,6 +458,8 @@ extern void uart_handle_cts_change(struct uart_port *uport,
extern void uart_insert_char(struct uart_port *port, unsigned int status,
		 unsigned int overrun, unsigned int ch, unsigned int flag);

void uart_xchar_out(struct uart_port *uport, int offset);

#ifdef CONFIG_MAGIC_SYSRQ_SERIAL
#define SYSRQ_TIMEOUT	(HZ * 5)