[PATCH v3 3/4] serial: sh-sci: Skip TDxE clear when hardware clears it automatically

From: Biju Das

Date: Mon Oct 26 2026 - 07:33:06 EST


From: Au Dang <au.dang.jx@xxxxxxxxxxx>

On some SCI variants, the TDxE (Transmit Data Empty) flag in SCxSR is
cleared by hardware when data is written to TDR. Clearing it again from
software via clear_SCxSR() generates a spurious TXI interrupt, causing
the last character to be transmitted twice.

Add a tdxe_auto_clear flag to struct sci_of_data, propagate it to struct
sci_port as has_tdxe_auto_clear, and skip the clear_SCxSR() call in
sci_transmit_chars() and sci_poll_put_char() when it is set. Enable the
flag for the PORT_SCI (of_sci_sci) variant.

Signed-off-by: Au Dang <au.dang.jx@xxxxxxxxxxx>
Signed-off-by: Biju Das <biju.das.jz@xxxxxxxxxxxxxx>
---
v2->v3:
* Added missing has_tdxe_auto_clear init in scix_early_console_setup().
v1->v2:
* Updated commit header and description.
* Replaced the PORT_SCI check with a has_tdxe_auto_clear flag in
struct sci_port, set from the new tdxe_auto_clear field in
struct sci_of_data.
---
drivers/tty/serial/sh-sci-common.h | 2 ++
drivers/tty/serial/sh-sci.c | 9 +++++++--
2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/sh-sci-common.h b/drivers/tty/serial/sh-sci-common.h
index 273856fbc20b..b75e5e1d3075 100644
--- a/drivers/tty/serial/sh-sci-common.h
+++ b/drivers/tty/serial/sh-sci-common.h
@@ -108,6 +108,7 @@ struct sci_of_data {
const struct sci_port_ops *ops;
unsigned short regtype;
unsigned short type;
+ bool tdxe_auto_clear;
};

struct sci_port {
@@ -163,6 +164,7 @@ struct sci_port {
bool has_rtscts;
bool autorts;
bool tx_occurred;
+ bool has_tdxe_auto_clear;
};

#define to_sci_port(uart) container_of((uart), struct sci_port, port)
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 6df25fef5a5f..1a4d9467d2be 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -913,7 +913,8 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c)
} while (!(status & SCxSR_TDxE(port)));

sci_serial_out(port, SCxTDR, c);
- s->ops->clear_SCxSR(port, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port));
+ if (!s->has_tdxe_auto_clear)
+ s->ops->clear_SCxSR(port, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port));
}
#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE ||
CONFIG_SERIAL_SH_SCI_EARLYCON */
@@ -1063,7 +1064,8 @@ static void sci_transmit_chars(struct uart_port *port)
port->icount.tx++;
} while (--count > 0);

- s->ops->clear_SCxSR(port, SCxSR_TDxE_CLEAR(port));
+ if (!s->has_tdxe_auto_clear)
+ s->ops->clear_SCxSR(port, SCxSR_TDxE_CLEAR(port));

if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);
@@ -3664,6 +3666,7 @@ static const struct sci_of_data of_sci_sci = {
.ops = &sci_port_ops,
.uart_ops = &sci_uart_ops,
.params = &sci_port_params[SCIx_SCI_REGTYPE],
+ .tdxe_auto_clear = true,
};

static const struct of_device_id of_sci_match[] __maybe_unused = {
@@ -3802,6 +3805,7 @@ static struct plat_sci_port *sci_parse_dt(struct platform_device *pdev,
sp->ops = data->ops;
sp->port.ops = data->uart_ops;
sp->params = data->params;
+ sp->has_tdxe_auto_clear = data->tdxe_auto_clear;

sp->has_rtscts = of_property_read_bool(np, "uart-has-rtscts");

@@ -4088,6 +4092,7 @@ int __init scix_early_console_setup(struct earlycon_device *device,
sci_ports[0].params = data->params;
sci_ports[0].ops = data->ops;
sci_ports[0].port.ops = data->uart_ops;
+ sci_ports[0].has_tdxe_auto_clear = data->tdxe_auto_clear;
sci_uart_earlycon = true;
regs = sci_ports[0].params->common_regs;


--
2.43.0