From dec94e70e12c39440e63159e0050d46795dfcf09 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 24 Sep 2012 11:13:15 +0100 Subject: SERIAL: core: use local variable uport in uart_set_termios() This is to make the following change more clear. Acked-by: Alan Cox Signed-off-by: Russell King --- drivers/tty/serial/serial_core.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 0fcfd98a956..bc2065d323b 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1210,6 +1210,7 @@ static void uart_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { struct uart_state *state = tty->driver_data; + struct uart_port *uport = state->uart_port; unsigned long flags; unsigned int cflag = tty->termios.c_cflag; @@ -1232,31 +1233,31 @@ static void uart_set_termios(struct tty_struct *tty, /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) - uart_clear_mctrl(state->uart_port, TIOCM_RTS | TIOCM_DTR); + uart_clear_mctrl(uport, TIOCM_RTS | TIOCM_DTR); /* Handle transition away from B0 status */ else if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { unsigned int mask = TIOCM_DTR; if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) mask |= TIOCM_RTS; - uart_set_mctrl(state->uart_port, mask); + uart_set_mctrl(uport, mask); } /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { - spin_lock_irqsave(&state->uart_port->lock, flags); + spin_lock_irqsave(&uport->lock, flags); tty->hw_stopped = 0; __uart_start(tty); - spin_unlock_irqrestore(&state->uart_port->lock, flags); + spin_unlock_irqrestore(&uport->lock, flags); } /* Handle turning on CRTSCTS */ else if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) { - spin_lock_irqsave(&state->uart_port->lock, flags); - if (!(state->uart_port->ops->get_mctrl(state->uart_port) & TIOCM_CTS)) { + spin_lock_irqsave(&uport->lock, flags); + if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) { tty->hw_stopped = 1; - state->uart_port->ops->stop_tx(state->uart_port); + uport->ops->stop_tx(uport); } - spin_unlock_irqrestore(&state->uart_port->lock, flags); + spin_unlock_irqrestore(&uport->lock, flags); } } -- cgit v1.2.3 From 2cbacafd7af0f1cc7a433668c662a91ba6aabc1b Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 17 Apr 2012 16:34:13 +0100 Subject: SERIAL: core: add hardware assisted s/w flow control support Ports which are capable of handling s/w flow control in hardware to know when the s/w flow control termios settings are changed. Add a flag to allow the low level serial drivers to indicate that they support this, and these changes should be propagated to them. Acked-by: Alan Cox Signed-off-by: Russell King --- drivers/tty/serial/serial_core.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index bc2065d323b..bd10bbd5644 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1213,7 +1213,19 @@ static void uart_set_termios(struct tty_struct *tty, struct uart_port *uport = state->uart_port; unsigned long flags; unsigned int cflag = tty->termios.c_cflag; + unsigned int iflag_mask = IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK; + bool sw_changed = false; + /* + * Drivers doing software flow control also need to know + * about changes to these input settings. + */ + if (uport->flags & UPF_SOFT_FLOW) { + iflag_mask |= IXANY|IXON|IXOFF; + sw_changed = + tty->termios.c_cc[VSTART] != old_termios->c_cc[VSTART] || + tty->termios.c_cc[VSTOP] != old_termios->c_cc[VSTOP]; + } /* * These are the bits that are used to setup various @@ -1221,11 +1233,11 @@ static void uart_set_termios(struct tty_struct *tty, * bits in c_cflag; c_[io]speed will always be set * appropriately by set_termios() in tty_ioctl.c */ -#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) if ((cflag ^ old_termios->c_cflag) == 0 && tty->termios.c_ospeed == old_termios->c_ospeed && tty->termios.c_ispeed == old_termios->c_ispeed && - RELEVANT_IFLAG(tty->termios.c_iflag ^ old_termios->c_iflag) == 0) { + ((tty->termios.c_iflag ^ old_termios->c_iflag) & iflag_mask) == 0 && + !sw_changed) { return; } -- cgit v1.2.3 From dba05832cbe4f305dfd998fb26d7c685d91fbbd8 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 17 Apr 2012 16:41:10 +0100 Subject: SERIAL: core: add hardware assisted h/w flow control support Ports which are handling h/w flow control in hardware must not have their RTS state altered depending on the tty's hardware-stopped state. Avoid this additional logic when setting the termios state. Acked-by: Alan Cox Signed-off-by: Russell King --- drivers/tty/serial/serial_core.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index bd10bbd5644..9d8796e7718 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1255,6 +1255,13 @@ static void uart_set_termios(struct tty_struct *tty, uart_set_mctrl(uport, mask); } + /* + * If the port is doing h/w assisted flow control, do nothing. + * We assume that tty->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_irqsave(&uport->lock, flags); -- cgit v1.2.3 From 9aba8d5b011193c8e01d565c5b585df5b94f1db2 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 17 Apr 2012 17:23:14 +0100 Subject: SERIAL: core: add throttle/unthrottle callbacks for hardware assisted flow control Add two callbacks for hardware assisted flow control; we need to know when the tty layers want us to stop and restart due to their buffer levels. Call a driver specific throttle/unthrottle function if and only if the driver indicates that it is using an enabled hardware assisted flow control method, otherwise fall back to the non-hardware assisted methods. Acked-by: Alan Cox Signed-off-by: Russell King --- drivers/tty/serial/serial_core.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 9d8796e7718..098bb99c2b9 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -610,27 +610,50 @@ static void uart_send_xchar(struct tty_struct *tty, char ch) static void uart_throttle(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; + struct uart_port *port = state->uart_port; + uint32_t mask = 0; if (I_IXOFF(tty)) + mask |= UPF_SOFT_FLOW; + if (tty->termios.c_cflag & CRTSCTS) + mask |= UPF_HARD_FLOW; + + if (port->flags & mask) { + port->ops->throttle(port); + mask &= ~port->flags; + } + + if (mask & UPF_SOFT_FLOW) uart_send_xchar(tty, STOP_CHAR(tty)); - if (tty->termios.c_cflag & CRTSCTS) - uart_clear_mctrl(state->uart_port, TIOCM_RTS); + if (mask & UPF_HARD_FLOW) + uart_clear_mctrl(port, TIOCM_RTS); } static void uart_unthrottle(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; struct uart_port *port = state->uart_port; + uint32_t mask = 0; - if (I_IXOFF(tty)) { + if (I_IXOFF(tty)) + mask |= UPF_SOFT_FLOW; + if (tty->termios.c_cflag & CRTSCTS) + mask |= UPF_HARD_FLOW; + + if (port->flags & mask) { + port->ops->unthrottle(port); + mask &= ~port->flags; + } + + if (mask & UPF_SOFT_FLOW) { if (port->x_char) port->x_char = 0; else uart_send_xchar(tty, START_CHAR(tty)); } - if (tty->termios.c_cflag & CRTSCTS) + if (mask & UPF_HARD_FLOW) uart_set_mctrl(port, TIOCM_RTS); } -- cgit v1.2.3 From 0d5b16639523970db001e6af298e360782f4d89a Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 5 Oct 2012 23:48:28 +0100 Subject: SERIAL: omap: allow hardware assisted rts/cts modes to be disabled There is nothing which clears the auto RTS/CTS bits, so once hardware flow control gets enabled, there's no possibility to disable it. So, clear these bits when CRTSCTS is cleared. Signed-off-by: Russell King --- drivers/tty/serial/omap-serial.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 6d3d26a607b..b6a1925dfc9 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -921,6 +921,13 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); serial_out(up, UART_MCR, up->mcr | UART_MCR_RTS); serial_out(up, UART_LCR, cval); + } else { + /* Disable AUTORTS and AUTOCTS */ + up->efr &= ~(UART_EFR_CTS | UART_EFR_RTS); + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, up->efr); + serial_out(up, UART_LCR, cval); } serial_omap_set_mctrl(&up->port, up->port.mctrl); -- cgit v1.2.3 From da5d01f23bc1ff710797a563ca3616df95395a12 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 6 Oct 2012 00:11:16 +0100 Subject: SERIAL: omap: allow hardware assisted IXANY mode to be disabled Nothing was clearing the UART_MCR_XONANY bit, so once the ixany mode gets set, there's no possibility to disable it. Clear this bit when IXANY mode is cleared. Signed-off-by: Russell King --- drivers/tty/serial/omap-serial.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index b6a1925dfc9..f5b2f395af2 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -698,6 +698,8 @@ serial_omap_configure_xonxoff */ if (termios->c_iflag & IXANY) up->mcr |= UART_MCR_XONANY; + else + up->mcr &= ~UART_MCR_XONANY; serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); -- cgit v1.2.3 From d864c03bfce2449703c7f0544dac3fbaed347239 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 6 Oct 2012 00:51:17 +0100 Subject: SERIAL: omap: remove setting of EFR SCD bit The SCD (special character detect) bit enables comparisons with XOFF2, which we do not program. As the XOFF2 character remains unprogrammed, there's little point enabling this feature along with its associated interrupt. Remove this, and ensure that the SCD bit is cleared. Signed-off-by: Russell King --- drivers/tty/serial/omap-serial.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index f5b2f395af2..71f968b5504 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -704,13 +704,8 @@ serial_omap_configure_xonxoff serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); - /* Enable special char function UARTi.EFR_REG[5] and - * load the new software flow control mode IXON or IXOFF - * and restore the UARTi.EFR_REG[4] ENHANCED_EN value. - */ - serial_out(up, UART_EFR, up->efr | UART_EFR_SCD); + serial_out(up, UART_EFR, up->efr); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); - serial_out(up, UART_MCR, up->mcr & ~UART_MCR_TCRTLR); serial_out(up, UART_LCR, up->lcr); } @@ -843,6 +838,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); up->efr = serial_in(up, UART_EFR); + up->efr &= ~UART_EFR_SCD; serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); -- cgit v1.2.3 From 511e74f3305e9fce1410f2205084fd67c50f81bc Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 5 Oct 2012 15:13:56 +0100 Subject: SERIAL: omap: no need to re-read EFR There's no need to re-read EFR after we've recently written it; the register is a configuration register which doesn't change its value without us writing to it. The last value which was written to this register was up->efr. Removing this re-reading avoids the possibility that we end up with up->efr having unintended bits set, which should only be temporarily set when accessing the enhanced features. Signed-off-by: Russell King --- drivers/tty/serial/omap-serial.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 71f968b5504..6ef4cc49d81 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -873,8 +873,6 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, serial_out(up, UART_OMAP_MDR1, up->mdr1); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - - up->efr = serial_in(up, UART_EFR); serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); serial_out(up, UART_LCR, 0); -- cgit v1.2.3 From 9363f8fa8930db6383b4089036799a276257fdb7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 5 Oct 2012 12:23:28 +0100 Subject: SERIAL: omap: fix set_mctrl() breakage c538d20c7f (and maybe previous commits) broke set_mctrl() by making it only capable of setting bits in the MCR register. This prevents software controlled flow control and modem control line manipulation via TIOCMSET/TIOCMBIC from working correctly. Signed-off-by: Russell King --- drivers/tty/serial/omap-serial.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 6ef4cc49d81..fb06def4d98 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -504,7 +504,7 @@ static unsigned int serial_omap_get_mctrl(struct uart_port *port) static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl) { struct uart_omap_port *up = to_uart_omap_port(port); - unsigned char mcr = 0; + unsigned char mcr = 0, old_mcr; dev_dbg(up->port.dev, "serial_omap_set_mctrl+%d\n", up->port.line); if (mctrl & TIOCM_RTS) @@ -519,8 +519,10 @@ static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl) mcr |= UART_MCR_LOOP; pm_runtime_get_sync(up->dev); - up->mcr = serial_in(up, UART_MCR); - up->mcr |= mcr; + old_mcr = serial_in(up, UART_MCR); + old_mcr &= ~(UART_MCR_LOOP | UART_MCR_OUT2 | UART_MCR_OUT1 | + UART_MCR_DTR | UART_MCR_RTS); + up->mcr = old_mcr | mcr; serial_out(up, UART_MCR, up->mcr); pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); -- cgit v1.2.3 From 08bd4903c20d9d4bea64b1751386a478faa24ab0 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 5 Oct 2012 13:54:53 +0100 Subject: SERIAL: omap: fix MCR TCRTLR bit handling The MCR TCRTLR bit can only be changed when ECB is set in the EFR. Unfortunately, several places were trying to alter this bit while ECB was clear: - serial_omap_configure_xonxoff() was attempting to clear the bit after explicitly clearing the ECB bit. - serial_omap_set_termios() was trying the same trick after setting the SCR, and when trying to change the TCR register when hardware flow control was enabled. Fix this by ensuring that we always have ECB set whenever the TCRTLR bit is changed. Moreover, we start out by reading the EFR and MCR registers, which may have indeterminent bit settings for the ECB and TCRTLR bits. Ensure that these bits always start off in a known state. In order to avoid any undesired behaviour appearing through fixing this, we also ensure that hardware assisted flow control is disabled while new driver specific parts are not in place. Signed-off-by: Russell King --- drivers/tty/serial/omap-serial.c | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index fb06def4d98..170be2b1cdb 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -706,9 +706,10 @@ serial_omap_configure_xonxoff serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); - serial_out(up, UART_EFR, up->efr); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); serial_out(up, UART_MCR, up->mcr & ~UART_MCR_TCRTLR); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, up->efr); serial_out(up, UART_LCR, up->lcr); } @@ -729,7 +730,6 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, { struct uart_omap_port *up = to_uart_omap_port(port); unsigned char cval = 0; - unsigned char efr = 0; unsigned long flags = 0; unsigned int baud, quot; @@ -839,12 +839,12 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - up->efr = serial_in(up, UART_EFR); + up->efr = serial_in(up, UART_EFR) & ~UART_EFR_ECB; up->efr &= ~UART_EFR_SCD; serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); - up->mcr = serial_in(up, UART_MCR); + up->mcr = serial_in(up, UART_MCR) & ~UART_MCR_TCRTLR; serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); /* FIFO ENABLE, DMA MODE */ @@ -863,9 +863,12 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, serial_out(up, UART_OMAP_SCR, up->scr); - serial_out(up, UART_EFR, up->efr); + /* Reset UART_MCR_TCRTLR: this must be done with the EFR_ECB bit set */ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); serial_out(up, UART_MCR, up->mcr); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, up->efr); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); /* Protocol, Baud Rate, and Interrupt Settings */ @@ -903,21 +906,22 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, /* Hardware Flow Control Configuration */ - if (termios->c_cflag & CRTSCTS) { - efr |= (UART_EFR_CTS | UART_EFR_RTS); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); - - up->mcr = serial_in(up, UART_MCR); - serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); - + if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) { + /* Enable access to TCR/TLR */ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - up->efr = serial_in(up, UART_EFR); serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); - serial_out(up, UART_EFR, efr); /* Enable AUTORTS and AUTOCTS */ - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + + /* Enable AUTORTS and AUTOCTS */ + up->efr |= UART_EFR_CTS | UART_EFR_RTS; + + /* Disable access to TCR/TLR */ serial_out(up, UART_MCR, up->mcr | UART_MCR_RTS); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, up->efr); serial_out(up, UART_LCR, cval); } else { /* Disable AUTORTS and AUTOCTS */ @@ -930,7 +934,8 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, serial_omap_set_mctrl(&up->port, up->port.mctrl); /* Software Flow Control Configuration */ - serial_omap_configure_xonxoff(up, termios); + if (up->port.flags & UPF_SOFT_FLOW) + serial_omap_configure_xonxoff(up, termios); spin_unlock_irqrestore(&up->port.lock, flags); pm_runtime_mark_last_busy(up->dev); -- cgit v1.2.3 From 4073a53b36ff993f7c4d158d1cf30d93c7d12add Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 15 Oct 2012 15:21:46 +0100 Subject: SERIAL: omap: remove 'irq_pending' bitfield irq_pending is never used, so let's remove it. It seems to be result of a bad rebase of d37c6cebcb0c (serial: omap: move uart_omap_port definition to C file) Signed-off-by: Russell King --- drivers/tty/serial/omap-serial.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 170be2b1cdb..da46be3d57b 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -99,7 +99,6 @@ struct uart_omap_port { u32 context_loss_cnt; u32 errata; u8 wakeups_enabled; - unsigned int irq_pending:1; int DTR_gpio; int DTR_inverted; -- cgit v1.2.3 From f91b55ab72a913a8a61e377a7766772a20f0d96b Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 6 Oct 2012 10:50:58 +0100 Subject: SERIAL: omap: move driver private definitions and structures to driver struct uart_omap_port and struct uart_omap_dma, and associated definitions are private to the driver, so there's no point them sitting in an include file under arch/arm. Move them into the driver itself. Signed-off-by: Russell King --- drivers/tty/serial/omap-serial.c | 52 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index da46be3d57b..17babde8feb 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -44,6 +44,8 @@ #include +#define OMAP_MAX_HSUART_PORTS 6 + #define UART_BUILD_REVISION(x, y) (((x) << 8) | (y)) #define OMAP_UART_REV_42 0x0402 @@ -51,10 +53,14 @@ #define OMAP_UART_REV_52 0x0502 #define OMAP_UART_REV_63 0x0603 +#define UART_ERRATA_i202_MDR1_ACCESS BIT(0) +#define UART_ERRATA_i291_DMA_FORCEIDLE BIT(1) + #define DEFAULT_CLK_SPEED 48000000 /* 48Mhz*/ /* SCR register bitmasks */ #define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK (1 << 7) +#define OMAP_UART_SCR_TX_EMPTY (1 << 3) /* FCR register bitmasks */ #define OMAP_UART_FCR_RX_FIFO_TRIG_MASK (0x3 << 6) @@ -71,6 +77,52 @@ #define OMAP_UART_MVR_MAJ_SHIFT 8 #define OMAP_UART_MVR_MIN_MASK 0x3f +#define OMAP_UART_DMA_CH_FREE -1 + +#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA +#define OMAP_MODE13X_SPEED 230400 + +/* WER = 0x7F + * Enable module level wakeup in WER reg + */ +#define OMAP_UART_WER_MOD_WKUP 0X7F + +/* Enable XON/XOFF flow control on output */ +#define OMAP_UART_SW_TX 0x4 + +/* Enable XON/XOFF flow control on input */ +#define OMAP_UART_SW_RX 0x4 + +#define OMAP_UART_SW_CLR 0xF0 + +#define OMAP_UART_TCR_TRIG 0x0F + +struct uart_omap_dma { + u8 uart_dma_tx; + u8 uart_dma_rx; + int rx_dma_channel; + int tx_dma_channel; + dma_addr_t rx_buf_dma_phys; + dma_addr_t tx_buf_dma_phys; + unsigned int uart_base; + /* + * Buffer for rx dma.It is not required for tx because the buffer + * comes from port structure. + */ + unsigned char *rx_buf; + unsigned int prev_rx_dma_pos; + int tx_buf_size; + int tx_dma_used; + int rx_dma_used; + spinlock_t tx_lock; + spinlock_t rx_lock; + /* timer to poll activity on rx dma */ + struct timer_list rx_timer; + unsigned int rx_buf_size; + unsigned int rx_poll_rate; + unsigned int rx_timeout; +}; + struct uart_omap_port { struct uart_port port; struct uart_omap_dma uart_dma; -- cgit v1.2.3 From 820344fe3de78e9fdc7691cd6076703683f5a6f4 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 5 Oct 2012 22:26:06 +0100 Subject: SERIAL: omap: configure xon/xoff before setting modem control lines Signed-off-by: Russell King --- drivers/tty/serial/omap-serial.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 17babde8feb..9f54cef5676 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -983,11 +983,12 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, serial_out(up, UART_LCR, cval); } - serial_omap_set_mctrl(&up->port, up->port.mctrl); /* Software Flow Control Configuration */ if (up->port.flags & UPF_SOFT_FLOW) serial_omap_configure_xonxoff(up, termios); + serial_omap_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); -- cgit v1.2.3 From 01d70bb37ccb74a1b5c9e3e08c9a69eacc8d84f4 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 15 Oct 2012 16:50:59 +0100 Subject: SERIAL: omap: serial_omap_configure_xonxoff() contents into set_termios Signed-off-by: Russell King --- drivers/tty/serial/omap-serial.c | 112 ++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 59 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 9f54cef5676..4888bd1ab32 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -707,63 +707,6 @@ static void serial_omap_shutdown(struct uart_port *port) free_irq(up->port.irq, up); } -static inline void -serial_omap_configure_xonxoff - (struct uart_omap_port *up, struct ktermios *termios) -{ - up->lcr = serial_in(up, UART_LCR); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - up->efr = serial_in(up, UART_EFR); - serial_out(up, UART_EFR, up->efr & ~UART_EFR_ECB); - - serial_out(up, UART_XON1, termios->c_cc[VSTART]); - serial_out(up, UART_XOFF1, termios->c_cc[VSTOP]); - - /* clear SW control mode bits */ - up->efr &= OMAP_UART_SW_CLR; - - /* - * IXON Flag: - * Enable XON/XOFF flow control on output. - * Transmit XON1, XOFF1 - */ - if (termios->c_iflag & IXON) - up->efr |= OMAP_UART_SW_TX; - - /* - * IXOFF Flag: - * Enable XON/XOFF flow control on input. - * Receiver compares XON1, XOFF1. - */ - if (termios->c_iflag & IXOFF) - up->efr |= OMAP_UART_SW_RX; - - serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); - - up->mcr = serial_in(up, UART_MCR); - - /* - * IXANY Flag: - * Enable any character to restart output. - * Operation resumes after receiving any - * character after recognition of the XOFF character - */ - if (termios->c_iflag & IXANY) - up->mcr |= UART_MCR_XONANY; - else - up->mcr &= ~UART_MCR_XONANY; - - serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); - serial_out(up, UART_MCR, up->mcr & ~UART_MCR_TCRTLR); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - serial_out(up, UART_EFR, up->efr); - serial_out(up, UART_LCR, up->lcr); -} - static void serial_omap_uart_qos_work(struct work_struct *work) { struct uart_omap_port *up = container_of(work, struct uart_omap_port, @@ -984,8 +927,59 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, } /* Software Flow Control Configuration */ - if (up->port.flags & UPF_SOFT_FLOW) - serial_omap_configure_xonxoff(up, termios); + if (up->port.flags & UPF_SOFT_FLOW) { + up->lcr = serial_in(up, UART_LCR); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + up->efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, up->efr & ~UART_EFR_ECB); + + serial_out(up, UART_XON1, termios->c_cc[VSTART]); + serial_out(up, UART_XOFF1, termios->c_cc[VSTOP]); + + /* clear SW control mode bits */ + up->efr &= OMAP_UART_SW_CLR; + + /* + * IXON Flag: + * Enable XON/XOFF flow control on output. + * Transmit XON1, XOFF1 + */ + if (termios->c_iflag & IXON) + up->efr |= OMAP_UART_SW_TX; + + /* + * IXOFF Flag: + * Enable XON/XOFF flow control on input. + * Receiver compares XON1, XOFF1. + */ + if (termios->c_iflag & IXOFF) + up->efr |= OMAP_UART_SW_RX; + + serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + + up->mcr = serial_in(up, UART_MCR); + + /* + * IXANY Flag: + * Enable any character to restart output. + * Operation resumes after receiving any + * character after recognition of the XOFF character + */ + if (termios->c_iflag & IXANY) + up->mcr |= UART_MCR_XONANY; + else + up->mcr &= ~UART_MCR_XONANY; + + serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + serial_out(up, UART_MCR, up->mcr & ~UART_MCR_TCRTLR); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, up->efr); + serial_out(up, UART_LCR, up->lcr); + } serial_omap_set_mctrl(&up->port, up->port.mctrl); -- cgit v1.2.3 From 1fe8aa8803536b8030375525a07a152ba8f15363 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 6 Oct 2012 09:04:03 +0100 Subject: SERIAL: omap: don't read back LCR/MCR/EFR There's really no reason to read back these registers while setting the termios modes, provided we keep our cached copies up to date. Remove these readbacks. This has the benefit that we know that the EFR_ECB and MCR_TCRTLR bits will always be clear, so we don't need to keep masking these bits throughout the code. Signed-off-by: Russell King --- drivers/tty/serial/omap-serial.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 4888bd1ab32..6d588e20c64 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -912,8 +912,11 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, /* Enable AUTORTS and AUTOCTS */ up->efr |= UART_EFR_CTS | UART_EFR_RTS; + /* Ensure MCR RTS is asserted */ + up->mcr |= UART_MCR_RTS; + /* Disable access to TCR/TLR */ - serial_out(up, UART_MCR, up->mcr | UART_MCR_RTS); + serial_out(up, UART_MCR, up->mcr); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); serial_out(up, UART_EFR, up->efr); serial_out(up, UART_LCR, cval); @@ -928,10 +931,8 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, /* Software Flow Control Configuration */ if (up->port.flags & UPF_SOFT_FLOW) { - up->lcr = serial_in(up, UART_LCR); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - up->efr = serial_in(up, UART_EFR); - serial_out(up, UART_EFR, up->efr & ~UART_EFR_ECB); + serial_out(up, UART_EFR, up->efr); serial_out(up, UART_XON1, termios->c_cc[VSTART]); serial_out(up, UART_XOFF1, termios->c_cc[VSTOP]); @@ -958,8 +959,6 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); - up->mcr = serial_in(up, UART_MCR); - /* * IXANY Flag: * Enable any character to restart output. @@ -975,7 +974,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); - serial_out(up, UART_MCR, up->mcr & ~UART_MCR_TCRTLR); + serial_out(up, UART_MCR, up->mcr); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); serial_out(up, UART_EFR, up->efr); serial_out(up, UART_LCR, up->lcr); -- cgit v1.2.3 From 18f360f865cffde44b112577fed1a6a0dd2740dc Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 6 Oct 2012 09:08:20 +0100 Subject: SERIAL: omap: simplify We have the sequence: - LCR mode B - write EFR with ECB clear - LCR mode normal - if s/w flow - LCR mode B - write EFR with ECB clear ... - LCR mode B - write EFR with ECB clear - LCR mode normal This can be simplified to: - if s/w flow - LCR mode B - write EFR with ECB clear ... - LCR mode B - write EFR with ECB clear - LCR mode normal Signed-off-by: Russell King --- drivers/tty/serial/omap-serial.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 6d588e20c64..fbce4c2e55c 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -917,19 +917,11 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, /* Disable access to TCR/TLR */ serial_out(up, UART_MCR, up->mcr); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - serial_out(up, UART_EFR, up->efr); - serial_out(up, UART_LCR, cval); } else { /* Disable AUTORTS and AUTOCTS */ up->efr &= ~(UART_EFR_CTS | UART_EFR_RTS); - - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - serial_out(up, UART_EFR, up->efr); - serial_out(up, UART_LCR, cval); } - /* Software Flow Control Configuration */ if (up->port.flags & UPF_SOFT_FLOW) { serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); serial_out(up, UART_EFR, up->efr); @@ -975,11 +967,12 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); serial_out(up, UART_MCR, up->mcr); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - serial_out(up, UART_EFR, up->efr); - serial_out(up, UART_LCR, up->lcr); } + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, up->efr); + serial_out(up, UART_LCR, up->lcr); + serial_omap_set_mctrl(&up->port, up->port.mctrl); spin_unlock_irqrestore(&up->port.lock, flags); -- cgit v1.2.3 From c7d059cae31f328bbe2be6ab737226d338f22486 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 6 Oct 2012 09:12:44 +0100 Subject: SERIAL: omap: always set TCR We always setup the TCR register in the software flow control path, and when hardware flow control is enabled. Remove this redundant setup, and place it before we setup any hardware flow control. Signed-off-by: Russell King --- drivers/tty/serial/omap-serial.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index fbce4c2e55c..7180ffc847e 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -898,31 +898,30 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, else serial_out(up, UART_OMAP_MDR1, up->mdr1); - /* Hardware Flow Control Configuration */ + /* Enable access to TCR/TLR */ + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); - if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) { - /* Enable access to TCR/TLR */ - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); - serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); + serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); - serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); + /* Hardware Flow Control Configuration */ + if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) { /* Enable AUTORTS and AUTOCTS */ up->efr |= UART_EFR_CTS | UART_EFR_RTS; /* Ensure MCR RTS is asserted */ up->mcr |= UART_MCR_RTS; - - /* Disable access to TCR/TLR */ - serial_out(up, UART_MCR, up->mcr); } else { /* Disable AUTORTS and AUTOCTS */ up->efr &= ~(UART_EFR_CTS | UART_EFR_RTS); } if (up->port.flags & UPF_SOFT_FLOW) { + /* Disable access to TCR/TLR */ + serial_out(up, UART_MCR, up->mcr); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); serial_out(up, UART_EFR, up->efr); @@ -961,14 +960,9 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, up->mcr |= UART_MCR_XONANY; else up->mcr &= ~UART_MCR_XONANY; - - serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); - serial_out(up, UART_MCR, up->mcr); } + serial_out(up, UART_MCR, up->mcr); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); serial_out(up, UART_EFR, up->efr); serial_out(up, UART_LCR, up->lcr); -- cgit v1.2.3 From c533e51b228020142cd2c4d5f21e8db4683ce457 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 6 Oct 2012 09:34:36 +0100 Subject: SERIAL: omap: move xon/xoff setting earlier Take advantage of the switch to mode B for accessing the TCR register, and move the xon/xoff configuration there. This allows further simplication of this sequence. Signed-off-by: Russell King --- drivers/tty/serial/omap-serial.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 7180ffc847e..0d2671e66da 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -898,16 +898,20 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, else serial_out(up, UART_OMAP_MDR1, up->mdr1); - /* Enable access to TCR/TLR */ + /* Configure flow control */ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + /* XON1/XOFF1 accessible mode B, TCRTLR=0, ECB=0 */ + serial_out(up, UART_XON1, termios->c_cc[VSTART]); + serial_out(up, UART_XOFF1, termios->c_cc[VSTOP]); + + /* Enable access to TCR/TLR */ serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); - /* Hardware Flow Control Configuration */ - if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) { /* Enable AUTORTS and AUTOCTS */ up->efr |= UART_EFR_CTS | UART_EFR_RTS; @@ -925,9 +929,6 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); serial_out(up, UART_EFR, up->efr); - serial_out(up, UART_XON1, termios->c_cc[VSTART]); - serial_out(up, UART_XOFF1, termios->c_cc[VSTOP]); - /* clear SW control mode bits */ up->efr &= OMAP_UART_SW_CLR; -- cgit v1.2.3 From 2405464083e152f88bb58b7108a9e50ca362178c Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 6 Oct 2012 09:36:47 +0100 Subject: SERIAL: omap: simplify (2) Simplify: - set ECB ... - LCR mode A - clear TCRTLR - LCR mode B - clear ECB - set ECB and update other bits - LCR mode A - update XONANY to: - set ECB ... - LCR mode B - set ECB and update other bits - LCR mode A - update XONANY and clear TCRTLR Signed-off-by: Russell King --- drivers/tty/serial/omap-serial.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 0d2671e66da..156a8543855 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -924,11 +924,6 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, } if (up->port.flags & UPF_SOFT_FLOW) { - /* Disable access to TCR/TLR */ - serial_out(up, UART_MCR, up->mcr); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - serial_out(up, UART_EFR, up->efr); - /* clear SW control mode bits */ up->efr &= OMAP_UART_SW_CLR; @@ -948,9 +943,6 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, if (termios->c_iflag & IXOFF) up->efr |= OMAP_UART_SW_RX; - serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); - /* * IXANY Flag: * Enable any character to restart output. @@ -962,7 +954,6 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, else up->mcr &= ~UART_MCR_XONANY; } - serial_out(up, UART_MCR, up->mcr); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); serial_out(up, UART_EFR, up->efr); -- cgit v1.2.3 From 3af08bd7adb09b0806c51c06b87db08cc7075568 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 5 Oct 2012 13:32:08 +0100 Subject: 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 --- drivers/tty/serial/omap-serial.c | 47 +++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 156a8543855..4dbbc00c826 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -88,10 +88,10 @@ #define OMAP_UART_WER_MOD_WKUP 0X7F /* 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 */ -#define OMAP_UART_SW_RX 0x4 +#define OMAP_UART_SW_RX 0x02 #define OMAP_UART_SW_CLR 0xF0 @@ -354,6 +354,34 @@ static void serial_omap_start_tx(struct uart_port *port) 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) { unsigned int status; @@ -929,19 +957,19 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, /* * IXON Flag: - * Enable XON/XOFF flow control on output. - * Transmit XON1, XOFF1 + * Enable XON/XOFF flow control on input. + * Receiver compares XON1, XOFF1. */ if (termios->c_iflag & IXON) - up->efr |= OMAP_UART_SW_TX; + up->efr |= OMAP_UART_SW_RX; /* * IXOFF Flag: - * Enable XON/XOFF flow control on input. - * Receiver compares XON1, XOFF1. + * Enable XON/XOFF flow control on output. + * Transmit XON1, XOFF1 */ if (termios->c_iflag & IXOFF) - up->efr |= OMAP_UART_SW_RX; + up->efr |= OMAP_UART_SW_TX; /* * IXANY Flag: @@ -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", up->port.line); up->port.type = PORT_OMAP; + up->port.flags |= UPF_SOFT_FLOW | UPF_HARD_FLOW; } static int @@ -1228,6 +1257,8 @@ static struct uart_ops serial_omap_pops = { .get_mctrl = serial_omap_get_mctrl, .stop_tx = serial_omap_stop_tx, .start_tx = serial_omap_start_tx, + .throttle = serial_omap_throttle, + .unthrottle = serial_omap_unthrottle, .stop_rx = serial_omap_stop_rx, .enable_ms = serial_omap_enable_ms, .break_ctl = serial_omap_break_ctl, -- cgit v1.2.3