From: Biju Das <biju.das.au@gmail.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Jiri Slaby <jirislaby@kernel.org>,
Kiryl Shutsemau <kas@kernel.org>,
Rick Edgecombe <rick.p.edgecombe@intel.com>,
Dave Hansen <dave.hansen@linux.intel.com>
Cc: linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org,
linux-renesas-soc@vger.kernel.org, x86@kernel.org,
linux-coco@lists.linux.dev, kvm@vger.kernel.org,
Biju Das <biju.das.jz@bp.renesas.com>
Subject: [PATCH v3 2/4] serial: sh-sci: Keep TX enabled on TEI while data is pending
Date: Mon, 26 Oct 2026 11:23:34 +0000 [thread overview]
Message-ID: <20261026-g2ul_sci-v3-2-b3097163edd5@bp.renesas.com> (raw)
In-Reply-To: <20261026-g2ul_sci-v3-0-b3097163edd5@bp.renesas.com>
From: Biju Das <biju.das.jz@bp.renesas.com>
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 the port's transmit_chars()
op so transmission continues normally. The existing TE/TEIE teardown
path only runs once nothing is left to send.
When the port is stopped with data still queued, the transmit_chars()
paths (sci_transmit_chars() and rsci_transmit_chars()) now also swap
TIE for TEIE, as they already did for an empty kfifo. Without this, TIE
would keep firing with nothing the driver is allowed to send, and TEI
would never be raised to complete the transmission.
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@bp.renesas.com>
---
v2->v3:
* Updated commit description
* When the port is stopped with data still queued, the transmit_chars()
paths (sci_transmit_chars() and rsci_transmit_chars()) now also swap
TIE for TEIE, as they already did for an empty kfifo. Without this, TIE
would keep firing with nothing the driver is allowed to send, and TEI
would never be raised to complete the transmission.
* Replaced sci_tx_interrupt() with port's transmit_chars() to avoid
lock drop race in sci_tx_end_interrupt() before TX handoff.
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 | 4 +++-
drivers/tty/serial/sh-sci-common.h | 2 ++
drivers/tty/serial/sh-sci.c | 47 +++++++++++++++++++++++++++++---------
3 files changed, 41 insertions(+), 12 deletions(-)
diff --git a/drivers/tty/serial/rsci.c b/drivers/tty/serial/rsci.c
index 40db9daa4272..0391111db8ae 100644
--- a/drivers/tty/serial/rsci.c
+++ b/drivers/tty/serial/rsci.c
@@ -465,7 +465,7 @@ static void rsci_transmit_chars(struct uart_port *port)
if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);
- if (kfifo_is_empty(&tport->xmit_fifo)) {
+ if (kfifo_is_empty(&tport->xmit_fifo) || stopped) {
ctrl = rsci_serial_in(port, CCR0);
ctrl &= ~CCR0_TIE;
ctrl |= CCR0_TEIE;
@@ -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..6df25fef5a5f 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 = {
@@ -1007,6 +1011,20 @@ static int sci_rxfill(struct uart_port *port)
* the interrupt related routines *
* ********************************************************************** */
+static void sci_clr_tie_set_teie(struct uart_port *port)
+{
+ struct sci_port *s = to_sci_port(port);
+ unsigned short ctrl;
+
+ if (s->type != PORT_SCI)
+ return;
+
+ ctrl = sci_serial_in(port, SCSCR);
+ ctrl &= ~SCSCR_TIE;
+ ctrl |= SCSCR_TEIE;
+ sci_serial_out(port, SCSCR, ctrl);
+}
+
static void sci_transmit_chars(struct uart_port *port)
{
struct tty_port *tport = &port->state->port;
@@ -1050,14 +1068,10 @@ static void sci_transmit_chars(struct uart_port *port)
if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);
if (kfifo_is_empty(&tport->xmit_fifo)) {
- if (s->type == PORT_SCI) {
- ctrl = sci_serial_in(port, SCSCR);
- ctrl &= ~SCSCR_TIE;
- ctrl |= SCSCR_TEIE;
- sci_serial_out(port, SCSCR, ctrl);
- }
-
+ sci_clr_tie_set_teie(port);
sci_stop_tx(port);
+ } else if (stopped) {
+ sci_clr_tie_set_teie(port);
}
}
@@ -2012,6 +2026,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 +2035,18 @@ 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);
+ s->ops->transmit_chars(port);
+ uart_port_unlock_irqrestore(port, flags);
+ return IRQ_HANDLED;
+ }
+
+ ctrl &= ~(s->params->param_bits->te_clear);
s->ops->write_reg(port, regs->control, ctrl);
uart_port_unlock_irqrestore(port, flags);
--
2.43.0
next prev parent reply other threads:[~2026-10-26 11:23 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-26 11:23 [PATCH v3 0/4] Fix transmit handling on SCI/RSCI ports Biju Das
2026-10-26 11:23 ` [PATCH v3 1/4] serial: sh-sci: Drop TE disable at end of transmission on SCI Biju Das
2026-10-26 11:23 ` Biju Das [this message]
2026-10-26 11:23 ` [PATCH v3 3/4] serial: sh-sci: Skip TDxE clear when hardware clears it automatically Biju Das
2026-10-26 11:23 ` [PATCH v3 4/4] serial: sh-sci: Toggle TE when restoring SCSCR in console write on SCI Biju Das
2026-10-27 5:57 ` [PATCH v3 0/4] Fix transmit handling on SCI/RSCI ports Borislav Petkov
2026-10-27 7:19 ` Biju Das
2026-10-27 7:23 ` Krzysztof Kozlowski
2026-10-27 8:33 ` Biju Das
2026-10-27 9:34 ` Krzysztof Kozlowski
2026-10-27 20:54 ` Borislav Petkov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261026-g2ul_sci-v3-2-b3097163edd5@bp.renesas.com \
--to=biju.das.au@gmail.com \
--cc=biju.das.jz@bp.renesas.com \
--cc=dave.hansen@linux.intel.com \
--cc=gregkh@linuxfoundation.org \
--cc=jirislaby@kernel.org \
--cc=kas@kernel.org \
--cc=kvm@vger.kernel.org \
--cc=linux-coco@lists.linux.dev \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-renesas-soc@vger.kernel.org \
--cc=linux-serial@vger.kernel.org \
--cc=rick.p.edgecombe@intel.com \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®