diff options
-rw-r--r-- | drivers/tty/serial/8250/8250_omap.c | 7 | ||||
-rw-r--r-- | drivers/tty/serial/omap-serial.c | 7 | ||||
-rw-r--r-- | drivers/tty/serial/serial_core.c | 76 | ||||
-rw-r--r-- | include/linux/serial_core.h | 21 |
4 files changed, 59 insertions, 52 deletions
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 9191d05cfaf0..6e4ff7148ead 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c | |||
@@ -421,8 +421,11 @@ static void omap_8250_set_termios(struct uart_port *port, | |||
421 | 421 | ||
422 | priv->efr = 0; | 422 | priv->efr = 0; |
423 | up->mcr &= ~(UART_MCR_RTS | UART_MCR_XONANY); | 423 | up->mcr &= ~(UART_MCR_RTS | UART_MCR_XONANY); |
424 | up->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF); | ||
425 | |||
424 | if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) { | 426 | if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) { |
425 | /* Enable AUTORTS and AUTOCTS */ | 427 | /* Enable AUTORTS and AUTOCTS */ |
428 | up->port.status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS; | ||
426 | priv->efr |= UART_EFR_CTS | UART_EFR_RTS; | 429 | priv->efr |= UART_EFR_CTS | UART_EFR_RTS; |
427 | } else if (up->port.flags & UPF_SOFT_FLOW) { | 430 | } else if (up->port.flags & UPF_SOFT_FLOW) { |
428 | /* | 431 | /* |
@@ -438,8 +441,10 @@ static void omap_8250_set_termios(struct uart_port *port, | |||
438 | * Enable XON/XOFF flow control on output. | 441 | * Enable XON/XOFF flow control on output. |
439 | * Transmit XON1, XOFF1 | 442 | * Transmit XON1, XOFF1 |
440 | */ | 443 | */ |
441 | if (termios->c_iflag & IXOFF) | 444 | if (termios->c_iflag & IXOFF) { |
445 | up->port.status |= UPSTAT_AUTOXOFF; | ||
442 | priv->efr |= OMAP_UART_SW_TX; | 446 | priv->efr |= OMAP_UART_SW_TX; |
447 | } | ||
443 | 448 | ||
444 | /* | 449 | /* |
445 | * IXANY Flag: | 450 | * IXANY Flag: |
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index b1cf9a3b673a..6129fe515932 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c | |||
@@ -1053,8 +1053,11 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, | |||
1053 | 1053 | ||
1054 | serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); | 1054 | serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); |
1055 | 1055 | ||
1056 | up->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF); | ||
1057 | |||
1056 | if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) { | 1058 | if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) { |
1057 | /* Enable AUTORTS and AUTOCTS */ | 1059 | /* Enable AUTORTS and AUTOCTS */ |
1060 | up->port.status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS; | ||
1058 | up->efr |= UART_EFR_CTS | UART_EFR_RTS; | 1061 | up->efr |= UART_EFR_CTS | UART_EFR_RTS; |
1059 | 1062 | ||
1060 | /* Ensure MCR RTS is asserted */ | 1063 | /* Ensure MCR RTS is asserted */ |
@@ -1081,8 +1084,10 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, | |||
1081 | * Enable XON/XOFF flow control on output. | 1084 | * Enable XON/XOFF flow control on output. |
1082 | * Transmit XON1, XOFF1 | 1085 | * Transmit XON1, XOFF1 |
1083 | */ | 1086 | */ |
1084 | if (termios->c_iflag & IXOFF) | 1087 | if (termios->c_iflag & IXOFF) { |
1088 | up->port.status |= UPSTAT_AUTOXOFF; | ||
1085 | up->efr |= OMAP_UART_SW_TX; | 1089 | up->efr |= OMAP_UART_SW_TX; |
1090 | } | ||
1086 | 1091 | ||
1087 | /* | 1092 | /* |
1088 | * IXANY Flag: | 1093 | * IXANY Flag: |
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 2c67a077042a..6a1055ae3437 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c | |||
@@ -179,14 +179,6 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, | |||
179 | if (tty->termios.c_cflag & CBAUD) | 179 | if (tty->termios.c_cflag & CBAUD) |
180 | uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR); | 180 | uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR); |
181 | } | 181 | } |
182 | |||
183 | spin_lock_irq(&uport->lock); | ||
184 | if (uart_cts_enabled(uport) && | ||
185 | !(uport->ops->get_mctrl(uport) & TIOCM_CTS)) | ||
186 | uport->hw_stopped = 1; | ||
187 | else | ||
188 | uport->hw_stopped = 0; | ||
189 | spin_unlock_irq(&uport->lock); | ||
190 | } | 182 | } |
191 | 183 | ||
192 | /* | 184 | /* |
@@ -442,6 +434,7 @@ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, | |||
442 | { | 434 | { |
443 | struct uart_port *uport = state->uart_port; | 435 | struct uart_port *uport = state->uart_port; |
444 | struct ktermios *termios; | 436 | struct ktermios *termios; |
437 | int hw_stopped; | ||
445 | 438 | ||
446 | /* | 439 | /* |
447 | * If we have no tty, termios, or the port does not exist, | 440 | * If we have no tty, termios, or the port does not exist, |
@@ -466,6 +459,18 @@ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, | |||
466 | uport->status &= ~UPSTAT_DCD_ENABLE; | 459 | uport->status &= ~UPSTAT_DCD_ENABLE; |
467 | else | 460 | else |
468 | uport->status |= UPSTAT_DCD_ENABLE; | 461 | uport->status |= UPSTAT_DCD_ENABLE; |
462 | |||
463 | /* reset sw-assisted CTS flow control based on (possibly) new mode */ | ||
464 | hw_stopped = uport->hw_stopped; | ||
465 | uport->hw_stopped = uart_softcts_mode(uport) && | ||
466 | !(uport->ops->get_mctrl(uport) & TIOCM_CTS); | ||
467 | if (uport->hw_stopped) { | ||
468 | if (!hw_stopped) | ||
469 | uport->ops->stop_tx(uport); | ||
470 | } else { | ||
471 | if (hw_stopped) | ||
472 | __uart_start(tty); | ||
473 | } | ||
469 | spin_unlock_irq(&uport->lock); | 474 | spin_unlock_irq(&uport->lock); |
470 | } | 475 | } |
471 | 476 | ||
@@ -619,22 +624,22 @@ static void uart_throttle(struct tty_struct *tty) | |||
619 | { | 624 | { |
620 | struct uart_state *state = tty->driver_data; | 625 | struct uart_state *state = tty->driver_data; |
621 | struct uart_port *port = state->uart_port; | 626 | struct uart_port *port = state->uart_port; |
622 | upf_t mask = 0; | 627 | upstat_t mask = 0; |
623 | 628 | ||
624 | if (I_IXOFF(tty)) | 629 | if (I_IXOFF(tty)) |
625 | mask |= UPF_SOFT_FLOW; | 630 | mask |= UPSTAT_AUTOXOFF; |
626 | if (tty->termios.c_cflag & CRTSCTS) | 631 | if (tty->termios.c_cflag & CRTSCTS) |
627 | mask |= UPF_HARD_FLOW; | 632 | mask |= UPSTAT_AUTORTS; |
628 | 633 | ||
629 | if (port->flags & mask) { | 634 | if (port->status & mask) { |
630 | port->ops->throttle(port); | 635 | port->ops->throttle(port); |
631 | mask &= ~port->flags; | 636 | mask &= ~port->status; |
632 | } | 637 | } |
633 | 638 | ||
634 | if (mask & UPF_SOFT_FLOW) | 639 | if (mask & UPSTAT_AUTOXOFF) |
635 | uart_send_xchar(tty, STOP_CHAR(tty)); | 640 | uart_send_xchar(tty, STOP_CHAR(tty)); |
636 | 641 | ||
637 | if (mask & UPF_HARD_FLOW) | 642 | if (mask & UPSTAT_AUTORTS) |
638 | uart_clear_mctrl(port, TIOCM_RTS); | 643 | uart_clear_mctrl(port, TIOCM_RTS); |
639 | } | 644 | } |
640 | 645 | ||
@@ -642,22 +647,22 @@ static void uart_unthrottle(struct tty_struct *tty) | |||
642 | { | 647 | { |
643 | struct uart_state *state = tty->driver_data; | 648 | struct uart_state *state = tty->driver_data; |
644 | struct uart_port *port = state->uart_port; | 649 | struct uart_port *port = state->uart_port; |
645 | upf_t mask = 0; | 650 | upstat_t mask = 0; |
646 | 651 | ||
647 | if (I_IXOFF(tty)) | 652 | if (I_IXOFF(tty)) |
648 | mask |= UPF_SOFT_FLOW; | 653 | mask |= UPSTAT_AUTOXOFF; |
649 | if (tty->termios.c_cflag & CRTSCTS) | 654 | if (tty->termios.c_cflag & CRTSCTS) |
650 | mask |= UPF_HARD_FLOW; | 655 | mask |= UPSTAT_AUTORTS; |
651 | 656 | ||
652 | if (port->flags & mask) { | 657 | if (port->status & mask) { |
653 | port->ops->unthrottle(port); | 658 | port->ops->unthrottle(port); |
654 | mask &= ~port->flags; | 659 | mask &= ~port->status; |
655 | } | 660 | } |
656 | 661 | ||
657 | if (mask & UPF_SOFT_FLOW) | 662 | if (mask & UPSTAT_AUTOXOFF) |
658 | uart_send_xchar(tty, START_CHAR(tty)); | 663 | uart_send_xchar(tty, START_CHAR(tty)); |
659 | 664 | ||
660 | if (mask & UPF_HARD_FLOW) | 665 | if (mask & UPSTAT_AUTORTS) |
661 | uart_set_mctrl(port, TIOCM_RTS); | 666 | uart_set_mctrl(port, TIOCM_RTS); |
662 | } | 667 | } |
663 | 668 | ||
@@ -1351,30 +1356,6 @@ static void uart_set_termios(struct tty_struct *tty, | |||
1351 | mask |= TIOCM_RTS; | 1356 | mask |= TIOCM_RTS; |
1352 | uart_set_mctrl(uport, mask); | 1357 | uart_set_mctrl(uport, mask); |
1353 | } | 1358 | } |
1354 | |||
1355 | /* | ||
1356 | * If the port is doing h/w assisted flow control, do nothing. | ||
1357 | * We assume that port->hw_stopped has never been set. | ||
1358 | */ | ||
1359 | if (uport->flags & UPF_HARD_FLOW) | ||
1360 | return; | ||
1361 | |||
1362 | /* Handle turning off CRTSCTS */ | ||
1363 | if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { | ||
1364 | spin_lock_irq(&uport->lock); | ||
1365 | uport->hw_stopped = 0; | ||
1366 | __uart_start(tty); | ||
1367 | spin_unlock_irq(&uport->lock); | ||
1368 | } | ||
1369 | /* Handle turning on CRTSCTS */ | ||
1370 | else if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) { | ||
1371 | spin_lock_irq(&uport->lock); | ||
1372 | if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) { | ||
1373 | uport->hw_stopped = 1; | ||
1374 | uport->ops->stop_tx(uport); | ||
1375 | } | ||
1376 | spin_unlock_irq(&uport->lock); | ||
1377 | } | ||
1378 | } | 1359 | } |
1379 | 1360 | ||
1380 | /* | 1361 | /* |
@@ -2855,7 +2836,7 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status) | |||
2855 | 2836 | ||
2856 | uport->icount.cts++; | 2837 | uport->icount.cts++; |
2857 | 2838 | ||
2858 | if (uart_cts_enabled(uport)) { | 2839 | if (uart_softcts_mode(uport)) { |
2859 | if (uport->hw_stopped) { | 2840 | if (uport->hw_stopped) { |
2860 | if (status) { | 2841 | if (status) { |
2861 | uport->hw_stopped = 0; | 2842 | uport->hw_stopped = 0; |
@@ -2868,6 +2849,7 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status) | |||
2868 | uport->ops->stop_tx(uport); | 2849 | uport->ops->stop_tx(uport); |
2869 | } | 2850 | } |
2870 | } | 2851 | } |
2852 | |||
2871 | } | 2853 | } |
2872 | } | 2854 | } |
2873 | EXPORT_SYMBOL_GPL(uart_handle_cts_change); | 2855 | EXPORT_SYMBOL_GPL(uart_handle_cts_change); |
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index a0c7033d5f91..baf3e1d08416 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h | |||
@@ -191,8 +191,10 @@ struct uart_port { | |||
191 | #define UPF_NO_TXEN_TEST ((__force upf_t) (1 << 15)) | 191 | #define UPF_NO_TXEN_TEST ((__force upf_t) (1 << 15)) |
192 | #define UPF_MAGIC_MULTIPLIER ((__force upf_t) ASYNC_MAGIC_MULTIPLIER /* 16 */ ) | 192 | #define UPF_MAGIC_MULTIPLIER ((__force upf_t) ASYNC_MAGIC_MULTIPLIER /* 16 */ ) |
193 | 193 | ||
194 | /* Port has hardware-assisted h/w flow control (iow, auto-RTS *not* auto-CTS) */ | 194 | /* Port has hardware-assisted h/w flow control */ |
195 | #define UPF_HARD_FLOW ((__force upf_t) (1 << 21)) | 195 | #define UPF_AUTO_CTS ((__force upf_t) (1 << 20)) |
196 | #define UPF_AUTO_RTS ((__force upf_t) (1 << 21)) | ||
197 | #define UPF_HARD_FLOW ((__force upf_t) (UPF_AUTO_CTS | UPF_AUTO_RTS)) | ||
196 | /* Port has hardware-assisted s/w flow control */ | 198 | /* Port has hardware-assisted s/w flow control */ |
197 | #define UPF_SOFT_FLOW ((__force upf_t) (1 << 22)) | 199 | #define UPF_SOFT_FLOW ((__force upf_t) (1 << 22)) |
198 | #define UPF_CONS_FLOW ((__force upf_t) (1 << 23)) | 200 | #define UPF_CONS_FLOW ((__force upf_t) (1 << 23)) |
@@ -214,11 +216,17 @@ struct uart_port { | |||
214 | #error Change mask not equivalent to userspace-visible bit defines | 216 | #error Change mask not equivalent to userspace-visible bit defines |
215 | #endif | 217 | #endif |
216 | 218 | ||
217 | /* status must be updated while holding port lock */ | 219 | /* |
220 | * Must hold termios_rwsem, port mutex and port lock to change; | ||
221 | * can hold any one lock to read. | ||
222 | */ | ||
218 | upstat_t status; | 223 | upstat_t status; |
219 | 224 | ||
220 | #define UPSTAT_CTS_ENABLE ((__force upstat_t) (1 << 0)) | 225 | #define UPSTAT_CTS_ENABLE ((__force upstat_t) (1 << 0)) |
221 | #define UPSTAT_DCD_ENABLE ((__force upstat_t) (1 << 1)) | 226 | #define UPSTAT_DCD_ENABLE ((__force upstat_t) (1 << 1)) |
227 | #define UPSTAT_AUTORTS ((__force upstat_t) (1 << 2)) | ||
228 | #define UPSTAT_AUTOCTS ((__force upstat_t) (1 << 3)) | ||
229 | #define UPSTAT_AUTOXOFF ((__force upstat_t) (1 << 4)) | ||
222 | 230 | ||
223 | int hw_stopped; /* sw-assisted CTS flow state */ | 231 | int hw_stopped; /* sw-assisted CTS flow state */ |
224 | unsigned int mctrl; /* current modem ctrl settings */ | 232 | unsigned int mctrl; /* current modem ctrl settings */ |
@@ -392,6 +400,13 @@ static inline bool uart_cts_enabled(struct uart_port *uport) | |||
392 | return !!(uport->status & UPSTAT_CTS_ENABLE); | 400 | return !!(uport->status & UPSTAT_CTS_ENABLE); |
393 | } | 401 | } |
394 | 402 | ||
403 | static inline bool uart_softcts_mode(struct uart_port *uport) | ||
404 | { | ||
405 | upstat_t mask = UPSTAT_CTS_ENABLE | UPSTAT_AUTOCTS; | ||
406 | |||
407 | return ((uport->status & mask) == UPSTAT_CTS_ENABLE); | ||
408 | } | ||
409 | |||
395 | /* | 410 | /* |
396 | * The following are helper functions for the low level drivers. | 411 | * The following are helper functions for the low level drivers. |
397 | */ | 412 | */ |