serial: sh-sci: Fix (AUTO)RTS in sci_init_pins()
authorGeert Uytterhoeven <geert+renesas@glider.be>
Tue, 28 Mar 2017 09:13:46 +0000 (11:13 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 31 Mar 2017 13:29:06 +0000 (15:29 +0200)
If a UART has dedicated RTS/CTS pins, and hardware control flow is
disabled (or AUTORTS is not yet effective), changing any serial port
configuration deasserts RTS, as .set_termios() calls sci_init_pins().

To fix this, consider the current (AUTO)RTS state when (re)initializing
the pins.  Note that for SCIFA/SCIFB, AUTORTS needs explicit
configuration of the RTS# pin function, while (H)SCIF handles this
automatically.

Fixes: d2b9775d795ec05f ("serial: sh-sci: Correct pin initialization on (H)SCIF")
Fixes: e9d7a45a03991349 ("serial: sh-sci: Add pin initialization for SCIFA/SCIFB")
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/sh-sci.c

index 6e405fb..71707e8 100644 (file)
@@ -683,24 +683,37 @@ static void sci_init_pins(struct uart_port *port, unsigned int cflag)
        }
 
        if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
+               u16 data = serial_port_in(port, SCPDR);
                u16 ctrl = serial_port_in(port, SCPCR);
 
                /* Enable RXD and TXD pin functions */
                ctrl &= ~(SCPCR_RXDC | SCPCR_TXDC);
                if (to_sci_port(port)->has_rtscts) {
-                       /* RTS# is output, driven 1 */
-                       ctrl |= SCPCR_RTSC;
-                       serial_port_out(port, SCPDR,
-                               serial_port_in(port, SCPDR) | SCPDR_RTSD);
+                       /* RTS# is output, active low, unless autorts */
+                       if (!(port->mctrl & TIOCM_RTS)) {
+                               ctrl |= SCPCR_RTSC;
+                               data |= SCPDR_RTSD;
+                       } else if (!s->autorts) {
+                               ctrl |= SCPCR_RTSC;
+                               data &= ~SCPDR_RTSD;
+                       } else {
+                               /* Enable RTS# pin function */
+                               ctrl &= ~SCPCR_RTSC;
+                       }
                        /* Enable CTS# pin function */
                        ctrl &= ~SCPCR_CTSC;
                }
+               serial_port_out(port, SCPDR, data);
                serial_port_out(port, SCPCR, ctrl);
        } else if (sci_getreg(port, SCSPTR)->size) {
                u16 status = serial_port_in(port, SCSPTR);
 
-               /* RTS# is output, driven 1 */
-               status |= SCSPTR_RTSIO | SCSPTR_RTSDT;
+               /* RTS# is always output; and active low, unless autorts */
+               status |= SCSPTR_RTSIO;
+               if (!(port->mctrl & TIOCM_RTS))
+                       status |= SCSPTR_RTSDT;
+               else if (!s->autorts)
+                       status &= ~SCSPTR_RTSDT;
                /* CTS# and SCK are inputs */
                status &= ~(SCSPTR_CTSIO | SCSPTR_SCKIO);
                serial_port_out(port, SCSPTR, status);