]> git.itanic.dy.fi Git - linux-stable/commitdiff
serial: 8250: 8250_omap: Avoid RS485 RTS glitch on ->set_termios()
authorLukas Wunner <lukas@wunner.de>
Tue, 27 Sep 2022 11:52:34 +0000 (13:52 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 3 Nov 2022 02:44:04 +0000 (03:44 +0100)
RS485-enabled UART ports on TI Sitara SoCs with active-low polarity
exhibit a Transmit Enable glitch on ->set_termios():

omap8250_restore_regs(), which is called from omap_8250_set_termios(),
sets the TCRTLR bit in the MCR register and clears all other bits,
including RTS.  If RTS uses active-low polarity, it is now asserted
for no reason.

The TCRTLR bit is subsequently cleared by writing up->mcr to the MCR
register.  That variable is always zero, so the RTS bit is still cleared
(incorrectly so if RTS is active-high).

(up->mcr is not, as one might think, a cache of the MCR register's
current value.  Rather, it only caches a single bit of that register,
the AFE bit.  And it only does so if the UART supports the AFE bit,
which OMAP does not.  For details see serial8250_do_set_termios() and
serial8250_do_set_mctrl().)

Finally at the end of omap8250_restore_regs(), the MCR register is
restored (and RTS deasserted) by a call to up->port.ops->set_mctrl()
(which equals serial8250_set_mctrl()) and serial8250_em485_stop_tx().

So there's an RTS glitch between setting TCRTLR and calling
serial8250_em485_stop_tx().  Avoid by using a read-modify-write
when setting TCRTLR.

While at it, drop a redundant initialization of up->mcr.  As explained
above, the variable isn't used by the driver and it is already
initialized to zero because it is part of the static struct
serial8250_ports[] declared in 8250_core.c.  (Static structs are
initialized to zero per section 6.7.8 nr. 10 of the C99 standard.)

Cc: Jan Kiszka <jan.kiszka@siemens.com>
Cc: Su Bao Cheng <baocheng.su@siemens.com>
Tested-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Link: https://lore.kernel.org/r/6554b0241a2c7fd50f32576fdbafed96709e11e8.1664278942.git.lukas@wunner.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/8250/8250_omap.c

index 41b8c6b27136a9cdb624e5c7c7c8bbedaeb26b53..68f5a167377fd0a05ea773950a3b7b9aa772a0c1 100644 (file)
@@ -292,6 +292,7 @@ static void omap8250_restore_regs(struct uart_8250_port *up)
 {
        struct omap8250_priv *priv = up->port.private_data;
        struct uart_8250_dma    *dma = up->dma;
+       u8 mcr = serial8250_in_MCR(up);
 
        if (dma && dma->tx_running) {
                /*
@@ -308,7 +309,7 @@ static void omap8250_restore_regs(struct uart_8250_port *up)
        serial_out(up, UART_EFR, UART_EFR_ECB);
 
        serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
-       serial8250_out_MCR(up, UART_MCR_TCRTLR);
+       serial8250_out_MCR(up, mcr | UART_MCR_TCRTLR);
        serial_out(up, UART_FCR, up->fcr);
 
        omap8250_update_scr(up, priv);
@@ -324,7 +325,8 @@ static void omap8250_restore_regs(struct uart_8250_port *up)
        serial_out(up, UART_LCR, 0);
 
        /* drop TCR + TLR access, we setup XON/XOFF later */
-       serial8250_out_MCR(up, up->mcr);
+       serial8250_out_MCR(up, mcr);
+
        serial_out(up, UART_IER, up->ier);
 
        serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
@@ -669,7 +671,6 @@ static int omap_8250_startup(struct uart_port *port)
 
        pm_runtime_get_sync(port->dev);
 
-       up->mcr = 0;
        serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
 
        serial_out(up, UART_LCR, UART_LCR_WLEN8);