Commit 4bf4ea9d authored by Peter Hurley's avatar Peter Hurley Committed by Greg Kroah-Hartman

serial: omap_8250: Fix RTS handling

The OMAP3 UART ignores MCR[1] (ie., UART_MCR_RTS) when in autoRTS
mode (UPF_HARD_FLOW + CRTSCTS). This makes it impossible for either
the serial core or userspace to manually flow control the sender.

Disable autoRTS mode when RTS is lowered and restore the previous
mode when RTS is raised.

Note that the OMAP3 UART provides no mechanism for switching from
autoRTS mode without corrupting incoming data; to access the
necessary register, the line control settings must be set to 8-e-2
and thus any data received during that time will be interpreted with
those settings. This corruption has been observed in practice.

Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: default avatarPeter Hurley <peter@hurleysoftware.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent fbf7ebe4
...@@ -1924,7 +1924,7 @@ static unsigned int serial8250_get_mctrl(struct uart_port *port) ...@@ -1924,7 +1924,7 @@ static unsigned int serial8250_get_mctrl(struct uart_port *port)
return ret; return ret;
} }
static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl) void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl)
{ {
struct uart_8250_port *up = up_to_u8250p(port); struct uart_8250_port *up = up_to_u8250p(port);
unsigned char mcr = 0; unsigned char mcr = 0;
...@@ -1944,6 +1944,14 @@ static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl) ...@@ -1944,6 +1944,14 @@ static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
serial_port_out(port, UART_MCR, mcr); serial_port_out(port, UART_MCR, mcr);
} }
EXPORT_SYMBOL_GPL(serial8250_do_set_mctrl);
static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
if (port->set_mctrl)
return port->set_mctrl(port, mctrl);
return serial8250_do_set_mctrl(port, mctrl);
}
static void serial8250_break_ctl(struct uart_port *port, int break_state) static void serial8250_break_ctl(struct uart_port *port, int break_state)
{ {
...@@ -3605,6 +3613,8 @@ int serial8250_register_8250_port(struct uart_8250_port *up) ...@@ -3605,6 +3613,8 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
/* Possibly override set_termios call */ /* Possibly override set_termios call */
if (up->port.set_termios) if (up->port.set_termios)
uart->port.set_termios = up->port.set_termios; uart->port.set_termios = up->port.set_termios;
if (up->port.set_mctrl)
uart->port.set_mctrl = up->port.set_mctrl;
if (up->port.startup) if (up->port.startup)
uart->port.startup = up->port.startup; uart->port.startup = up->port.startup;
if (up->port.shutdown) if (up->port.shutdown)
......
...@@ -106,6 +106,27 @@ static u32 uart_read(struct uart_8250_port *up, u32 reg) ...@@ -106,6 +106,27 @@ static u32 uart_read(struct uart_8250_port *up, u32 reg)
return readl(up->port.membase + (reg << up->port.regshift)); return readl(up->port.membase + (reg << up->port.regshift));
} }
static void omap8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct uart_8250_port *up = up_to_u8250p(port);
struct omap8250_priv *priv = up->port.private_data;
u8 lcr;
serial8250_do_set_mctrl(port, mctrl);
/*
* Turn off autoRTS if RTS is lowered and restore autoRTS setting
* if RTS is raised
*/
lcr = serial_in(up, UART_LCR);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
if (mctrl & TIOCM_RTS)
serial_out(up, UART_EFR, priv->efr);
else
serial_out(up, UART_EFR, priv->efr & ~UART_EFR_RTS);
serial_out(up, UART_LCR, lcr);
}
/* /*
* Work Around for Errata i202 (2430, 3430, 3630, 4430 and 4460) * Work Around for Errata i202 (2430, 3430, 3630, 4430 and 4460)
* The access to uart register after MDR1 Access * The access to uart register after MDR1 Access
...@@ -400,9 +421,6 @@ static void omap_8250_set_termios(struct uart_port *port, ...@@ -400,9 +421,6 @@ static void omap_8250_set_termios(struct uart_port *port,
if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) { if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) {
/* Enable AUTORTS and AUTOCTS */ /* Enable AUTORTS and AUTOCTS */
priv->efr |= UART_EFR_CTS | UART_EFR_RTS; priv->efr |= UART_EFR_CTS | UART_EFR_RTS;
/* Ensure MCR RTS is asserted */
up->mcr |= UART_MCR_RTS;
} else if (up->port.flags & UPF_SOFT_FLOW) { } else if (up->port.flags & UPF_SOFT_FLOW) {
/* /*
* IXON Flag: * IXON Flag:
...@@ -1007,6 +1025,7 @@ static int omap8250_probe(struct platform_device *pdev) ...@@ -1007,6 +1025,7 @@ static int omap8250_probe(struct platform_device *pdev)
up.capabilities |= UART_CAP_RPM; up.capabilities |= UART_CAP_RPM;
#endif #endif
up.port.set_termios = omap_8250_set_termios; up.port.set_termios = omap_8250_set_termios;
up.port.set_mctrl = omap8250_set_mctrl;
up.port.pm = omap_8250_pm; up.port.pm = omap_8250_pm;
up.port.startup = omap_8250_startup; up.port.startup = omap_8250_startup;
up.port.shutdown = omap_8250_shutdown; up.port.shutdown = omap_8250_shutdown;
......
...@@ -126,6 +126,7 @@ extern int serial8250_do_startup(struct uart_port *port); ...@@ -126,6 +126,7 @@ extern int serial8250_do_startup(struct uart_port *port);
extern void serial8250_do_shutdown(struct uart_port *port); extern void serial8250_do_shutdown(struct uart_port *port);
extern void serial8250_do_pm(struct uart_port *port, unsigned int state, extern void serial8250_do_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate); unsigned int oldstate);
extern void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl);
extern int fsl8250_handle_irq(struct uart_port *port); extern int fsl8250_handle_irq(struct uart_port *port);
int serial8250_handle_irq(struct uart_port *port, unsigned int iir); int serial8250_handle_irq(struct uart_port *port, unsigned int iir);
unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr); unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr);
......
...@@ -123,6 +123,7 @@ struct uart_port { ...@@ -123,6 +123,7 @@ struct uart_port {
void (*set_termios)(struct uart_port *, void (*set_termios)(struct uart_port *,
struct ktermios *new, struct ktermios *new,
struct ktermios *old); struct ktermios *old);
void (*set_mctrl)(struct uart_port *, unsigned int);
int (*startup)(struct uart_port *port); int (*startup)(struct uart_port *port);
void (*shutdown)(struct uart_port *port); void (*shutdown)(struct uart_port *port);
void (*throttle)(struct uart_port *port); void (*throttle)(struct uart_port *port);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment