Commit 3af08bd7 authored by Russell King's avatar Russell King

SERIAL: omap: fix hardware assisted flow control

When the UART device has hardware flow control enabled, it ignores the
MCR RTS bit in the MCR register, and keeps RTS asserted as long as we
continue to read characters from the UART receiver FIFO.  This means
that when the TTY buffers become full, the UART doesn't tell the remote
end to stop sending, which causes the TTY layer to start dropping
characters.

A similar problem exists with software flow control.  We need the FIFO
register to fill when software flow control is enabled to provoke the
UART to send the XOFF character.

Fix this by implementing the throttle/unthrottle callbacks, and use
these to disable receiver interrupts.  This in turn means that the UART
FIFO will fill, which will then cause the UART's hardware to deassert
the RTS signal and/or send the XOFF character, stopping the remote end.
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 24054640
...@@ -88,10 +88,10 @@ ...@@ -88,10 +88,10 @@
#define OMAP_UART_WER_MOD_WKUP 0X7F #define OMAP_UART_WER_MOD_WKUP 0X7F
/* Enable XON/XOFF flow control on output */ /* Enable XON/XOFF flow control on output */
#define OMAP_UART_SW_TX 0x4 #define OMAP_UART_SW_TX 0x08
/* Enable XON/XOFF flow control on input */ /* Enable XON/XOFF flow control on input */
#define OMAP_UART_SW_RX 0x4 #define OMAP_UART_SW_RX 0x02
#define OMAP_UART_SW_CLR 0xF0 #define OMAP_UART_SW_CLR 0xF0
...@@ -354,6 +354,34 @@ static void serial_omap_start_tx(struct uart_port *port) ...@@ -354,6 +354,34 @@ static void serial_omap_start_tx(struct uart_port *port)
pm_runtime_put_autosuspend(up->dev); pm_runtime_put_autosuspend(up->dev);
} }
static void serial_omap_throttle(struct uart_port *port)
{
struct uart_omap_port *up = to_uart_omap_port(port);
unsigned long flags;
pm_runtime_get_sync(up->dev);
spin_lock_irqsave(&up->port.lock, flags);
up->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
serial_out(up, UART_IER, up->ier);
spin_unlock_irqrestore(&up->port.lock, flags);
pm_runtime_mark_last_busy(up->dev);
pm_runtime_put_autosuspend(up->dev);
}
static void serial_omap_unthrottle(struct uart_port *port)
{
struct uart_omap_port *up = to_uart_omap_port(port);
unsigned long flags;
pm_runtime_get_sync(up->dev);
spin_lock_irqsave(&up->port.lock, flags);
up->ier |= UART_IER_RLSI | UART_IER_RDI;
serial_out(up, UART_IER, up->ier);
spin_unlock_irqrestore(&up->port.lock, flags);
pm_runtime_mark_last_busy(up->dev);
pm_runtime_put_autosuspend(up->dev);
}
static unsigned int check_modem_status(struct uart_omap_port *up) static unsigned int check_modem_status(struct uart_omap_port *up)
{ {
unsigned int status; unsigned int status;
...@@ -929,19 +957,19 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, ...@@ -929,19 +957,19 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
/* /*
* IXON Flag: * IXON Flag:
* Enable XON/XOFF flow control on output. * Enable XON/XOFF flow control on input.
* Transmit XON1, XOFF1 * Receiver compares XON1, XOFF1.
*/ */
if (termios->c_iflag & IXON) if (termios->c_iflag & IXON)
up->efr |= OMAP_UART_SW_TX; up->efr |= OMAP_UART_SW_RX;
/* /*
* IXOFF Flag: * IXOFF Flag:
* Enable XON/XOFF flow control on input. * Enable XON/XOFF flow control on output.
* Receiver compares XON1, XOFF1. * Transmit XON1, XOFF1
*/ */
if (termios->c_iflag & IXOFF) if (termios->c_iflag & IXOFF)
up->efr |= OMAP_UART_SW_RX; up->efr |= OMAP_UART_SW_TX;
/* /*
* IXANY Flag: * IXANY Flag:
...@@ -1025,6 +1053,7 @@ static void serial_omap_config_port(struct uart_port *port, int flags) ...@@ -1025,6 +1053,7 @@ static void serial_omap_config_port(struct uart_port *port, int flags)
dev_dbg(up->port.dev, "serial_omap_config_port+%d\n", dev_dbg(up->port.dev, "serial_omap_config_port+%d\n",
up->port.line); up->port.line);
up->port.type = PORT_OMAP; up->port.type = PORT_OMAP;
up->port.flags |= UPF_SOFT_FLOW | UPF_HARD_FLOW;
} }
static int static int
...@@ -1228,6 +1257,8 @@ static struct uart_ops serial_omap_pops = { ...@@ -1228,6 +1257,8 @@ static struct uart_ops serial_omap_pops = {
.get_mctrl = serial_omap_get_mctrl, .get_mctrl = serial_omap_get_mctrl,
.stop_tx = serial_omap_stop_tx, .stop_tx = serial_omap_stop_tx,
.start_tx = serial_omap_start_tx, .start_tx = serial_omap_start_tx,
.throttle = serial_omap_throttle,
.unthrottle = serial_omap_unthrottle,
.stop_rx = serial_omap_stop_rx, .stop_rx = serial_omap_stop_rx,
.enable_ms = serial_omap_enable_ms, .enable_ms = serial_omap_enable_ms,
.break_ctl = serial_omap_break_ctl, .break_ctl = serial_omap_break_ctl,
......
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