Re: 8250 driver RS485 troubles

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

 



Hello Florian,

When working with a Zynq I wonder why you use the 8250 driver for RS485.
Using software to time RS485 changeovers is asking for trouble IMHO.

Have you seen my patch for the uartlite to support RS485 in hardware?
https://forums.xilinx.com/t5/Embedded-Development-Tools/Feature-Request-UARTlite-with-RS485-Driver-Enable-output-signal/m-p/689335

Maarten

On 2017-08-23 10:20, Florian Reitz wrote:
Dear kernel community,

I am working on an embedded Linux device that that is using the Linux
4.7 Kernel on the Xilinx Zynq platform. I am having trouble getting
the RS485 part of the 8250 serial driver to work. All the functions
needed for switching the RS485 transceiver using the RTS pin are
already present in the 8250_port.c file, but I had to add a
rs485_config() function to the 8250_of.c file to support configuration
using TIOCSRS485 ioctl() calls.

My problem was that at first, that the functions responsible for
setting the RTS pin was only called at the beginning of transmission,
leaving the transceiver in send mode. I found out that there may be a
bug in the __stop_tx() function from 8250_port.c

static inline void __stop_tx(struct uart_8250_port *p)
{
    struct uart_8250_em485 *em485 = p->em485;

    if (em485) {
        unsigned char lsr = serial_in(p, UART_LSR);
        /*
         * To provide required timeing and allow FIFO transfer,
         * __stop_tx_rs485 must be called only when both FIFO and
         * shift register are empty. It is for device driver to enable
         * interrupt on TEMT.
         */
        if ((lsr & BOTH_EMPTY) != BOTH_EMPTY)
        {
            return;
        }

        del_timer(&em485->start_tx_timer);
        em485->active_timer = NULL;
    }
    __do_stop_tx(p);
    __stop_tx_rs485(p);
}

BOTH_EMPTY translates to THRE and TEMT bits in the LSR register. THRE
is set when tx data has been transferred from the holding register to
the shift register, indicating, that the cpu may write a new byte to
the UART. TEMT is set when the data has been shifted out of the shift
register, ie. has actually been sent over the line. Therefore, TEMT
will always be set AFTER THRE was set, with the delay of the time it
takes to shift the byte on the line, including start-, stop- and
parity-bits.

I believe the problem is, that THRE and TEMT are tested together but
they may not have been set when the function is called, especially
when a low baudrate is used (19200 Bd in my case). I verified that the
return in the middle was always taken, even after the last byte, when
it was supposed to call __stop_tx_rs485(). That is why the transceiver
was never switched back to receive.

I simply commented the return out and it's working now, kind of. But
that is another question/problem I will ask later. I'd like to hear
your opinion on this topic first. This code is still present in the
4.12 kernel files.

Greetings, Florian

PS: Please CC your answers to my email address.

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