summaryrefslogtreecommitdiffstats
path: root/drivers/tty
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 10:11:28 -0800
commit391f93f2ec9f857c83bdd21a14dcf7e699f38579 (patch)
tree72cdba8b46c6fe2700e9fd1b8ed1fe84210cd9ac /drivers/tty
parenta4c639b04f301ddc3f71bc0f2600c3759846db43 (diff)
downloadlinux-391f93f2ec9f857c83bdd21a14dcf7e699f38579.tar.bz2
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>
Diffstat (limited to 'drivers/tty')
-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
3 files changed, 41 insertions, 49 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,
priv->efr = 0;
up->mcr &= ~(UART_MCR_RTS | UART_MCR_XONANY);
+ up->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF);
+
if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) {
/* Enable AUTORTS and AUTOCTS */
+ up->port.status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
priv->efr |= UART_EFR_CTS | UART_EFR_RTS;
} else if (up->port.flags & UPF_SOFT_FLOW) {
/*
@@ -438,8 +441,10 @@ static void omap_8250_set_termios(struct uart_port *port,
* Enable XON/XOFF flow control on output.
* Transmit XON1, XOFF1
*/
- if (termios->c_iflag & IXOFF)
+ if (termios->c_iflag & IXOFF) {
+ up->port.status |= UPSTAT_AUTOXOFF;
priv->efr |= OMAP_UART_SW_TX;
+ }
/*
* 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,
serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG);
+ up->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF);
+
if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) {
/* Enable AUTORTS and AUTOCTS */
+ up->port.status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
up->efr |= UART_EFR_CTS | UART_EFR_RTS;
/* Ensure MCR RTS is asserted */
@@ -1081,8 +1084,10 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
* Enable XON/XOFF flow control on output.
* Transmit XON1, XOFF1
*/
- if (termios->c_iflag & IXOFF)
+ if (termios->c_iflag & IXOFF) {
+ up->port.status |= UPSTAT_AUTOXOFF;
up->efr |= OMAP_UART_SW_TX;
+ }
/*
* 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,
if (tty->termios.c_cflag & CBAUD)
uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
}
-
- spin_lock_irq(&uport->lock);
- if (uart_cts_enabled(uport) &&
- !(uport->ops->get_mctrl(uport) & TIOCM_CTS))
- uport->hw_stopped = 1;
- else
- uport->hw_stopped = 0;
- spin_unlock_irq(&uport->lock);
}
/*
@@ -442,6 +434,7 @@ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
{
struct uart_port *uport = state->uart_port;
struct ktermios *termios;
+ int hw_stopped;
/*
* 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,
uport->status &= ~UPSTAT_DCD_ENABLE;
else
uport->status |= UPSTAT_DCD_ENABLE;
+
+ /* reset sw-assisted CTS flow control based on (possibly) new mode */
+ hw_stopped = uport->hw_stopped;
+ uport->hw_stopped = uart_softcts_mode(uport) &&
+ !(uport->ops->get_mctrl(uport) & TIOCM_CTS);
+ if (uport->hw_stopped) {
+ if (!hw_stopped)
+ uport->ops->stop_tx(uport);
+ } else {
+ if (hw_stopped)
+ __uart_start(tty);
+ }
spin_unlock_irq(&uport->lock);
}
@@ -619,22 +624,22 @@ static void uart_throttle(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->uart_port;
- upf_t mask = 0;
+ upstat_t mask = 0;
if (I_IXOFF(tty))
- mask |= UPF_SOFT_FLOW;
+ mask |= UPSTAT_AUTOXOFF;
if (tty->termios.c_cflag & CRTSCTS)
- mask |= UPF_HARD_FLOW;
+ mask |= UPSTAT_AUTORTS;
- if (port->flags & mask) {
+ if (port->status & mask) {
port->ops->throttle(port);
- mask &= ~port->flags;
+ mask &= ~port->status;
}
- if (mask & UPF_SOFT_FLOW)
+ if (mask & UPSTAT_AUTOXOFF)
uart_send_xchar(tty, STOP_CHAR(tty));
- if (mask & UPF_HARD_FLOW)
+ if (mask & UPSTAT_AUTORTS)
uart_clear_mctrl(port, TIOCM_RTS);
}
@@ -642,22 +647,22 @@ static void uart_unthrottle(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->uart_port;
- upf_t mask = 0;
+ upstat_t mask = 0;
if (I_IXOFF(tty))
- mask |= UPF_SOFT_FLOW;
+ mask |= UPSTAT_AUTOXOFF;
if (tty->termios.c_cflag & CRTSCTS)
- mask |= UPF_HARD_FLOW;
+ mask |= UPSTAT_AUTORTS;
- if (port->flags & mask) {
+ if (port->status & mask) {
port->ops->unthrottle(port);
- mask &= ~port->flags;
+ mask &= ~port->status;
}
- if (mask & UPF_SOFT_FLOW)
+ if (mask & UPSTAT_AUTOXOFF)
uart_send_xchar(tty, START_CHAR(tty));
- if (mask & UPF_HARD_FLOW)
+ if (mask & UPSTAT_AUTORTS)
uart_set_mctrl(port, TIOCM_RTS);
}
@@ -1351,30 +1356,6 @@ static void uart_set_termios(struct tty_struct *tty,
mask |= TIOCM_RTS;
uart_set_mctrl(uport, mask);
}
-
- /*
- * If the port is doing h/w assisted flow control, do nothing.
- * We assume that port->hw_stopped has never been set.
- */
- if (uport->flags & UPF_HARD_FLOW)
- return;
-
- /* Handle turning off CRTSCTS */
- if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
- spin_lock_irq(&uport->lock);
- uport->hw_stopped = 0;
- __uart_start(tty);
- spin_unlock_irq(&uport->lock);
- }
- /* Handle turning on CRTSCTS */
- else if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {
- spin_lock_irq(&uport->lock);
- if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) {
- uport->hw_stopped = 1;
- uport->ops->stop_tx(uport);
- }
- spin_unlock_irq(&uport->lock);
- }
}
/*
@@ -2855,7 +2836,7 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status)
uport->icount.cts++;
- if (uart_cts_enabled(uport)) {
+ if (uart_softcts_mode(uport)) {
if (uport->hw_stopped) {
if (status) {
uport->hw_stopped = 0;
@@ -2868,6 +2849,7 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status)
uport->ops->stop_tx(uport);
}
}
+
}
}
EXPORT_SYMBOL_GPL(uart_handle_cts_change);