Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

serial: omap: fix rs485 half-duplex filtering

Data received during half-duplex transmission must be filtered.
If the target device responds quickly, emptying the FIFO at the end of
the transmission can erase not only the echo characters but also part of
the response message.
By keeping the receive interrupt enabled even during transmission, it
allows you to filter each echo character and only in a number equal to
those transmitted.
The issue was generated by a target device that started responding
240us later having received a request in communication at 115200bps.
Sometimes, some messages received by the target were missing some of the
first bytes.

Fixes: 3a13884abea0 ("tty/serial: omap: empty the RX FIFO at the end of half-duplex TX")
Signed-off-by: Dario Binacchi <dariobin@libero.it>
Link: https://lore.kernel.org/r/20210418094705.27014-1-dariobin@libero.it
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Dario Binacchi and committed by
Greg Kroah-Hartman
e2a5e844 45f6b6db

+24 -15
+24 -15
drivers/tty/serial/omap-serial.c
··· 159 159 u32 calc_latency; 160 160 struct work_struct qos_work; 161 161 bool is_suspending; 162 + 163 + unsigned int rs485_tx_filter_count; 162 164 }; 163 165 164 166 #define to_uart_omap_port(p) ((container_of((p), struct uart_omap_port, port))) ··· 331 329 serial_out(up, UART_IER, up->ier); 332 330 } 333 331 334 - if ((port->rs485.flags & SER_RS485_ENABLED) && 335 - !(port->rs485.flags & SER_RS485_RX_DURING_TX)) { 336 - /* 337 - * Empty the RX FIFO, we are not interested in anything 338 - * received during the half-duplex transmission. 339 - */ 340 - serial_out(up, UART_FCR, up->fcr | UART_FCR_CLEAR_RCVR); 341 - /* Re-enable RX interrupts */ 342 - up->ier |= UART_IER_RLSI | UART_IER_RDI; 343 - up->port.read_status_mask |= UART_LSR_DR; 344 - serial_out(up, UART_IER, up->ier); 345 - } 346 - 347 332 pm_runtime_mark_last_busy(up->dev); 348 333 pm_runtime_put_autosuspend(up->dev); 349 334 } ··· 356 367 serial_out(up, UART_TX, up->port.x_char); 357 368 up->port.icount.tx++; 358 369 up->port.x_char = 0; 370 + if ((up->port.rs485.flags & SER_RS485_ENABLED) && 371 + !(up->port.rs485.flags & SER_RS485_RX_DURING_TX)) 372 + up->rs485_tx_filter_count++; 373 + 359 374 return; 360 375 } 361 376 if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { ··· 371 378 serial_out(up, UART_TX, xmit->buf[xmit->tail]); 372 379 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); 373 380 up->port.icount.tx++; 381 + if ((up->port.rs485.flags & SER_RS485_ENABLED) && 382 + !(up->port.rs485.flags & SER_RS485_RX_DURING_TX)) 383 + up->rs485_tx_filter_count++; 384 + 374 385 if (uart_circ_empty(xmit)) 375 386 break; 376 387 } while (--count > 0); ··· 418 421 419 422 if ((port->rs485.flags & SER_RS485_ENABLED) && 420 423 !(port->rs485.flags & SER_RS485_RX_DURING_TX)) 421 - serial_omap_stop_rx(port); 424 + up->rs485_tx_filter_count = 0; 422 425 423 426 serial_omap_enable_ier_thri(up); 424 427 pm_runtime_mark_last_busy(up->dev); ··· 489 492 * Read one data character out to avoid stalling the receiver according 490 493 * to the table 23-246 of the omap4 TRM. 491 494 */ 492 - if (likely(lsr & UART_LSR_DR)) 495 + if (likely(lsr & UART_LSR_DR)) { 493 496 serial_in(up, UART_RX); 497 + if ((up->port.rs485.flags & SER_RS485_ENABLED) && 498 + !(up->port.rs485.flags & SER_RS485_RX_DURING_TX) && 499 + up->rs485_tx_filter_count) 500 + up->rs485_tx_filter_count--; 501 + } 494 502 495 503 up->port.icount.rx++; 496 504 flag = TTY_NORMAL; ··· 546 544 return; 547 545 548 546 ch = serial_in(up, UART_RX); 547 + if ((up->port.rs485.flags & SER_RS485_ENABLED) && 548 + !(up->port.rs485.flags & SER_RS485_RX_DURING_TX) && 549 + up->rs485_tx_filter_count) { 550 + up->rs485_tx_filter_count--; 551 + return; 552 + } 553 + 549 554 flag = TTY_NORMAL; 550 555 up->port.icount.rx++; 551 556