aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/serial/omap-serial.c
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2012-10-05 08:32:08 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2012-11-04 07:30:28 -0500
commit3af08bd7adb09b0806c51c06b87db08cc7075568 (patch)
tree69b680bace06bb2127a69e883312dedcfb343544 /drivers/tty/serial/omap-serial.c
parent2405464083e152f88bb58b7108a9e50ca362178c (diff)
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: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'drivers/tty/serial/omap-serial.c')
-rw-r--r--drivers/tty/serial/omap-serial.c47
1 files changed, 39 insertions, 8 deletions
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index 156a85438558..4dbbc00c8266 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -88,10 +88,10 @@
88#define OMAP_UART_WER_MOD_WKUP 0X7F 88#define OMAP_UART_WER_MOD_WKUP 0X7F
89 89
90/* Enable XON/XOFF flow control on output */ 90/* Enable XON/XOFF flow control on output */
91#define OMAP_UART_SW_TX 0x4 91#define OMAP_UART_SW_TX 0x08
92 92
93/* Enable XON/XOFF flow control on input */ 93/* Enable XON/XOFF flow control on input */
94#define OMAP_UART_SW_RX 0x4 94#define OMAP_UART_SW_RX 0x02
95 95
96#define OMAP_UART_SW_CLR 0xF0 96#define OMAP_UART_SW_CLR 0xF0
97 97
@@ -354,6 +354,34 @@ static void serial_omap_start_tx(struct uart_port *port)
354 pm_runtime_put_autosuspend(up->dev); 354 pm_runtime_put_autosuspend(up->dev);
355} 355}
356 356
357static void serial_omap_throttle(struct uart_port *port)
358{
359 struct uart_omap_port *up = to_uart_omap_port(port);
360 unsigned long flags;
361
362 pm_runtime_get_sync(up->dev);
363 spin_lock_irqsave(&up->port.lock, flags);
364 up->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
365 serial_out(up, UART_IER, up->ier);
366 spin_unlock_irqrestore(&up->port.lock, flags);
367 pm_runtime_mark_last_busy(up->dev);
368 pm_runtime_put_autosuspend(up->dev);
369}
370
371static void serial_omap_unthrottle(struct uart_port *port)
372{
373 struct uart_omap_port *up = to_uart_omap_port(port);
374 unsigned long flags;
375
376 pm_runtime_get_sync(up->dev);
377 spin_lock_irqsave(&up->port.lock, flags);
378 up->ier |= UART_IER_RLSI | UART_IER_RDI;
379 serial_out(up, UART_IER, up->ier);
380 spin_unlock_irqrestore(&up->port.lock, flags);
381 pm_runtime_mark_last_busy(up->dev);
382 pm_runtime_put_autosuspend(up->dev);
383}
384
357static unsigned int check_modem_status(struct uart_omap_port *up) 385static unsigned int check_modem_status(struct uart_omap_port *up)
358{ 386{
359 unsigned int status; 387 unsigned int status;
@@ -929,19 +957,19 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
929 957
930 /* 958 /*
931 * IXON Flag: 959 * IXON Flag:
932 * Enable XON/XOFF flow control on output. 960 * Enable XON/XOFF flow control on input.
933 * Transmit XON1, XOFF1 961 * Receiver compares XON1, XOFF1.
934 */ 962 */
935 if (termios->c_iflag & IXON) 963 if (termios->c_iflag & IXON)
936 up->efr |= OMAP_UART_SW_TX; 964 up->efr |= OMAP_UART_SW_RX;
937 965
938 /* 966 /*
939 * IXOFF Flag: 967 * IXOFF Flag:
940 * Enable XON/XOFF flow control on input. 968 * Enable XON/XOFF flow control on output.
941 * Receiver compares XON1, XOFF1. 969 * Transmit XON1, XOFF1
942 */ 970 */
943 if (termios->c_iflag & IXOFF) 971 if (termios->c_iflag & IXOFF)
944 up->efr |= OMAP_UART_SW_RX; 972 up->efr |= OMAP_UART_SW_TX;
945 973
946 /* 974 /*
947 * IXANY Flag: 975 * IXANY Flag:
@@ -1025,6 +1053,7 @@ static void serial_omap_config_port(struct uart_port *port, int flags)
1025 dev_dbg(up->port.dev, "serial_omap_config_port+%d\n", 1053 dev_dbg(up->port.dev, "serial_omap_config_port+%d\n",
1026 up->port.line); 1054 up->port.line);
1027 up->port.type = PORT_OMAP; 1055 up->port.type = PORT_OMAP;
1056 up->port.flags |= UPF_SOFT_FLOW | UPF_HARD_FLOW;
1028} 1057}
1029 1058
1030static int 1059static int
@@ -1228,6 +1257,8 @@ static struct uart_ops serial_omap_pops = {
1228 .get_mctrl = serial_omap_get_mctrl, 1257 .get_mctrl = serial_omap_get_mctrl,
1229 .stop_tx = serial_omap_stop_tx, 1258 .stop_tx = serial_omap_stop_tx,
1230 .start_tx = serial_omap_start_tx, 1259 .start_tx = serial_omap_start_tx,
1260 .throttle = serial_omap_throttle,
1261 .unthrottle = serial_omap_unthrottle,
1231 .stop_rx = serial_omap_stop_rx, 1262 .stop_rx = serial_omap_stop_rx,
1232 .enable_ms = serial_omap_enable_ms, 1263 .enable_ms = serial_omap_enable_ms,
1233 .break_ctl = serial_omap_break_ctl, 1264 .break_ctl = serial_omap_break_ctl,