From: Au Dang 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 Signed-off-by: Biju Das --- 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 | 8 ++++++-- 2 files changed, 8 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 58120b523ca1..aa179c787487 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 */ @@ -1049,7 +1050,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); @@ -3653,6 +3655,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 = { @@ -3791,6 +3794,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"); -- 2.43.0