Re: Atmel: RS485 half duplex with DMA issue

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

 



On Fri, Dec 2, 2016 at 10:56 AM, Richard Genoud
<richard.genoud@xxxxxxxxx> wrote:
> 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?  :)

Sorry for that, I will do it next time :-)

>>
>>> 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)

Just tested and it is working on my target.
So yes it is probably better to move it to avoid redundant code.
Test of SER_RS485_RX_DURING_TX is also working.

>
>
> 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 ?

I tend to agree with you

>
> Richard.
>

Thanks,
Gil
--
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