Skip to content

Commit

Permalink
SERIAL: omap: fix MCR TCRTLR bit handling
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Russell King committed Nov 4, 2012
1 parent 9363f8f commit 08bd490
Showing 1 changed file with 21 additions and 16 deletions.
37 changes: 21 additions & 16 deletions drivers/tty/serial/omap-serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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;

Expand Down Expand Up @@ -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 */

Expand All @@ -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 */

Expand Down Expand Up @@ -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 */
Expand All @@ -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);
Expand Down

0 comments on commit 08bd490

Please sign in to comment.