tty/serial/8250: Fix for TL16C750 with RTS/CTS autoflow control

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello

While working on my homebrew 68030 system, I have found a bug in the Linux 
8250 serial driver when using the TL16C750 UART with RTS/CTS flow control.
I have tested and can reproduce the fault with Linux 4.5.

I have prepared a patch which resolves the issue. This patch is attached. 

The TL16C750 UART has hardware-assisted control of the RTS and CTS lines 
("autoflow control"). This is intended to reduce the number of interrupts to 
the CPU has by handling the RTS and CTS flow control lines in hardware.

The datasheet explains the feature in detail:
  http://www.ti.com/lit/ds/symlink/tl16c750.pdf

Briefly: When this hardware-assist is enabled the hardware monitors the CTS 
line; it will not start transmitting a new character unless CTS indicates the 
peer has capacity to receive it, and it will not raise interrupts to report 
changes in CTS to the CPU. When the peer later changes CTS to indicate it has 
capacity to receive the transmitter will automatically start transmitting the 
next byte from the transmit FIFO.

The bug: The 8250 driver will also check the CTS line in software even when 
the hardware autoflow control is enabled. During every interrupt it will 
check the CTS input. If the peer indicates it has no capacity to receive the 
interrupt handler will not load any more characters into the transmit FIFO.
However if the transmit FIFO empties just as the peer drops the CTS line, 
during the resulting interrupt to refill the transmit FIFO the driver will 
note the state of CTS and will not refill the transmit FIFO. When CTS is 
raised again the hardware cannot resume transmission since the transmit FIFO 
remains empty, it will not raise a second "transmitter empty" interrupt since 
no characters were loaded in response to the previous one, and the hardware 
will not raise an interrupt to alert the CPU to the change in CTS (since the 
autoflow control is enabled).

No further characters will be transmitted on the port until another interrupt 
is raised and causes the driver to re-examine CTS. For example hitting a key 
on an attached serial terminal will re-start the output when this bug is 
triggered.

The attached patch sets the UPSTAT_AUTOCTS bit in uart_port.status.  
uart_softcts_mode() will then correctly return false for this port when 
autoflow control is enabled. This means that uart_handle_cts_change, called 
by serial8250_modem_status from serial8250_handle_irq, will not inspect the 
CTS line and will no longer halt the transmitter.

I am driving a DEC VT510 terminal with my TL16C750, with RTS/CTS flow 
control. I observed that large amounts of output to the terminal would pause 
apparently at random, and would not resume until a key was pressed. The 
terminal behaved correctly when connected to another UART (FTDI FT232RL). The 
terminal also behaved correctly if I modified the 8250 driver not to enable 
the "autoflow control" feature on the TL16C750 (ie commented out the lines 
which set the AFE bit in MCR).

There's a bit more info to be found, including oscilloscope traces 
illustrating the fault condition, here:
  https://www.retrobrewcomputers.org/forum/index.php?t=msg&goto=444&#msg_444

It seems plausible that the UPSTAT_AUTORTS bit should also be set when 
enabling the autoflow control since setting bit 5 (AFE) in MCR always enables 
auto-CTS and also enables auto-RTS control when MCR bit 1 (RTS) is set.  
However I have not had any issues with RTS, have not tested this change, and 
I do not believe a stall will occur if both hardware and software control 
this line. Perhaps someone who better understands the implications of setting 
UPSTAT_AUTORTS would be able to comment on this?

I hope you will consider merging this patch.

Many thanks

Will Sowerbutts

_________________________________________________________________________
William R Sowerbutts                                  will@xxxxxxxxxxxxxx
"Carpe post meridiem"                               http://sowerbutts.com
         main(){char*s=">#=0> ^#X@#@^7=",c=0,m;for(;c<15;c++)for
         (m=-1;m<7;putchar(m++/6&c%3/2?10:s[c]-31&1<<m?42:32));}  

--- linux-4.5/drivers/tty/serial/8250/8250_port.c	2016-03-14 04:28:54.000000000 +0000
+++ linux-4.5-kiss/drivers/tty/serial/8250/8250_port.c	2016-03-28 17:55:45.580249260 +0100
@@ -2309,8 +2309,12 @@
 	 */
 	if (up->capabilities & UART_CAP_AFE && port->fifosize >= 32) {
 		up->mcr &= ~UART_MCR_AFE;
-		if (termios->c_cflag & CRTSCTS)
+		port->status &= ~UPSTAT_AUTOCTS;
+
+		if (termios->c_cflag & CRTSCTS) {
 			up->mcr |= UART_MCR_AFE;
+			port->status |= UPSTAT_AUTOCTS;
+		}
 	}
 
 	/*

[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux