From cfa6eb239154315e6efcdda1d929e024097f927b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 28 Mar 2017 11:13:46 +0200 Subject: [PATCH] serial: sh-sci: Fix (AUTO)RTS in sci_init_pins() 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 Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 6e405fb5a23f..71707e8e6e3f 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -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);