This patch allows RS485 Half Duplex (ATMEL_US_USMODE_RS485 is half duplex) to work even if handshaking is turned on. This mimics the behavior of the EXAR USB/Serial driver known as Vizzini. It is impossible to use handshaking when using ATMEL_US_USMODE_RS485. This provides compatibility between MTCDT-0.1 and MTCDT-0.2. Note that frequently the state of the driver will be stopped if handshaking was turned on before RS485 was set. We need to take steps to enable transmit and receive when we go to RS485 mode, or the driver will be blocked. ======================================================================================= diff -Naru orig/drivers/tty/serial/atmel_serial.c new/drivers/tty/serial/atmel_serial.c --- orig/drivers/tty/serial/atmel_serial.c 2021-11-30 14:54:49.313993839 -0600 +++ new/drivers/tty/serial/atmel_serial.c 2021-11-30 16:14:58.430090773 -0600 @@ -74,6 +74,7 @@ static void atmel_start_rx(struct uart_port *port); static void atmel_stop_rx(struct uart_port *port); +static void atmel_start_tx(struct uart_port *port); #ifdef CONFIG_SERIAL_ATMEL_TTYAT @@ -357,6 +358,7 @@ { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); unsigned int mode; + unsigned int sav_c_cflag = 0; /* Disable interrupts */ atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask); @@ -369,11 +371,33 @@ port->rs485 = *rs485conf; if (rs485conf->flags & SER_RS485_ENABLED) { - dev_dbg(port->dev, "Setting UART to RS485\n"); - atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; - atmel_uart_writel(port, ATMEL_US_TTGR, + struct uart_state *state_p = port->state; + struct tty_struct *tty_struct_p = NULL; + + dev_dbg(port->dev, "Setting UART to RS485\n"); + if (state_p) + tty_struct_p = state_p->port.tty; + + if(tty_struct_p) { + struct ktermios *termios = &(tty_struct_p->termios); + sav_c_cflag = termios->c_cflag; + termios->c_cflag &= ~CRTSCTS; + } + + atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; + atmel_uart_writel(port, ATMEL_US_TTGR, rs485conf->delay_rts_after_send); + dev_dbg(port->dev, + "atmel_config_rs485: orig: c_cflag 0x%x port status 0x%x, hw_stopped %d\n", + sav_c_cflag,port->status,port->hw_stopped + ); + + port->status &= ~UPSTAT_CTS_ENABLE; mode |= ATMEL_US_USMODE_RS485; + port->hw_stopped = 0; // No longer possible + if (atmel_port->tx_stopped == true) + atmel_start_tx(port); + atmel_start_rx(port); } else { dev_dbg(port->dev, "Setting UART to RS232\n"); if (atmel_use_pdc_tx(port)) @@ -413,13 +437,36 @@ unsigned int mode = atmel_uart_readl(port, ATMEL_US_MR); unsigned int rts_paused, rts_ready; struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + unsigned int sav_c_cflag = 0; /* override mode to RS485 if needed, otherwise keep the current mode */ if (port->rs485.flags & SER_RS485_ENABLED) { - atmel_uart_writel(port, ATMEL_US_TTGR, - port->rs485.delay_rts_after_send); - mode &= ~ATMEL_US_USMODE; - mode |= ATMEL_US_USMODE_RS485; + struct uart_state *state_p = port->state; + struct tty_struct *tty_struct_p = NULL; + + if (state_p) + tty_struct_p = state_p->port.tty; + + if(tty_struct_p) { + struct ktermios *termios = &(tty_struct_p->termios); + + sav_c_cflag = termios->c_cflag; + termios->c_cflag &= ~CRTSCTS; + } + + atmel_uart_writel(port, ATMEL_US_TTGR, + port->rs485.delay_rts_after_send); + mode &= ~ATMEL_US_USMODE; + mode |= ATMEL_US_USMODE_RS485; + dev_dbg(port->dev, + "atmel_set_mctrl: SER_RS485_ENABLED: c_cflag 0x%x port status 0x%x, hw_stopped %d\n", + sav_c_cflag,port->status,port->hw_stopped + ); + port->status &= ~UPSTAT_CTS_ENABLE; + port->hw_stopped = 0; // No longer possible + if (atmel_port->tx_stopped == true) + atmel_start_tx(port); + atmel_start_rx(port); } /* set the RTS line state according to the mode */ @@ -2168,6 +2215,19 @@ atmel_uart_writel(port, ATMEL_US_TTGR, port->rs485.delay_rts_after_send); mode |= ATMEL_US_USMODE_RS485; + /* CRTSCTS is impossible in RS485 mode, and breaks the driver + * so we cannot do a send. */ + dev_dbg(port->dev, + "atmel_set_termios: SER_RS485_ENABLED: original: c_cflag 0x%x status 0x%x, hw_stopped %d\n", + termios->c_cflag,port->status,port->hw_stopped + ); + + termios->c_cflag &= ~CRTSCTS; + port->status &= ~UPSTAT_CTS_ENABLE; + port->hw_stopped = 0; // Not possible + if (atmel_port->tx_stopped == true) + atmel_start_tx(port); + atmel_start_rx(port); } else if (termios->c_cflag & CRTSCTS) { /* RS232 with hardware handshake (RTS/CTS) */ if (atmel_use_fifo(port) &&