Re: Atmel: RS485 half duplex with DMA issue

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

 



On 01/12/2016 16:10, Greg KH wrote:
> On Thu, Dec 01, 2016 at 03:20:35PM +0100, Gil Weber wrote:
>> Hello,
Hi,
>> I am unable to communicate in RS485 since I upgrade to the latest kernel
>> version (v4.9-rc7). Previously I was in 3.12.
>>
>> It seems to be caused by commit "0058f0871efe7b01c6f2b3046c68196ab73e96da
>> tty/serial: atmel: fix RS485 half duplex with DMA".
>> RX is disabled during TX, but in my case it is not re-enabled after.
>> If I revert this patch, everything seems to work again.
> 
> Any reason you didn't cc: the author of that patch?  :)
> 
>> Currently enable RX is done in atmel_tx_dma but it is not called if there are
>> still data to transfer... problem in that it is never done later.
>> So I think we should also enable RX in atmel_complete_tx_dma.
>>
>> I have made this fix, and it seems to work now:
>>
>>
>> diff --git a/drivers/tty/serial/atmel_serial.c
>> b/drivers/tty/serial/atmel_serial.c
>> index 74e97dc..4190012 100644
>> --- a/drivers/tty/serial/atmel_serial.c
>> +++ b/drivers/tty/serial/atmel_serial.c
>> @@ -806,6 +806,10 @@ static void atmel_complete_tx_dma(void *arg)
>>       */
>>      if (!uart_circ_empty(xmit))
>>          atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx);
>> +    else if (port->rs485.flags & SER_RS485_ENABLED) {
Don't you need to test the SER_RS485_RX_DURING_TX flag as well ?
Hum. I see. It is not present in atmel_tx_dma().
Maybe we can move the test from atmel_tx_dma() to here.
>> +        /* DMA done, start RX for RS485 */
>> +        atmel_start_rx(port);
>> +    }
>>
>>      spin_unlock_irqrestore(&port->lock, flags);
>>
>>
>>
>>
>> Is it correct? or maybe there is a better way to fix it?
>> Thanks,
>> Gil
> 
> Alexandre, any ideas?  
> 

Gil, could you try something like:

diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 168b10cad47b..11c0117af80b 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -798,6 +798,11 @@ static void atmel_complete_tx_dma(void *arg)
 	 */
 	if (!uart_circ_empty(xmit))
 		atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx);
+	else if ((port->rs485.flags & SER_RS485_ENABLED) &&
+		 !(port->rs485.flags & SER_RS485_RX_DURING_TX)) {
+		/* DMA done, stop TX, start RX for RS485 */
+		atmel_start_rx(port);
+	}
 
 	spin_unlock_irqrestore(&port->lock, flags);
 }
@@ -900,12 +905,6 @@ static void atmel_tx_dma(struct uart_port *port)
 		desc->callback = atmel_complete_tx_dma;
 		desc->callback_param = atmel_port;
 		atmel_port->cookie_tx = dmaengine_submit(desc);
-
-	} else {
-		if (port->rs485.flags & SER_RS485_ENABLED) {
-			/* DMA done, stop TX, start RX for RS485 */
-			atmel_start_rx(port);
-		}
 	}
 
 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)



Looking at atmel_{start,stop}_{rx,tx} functions, I wonder if it makes sense to have:

	if (atmel_use_pdc_tx(port) || atmel_use_dma_tx(port))
		if ((port->rs485.flags & SER_RS485_ENABLED) &&
		    !(port->rs485.flags & SER_RS485_RX_DURING_TX))
			atmel_stop_rx(port);
in atmel_start_tx()
and just:
	if ((port->rs485.flags & SER_RS485_ENABLED) &&
	    !(port->rs485.flags & SER_RS485_RX_DURING_TX))
		atmel_start_rx(port);
in atmel_stop_tx() ?

The condition on (atmel_use_pdc_tx(port) || atmel_use_dma_tx(port)) doesn't seems to be necessary.
If there's no DMA nor PDC, atmel_stop_rx(port) should still be called, shouldn't it ?

Richard.

--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[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