Skip to content

Commit 1cf6e8f

Browse files
Cyrille Pitchengregkh
authored andcommitted
tty/serial: at91: fix RTS line management when hardware handshake is enabled
This patch fixes many bugs in the code dealing with the hardware handshake. As an example, in atmel_set_termios(), we used to test whether the CRTSCTS c_cflag was set. If so, we selected the "Hardware Handshake" mode through the Mode Register. However, few lines below the mode was reset to "Normal" (0). So there was no way to select the "Hardware Handshake" mode. To fix this issue, we moved the CRTSCRTS c_cflag test AFTER the mode has been reset to "Normal". Also setting the RTSEN and RTSDIS bits in the Control Register has different results whether the USART is set in "Normal" or "Hardware Handshake" mode: 1) "Normal" mode - the RTSEN bit forces the RTS line to low level, which tells the remote peer that we are ready to received new data. - the RTSDIS bit forces the RTS line to high level, which tells the remote peer to stop sending new data. 2) "Hardware Handshake" mode - the RTSEN bit forces the RTS line to high level. - the RTSDIS bit lets the hardware control the RTS line. WARNING: when FIFOs are not available or not enabled, the RTS line is controlled by the PDC. This is why using the Hardware Handshake mode requires using the PDC channel for reception. However the Hardware Handshake mode DOES NOT work with DMA controller since it cannot control the RTS line. Future designs with FIFOs will introduce a new feature: the RTS line will be controlled by the RX FIFO using thresholds. This patch was tested with this new design. Signed-off-by: Cyrille Pitchen <[email protected]> Acked-by: Nicolas Ferre <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 6fbb9bd commit 1cf6e8f

File tree

1 file changed

+61
-30
lines changed

1 file changed

+61
-30
lines changed

drivers/tty/serial/atmel_serial.c

Lines changed: 61 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -341,13 +341,37 @@ static u_int atmel_tx_empty(struct uart_port *port)
341341
static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
342342
{
343343
unsigned int control = 0;
344-
unsigned int mode;
344+
unsigned int mode = UART_GET_MR(port);
345+
unsigned int rts_paused, rts_ready;
345346
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
346347

348+
/* override mode to RS485 if needed, otherwise keep the current mode */
349+
if (port->rs485.flags & SER_RS485_ENABLED) {
350+
if ((port->rs485.delay_rts_after_send) > 0)
351+
UART_PUT_TTGR(port, port->rs485.delay_rts_after_send);
352+
mode &= ~ATMEL_US_USMODE;
353+
mode |= ATMEL_US_USMODE_RS485;
354+
}
355+
356+
/* set the RTS line state according to the mode */
357+
if ((mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_HWHS) {
358+
/* force RTS line to high level */
359+
rts_paused = ATMEL_US_RTSEN;
360+
361+
/* give the control of the RTS line back to the hardware */
362+
rts_ready = ATMEL_US_RTSDIS;
363+
} else {
364+
/* force RTS line to high level */
365+
rts_paused = ATMEL_US_RTSDIS;
366+
367+
/* force RTS line to low level */
368+
rts_ready = ATMEL_US_RTSEN;
369+
}
370+
347371
if (mctrl & TIOCM_RTS)
348-
control |= ATMEL_US_RTSEN;
372+
control |= rts_ready;
349373
else
350-
control |= ATMEL_US_RTSDIS;
374+
control |= rts_paused;
351375

352376
if (mctrl & TIOCM_DTR)
353377
control |= ATMEL_US_DTREN;
@@ -359,23 +383,12 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
359383
mctrl_gpio_set(atmel_port->gpios, mctrl);
360384

361385
/* Local loopback mode? */
362-
mode = UART_GET_MR(port) & ~ATMEL_US_CHMODE;
386+
mode &= ~ATMEL_US_CHMODE;
363387
if (mctrl & TIOCM_LOOP)
364388
mode |= ATMEL_US_CHMODE_LOC_LOOP;
365389
else
366390
mode |= ATMEL_US_CHMODE_NORMAL;
367391

368-
/* Resetting serial mode to RS232 (0x0) */
369-
mode &= ~ATMEL_US_USMODE;
370-
371-
if (port->rs485.flags & SER_RS485_ENABLED) {
372-
dev_dbg(port->dev, "Setting UART to RS485\n");
373-
if ((port->rs485.delay_rts_after_send) > 0)
374-
UART_PUT_TTGR(port, port->rs485.delay_rts_after_send);
375-
mode |= ATMEL_US_USMODE_RS485;
376-
} else {
377-
dev_dbg(port->dev, "Setting UART to RS232\n");
378-
}
379392
UART_PUT_MR(port, mode);
380393
}
381394

@@ -1921,12 +1934,14 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
19211934
struct ktermios *old)
19221935
{
19231936
unsigned long flags;
1924-
unsigned int mode, imr, quot, baud;
1937+
unsigned int old_mode, mode, imr, quot, baud;
1938+
1939+
/* save the current mode register */
1940+
mode = old_mode = UART_GET_MR(port);
19251941

1926-
/* Get current mode register */
1927-
mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL
1928-
| ATMEL_US_NBSTOP | ATMEL_US_PAR
1929-
| ATMEL_US_USMODE);
1942+
/* reset the mode, clock divisor, parity, stop bits and data size */
1943+
mode &= ~(ATMEL_US_USCLKS | ATMEL_US_CHRL | ATMEL_US_NBSTOP |
1944+
ATMEL_US_PAR | ATMEL_US_USMODE);
19301945

19311946
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
19321947
quot = uart_get_divisor(port, baud);
@@ -1971,12 +1986,6 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
19711986
} else
19721987
mode |= ATMEL_US_PAR_NONE;
19731988

1974-
/* hardware handshake (RTS/CTS) */
1975-
if (termios->c_cflag & CRTSCTS)
1976-
mode |= ATMEL_US_USMODE_HWHS;
1977-
else
1978-
mode |= ATMEL_US_USMODE_NORMAL;
1979-
19801989
spin_lock_irqsave(&port->lock, flags);
19811990

19821991
port->read_status_mask = ATMEL_US_OVRE;
@@ -2020,18 +2029,40 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
20202029
/* disable receiver and transmitter */
20212030
UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
20222031

2023-
/* Resetting serial mode to RS232 (0x0) */
2024-
mode &= ~ATMEL_US_USMODE;
2025-
2032+
/* mode */
20262033
if (port->rs485.flags & SER_RS485_ENABLED) {
20272034
if ((port->rs485.delay_rts_after_send) > 0)
20282035
UART_PUT_TTGR(port, port->rs485.delay_rts_after_send);
20292036
mode |= ATMEL_US_USMODE_RS485;
2037+
} else if (termios->c_cflag & CRTSCTS) {
2038+
/* RS232 with hardware handshake (RTS/CTS) */
2039+
mode |= ATMEL_US_USMODE_HWHS;
2040+
} else {
2041+
/* RS232 without hadware handshake */
2042+
mode |= ATMEL_US_USMODE_NORMAL;
20302043
}
20312044

2032-
/* set the parity, stop bits and data size */
2045+
/* set the mode, clock divisor, parity, stop bits and data size */
20332046
UART_PUT_MR(port, mode);
20342047

2048+
/*
2049+
* when switching the mode, set the RTS line state according to the
2050+
* new mode, otherwise keep the former state
2051+
*/
2052+
if ((old_mode & ATMEL_US_USMODE) != (mode & ATMEL_US_USMODE)) {
2053+
unsigned int rts_state;
2054+
2055+
if ((mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_HWHS) {
2056+
/* let the hardware control the RTS line */
2057+
rts_state = ATMEL_US_RTSDIS;
2058+
} else {
2059+
/* force RTS line to low level */
2060+
rts_state = ATMEL_US_RTSEN;
2061+
}
2062+
2063+
UART_PUT_CR(port, rts_state);
2064+
}
2065+
20352066
/* set the baud rate */
20362067
UART_PUT_BRGR(port, quot);
20372068
UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);

0 commit comments

Comments
 (0)