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 --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 2d17558a807d..e721e2028b4a 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -62,6 +62,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 @@ -300,6 +301,7 @@ static int atmel_config_rs485(struct uart_port *port, { 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); @@ -312,15 +314,33 @@ static int atmel_config_rs485(struct uart_port *port, port->rs485 = *rs485conf; if (rs485conf->flags & SER_RS485_ENABLED) { - dev_dbg(port->dev, "Setting UART to RS485\n"); - if (port->rs485.flags & SER_RS485_RX_DURING_TX) - atmel_port->tx_done_mask = ATMEL_US_TXRDY; - else - atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; + struct uart_state *state_p = port->state; + struct tty_struct *tty_struct_p = NULL; - atmel_uart_writel(port, ATMEL_US_TTGR, + 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)) @@ -481,13 +501,36 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) 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 */ @ -2250,6 +2250,19 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, 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 (port->iso7816.flags & SER_ISO7816_ENABLED) { atmel_uart_writel(port, ATMEL_US_TTGR, port->iso7816.tg); /* select mck clock, and output */ -- 2.25.1