aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Hurley <peter@hurleysoftware.com>2015-01-25 14:44:51 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-02-02 13:11:28 -0500
commit391f93f2ec9f857c83bdd21a14dcf7e699f38579 (patch)
tree72cdba8b46c6fe2700e9fd1b8ed1fe84210cd9ac
parenta4c639b04f301ddc3f71bc0f2600c3759846db43 (diff)
serial: core: Rework hw-assisted flow control support
hw-assisted flow control support was added to the serial core in v3.8 with commits, dba05832cbe4f ("SERIAL: core: add hardware assisted h/w flow control support") 2cbacafd7af0f ("SERIAL: core: add hardware assisted s/w flow control support") 9aba8d5b01119 ("SERIAL: core: add throttle/unthrottle callbacks for hardware assisted flow control") Since then, additional requirements for serial core support have arisen. Specifically, 1. Separate tx and rx flow control settings for UARTs which only support tx flow control (ie., autoCTS). 2. Disable sw-assisted CTS flow control in autoCTS mode 3. Support for RTS flow control by serial core and userspace in autoRTS mode Distinguish mode from capability; introduce UPSTAT_AUTORTS, UPSTAT_AUTOCTS and UPSTAT_AUTOXOFF which, when set by the uart driver, enable serial core support for hw-assisted rx, hw-assisted tx and hw-assisted in-band/IXOFF rx flow control, respectively. [Note: hw-assisted in-band/IXON tx flow control does not require serial core support/intervention and can be enabled by the uart driver when required.] These modes must be set/reset in the driver's set_termios() method, based on termios settings, and thus can be safely queried in any context in which one of the port lock, port mutex or termios rwsem are held. Set these modes in the 2 in-tree drivers, omap-serial and 8250_omap, which currently use UPF_HARD_FLOW/UPF_SOFT_FLOW support. Retain UPF_HARD_FLOW and UPF_SOFT_FLOW as capabilities; re-define UPF_HARD_FLOW as both UPF_AUTO_RTS and UPF_AUTO_CTS to allow for distinct and separate rx and tx flow control capabilities. Disable sw-assisted CTS flow control when UPSTAT_AUTOCTS is enabled. Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/tty/serial/8250/8250_omap.c7
-rw-r--r--drivers/tty/serial/omap-serial.c7
-rw-r--r--drivers/tty/serial/serial_core.c76
-rw-r--r--include/linux/serial_core.h21
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}
2873EXPORT_SYMBOL_GPL(uart_handle_cts_change); 2855EXPORT_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
403static 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 */