[PATCH v2 2/4] serial: sh-sci: Keep TX enabled on TEI while data is pending
From: Biju Das
Date: Wed Oct 21 2026 - 09:18:45 EST
From: Biju Das <biju.das.jz@xxxxxxxxxxxxxx>
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 <biju.das.jz@xxxxxxxxxxxxxx>
---
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