summaryrefslogtreecommitdiff
path: root/drivers/tty/serial/omap-serial.c
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2012-10-05 13:54:53 +0100
committerRussell King <rmk+kernel@arm.linux.org.uk>2012-11-04 11:54:31 +0000
commit08bd4903c20d9d4bea64b1751386a478faa24ab0 (patch)
tree79659e2199785262744720805ba6ca641df893cb /drivers/tty/serial/omap-serial.c
parent9363f8fa8930db6383b4089036799a276257fdb7 (diff)
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 <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'drivers/tty/serial/omap-serial.c')
-rw-r--r--drivers/tty/serial/omap-serial.c37
1 files changed, 21 insertions, 16 deletions
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index fb06def4d982..170be2b1cdb1 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);