summaryrefslogtreecommitdiff
path: root/recipes-kernel/linux/linux-at91-5.4.81/mtcdt/linux-5.4-rs485-atmel-crtscts.patch
blob: 30cc7d54265322e480a3183c9c73f1f3915d746c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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