From: Biju Das sci_tx_end_interrupt() handles the transmit-end interrupt (TEI) on SCI and RSCI ports by clearing TE and TEIE, which turns the transmitter off. It does this unconditionally, even if the xmit kfifo was refilled, or an x_char was queued, after the last byte was written to the data register and before TEI fired. The pending data is then not sent until the transmitter is re-enabled, so output stalls or is lost. Check for pending data (x_char, or a non-empty xmit kfifo on a port that is not stopped) before shutting the transmitter down. If there is any, clear TEIE, set TIE and hand over to sci_tx_interrupt() so transmission continues normally. The existing TE/TEIE teardown path only runs once nothing is left to send. To keep the handler independent of the register layout, add tie and teie masks to struct sci_port_params_bits and populate them for the SCI, SCIF and RSCI variants. Fixes: d61ae331d6f3 ("tty: serial: sh-sci: Add support for tx end interrupt handling") Signed-off-by: Biju Das --- v1->v2: * Updated commit header and description. * Take the port lock before reading the control register, so the read-modify-write in sci_tx_end_interrupt() is protected. * Extended the pending-data check in sci_tx_end_interrupt() to cover x_char and a stopped port, not only a non-empty xmit kfifo. --- drivers/tty/serial/rsci.c | 2 ++ drivers/tty/serial/sh-sci-common.h | 2 ++ drivers/tty/serial/sh-sci.c | 22 ++++++++++++++++++---- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/rsci.c b/drivers/tty/serial/rsci.c index 40db9daa4272..52638db7bbeb 100644 --- a/drivers/tty/serial/rsci.c +++ b/drivers/tty/serial/rsci.c @@ -627,6 +627,8 @@ static const struct sci_port_params_bits rsci_port_param_bits = { .rxtx_enable = CCR0_RE | CCR0_TE, .te_clear = CCR0_TE | CCR0_TEIE, .poll_sent_bits = CSR_TDRE | CSR_TEND, + .tie = CCR0_TIE, + .teie = CCR0_TEIE, }; static const struct sci_port_params rsci_rzg3e_port_params = { diff --git a/drivers/tty/serial/sh-sci-common.h b/drivers/tty/serial/sh-sci-common.h index 01ff9fced803..273856fbc20b 100644 --- a/drivers/tty/serial/sh-sci-common.h +++ b/drivers/tty/serial/sh-sci-common.h @@ -56,6 +56,8 @@ struct sci_port_params_bits { unsigned int rxtx_enable; unsigned int te_clear; unsigned int poll_sent_bits; + unsigned int tie; + unsigned int teie; }; struct sci_common_regs { diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 9eb297dbc901..58120b523ca1 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -261,13 +261,17 @@ static bool sci_uart_earlycon_dev_probing; static const struct sci_port_params_bits sci_sci_port_params_bits = { .rxtx_enable = SCSCR_RE | SCSCR_TE, .te_clear = SCSCR_TE | SCSCR_TEIE, - .poll_sent_bits = SCI_TDRE | SCI_TEND + .poll_sent_bits = SCI_TDRE | SCI_TEND, + .tie = SCSCR_TIE, + .teie = SCSCR_TEIE }; static const struct sci_port_params_bits sci_scif_port_params_bits = { .rxtx_enable = SCSCR_RE | SCSCR_TE, .te_clear = SCSCR_TE | SCSCR_TEIE, - .poll_sent_bits = SCIF_TDFE | SCIF_TEND + .poll_sent_bits = SCIF_TDFE | SCIF_TEND, + .tie = SCSCR_TIE, + .teie = SCSCR_TEIE }; static const struct sci_common_regs sci_common_regs = { @@ -2012,6 +2016,7 @@ static irqreturn_t sci_tx_end_interrupt(int irq, void *ptr) { struct uart_port *port = ptr; struct sci_port *s = to_sci_port(port); + struct tty_port *tport = &port->state->port; const struct sci_common_regs *regs = s->params->common_regs; unsigned long flags; u32 ctrl; @@ -2020,8 +2025,17 @@ static irqreturn_t sci_tx_end_interrupt(int irq, void *ptr) return sci_tx_interrupt(irq, ptr); uart_port_lock_irqsave(port, &flags); - ctrl = s->ops->read_reg(port, regs->control) & - ~(s->params->param_bits->te_clear); + ctrl = s->ops->read_reg(port, regs->control); + if (port->x_char || (!kfifo_is_empty(&tport->xmit_fifo) && + !uart_tx_stopped(port))) { + ctrl &= ~(s->params->param_bits->teie); + ctrl |= s->params->param_bits->tie; + s->ops->write_reg(port, regs->control, ctrl); + uart_port_unlock_irqrestore(port, flags); + return sci_tx_interrupt(irq, ptr); + } + + ctrl &= ~(s->params->param_bits->te_clear); s->ops->write_reg(port, regs->control, ctrl); uart_port_unlock_irqrestore(port, flags); -- 2.43.0