serial: amba-pl011: Fix DMA transmission in RS485 mode
authorLino Sanfilippo <l.sanfilippo@kunbus.com>
Fri, 16 Feb 2024 22:47:08 +0000 (23:47 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 19 Feb 2024 08:43:37 +0000 (09:43 +0100)
When DMA is used in RS485 mode make sure that the UARTs tx section is
enabled before the DMA buffers are queued for transmission.

Cc: stable@vger.kernel.org
Fixes: 8d479237727c ("serial: amba-pl011: add RS485 support")
Signed-off-by: Lino Sanfilippo <l.sanfilippo@kunbus.com>
Link: https://lore.kernel.org/r/20240216224709.9928-2-l.sanfilippo@kunbus.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/amba-pl011.c

index fccec1698a54104c1487ea65536dce7729123c61..cf2c890a560f05204e249b931668deca04b3cb27 100644 (file)
@@ -1339,11 +1339,41 @@ static void pl011_start_tx_pio(struct uart_amba_port *uap)
        }
 }
 
+static void pl011_rs485_tx_start(struct uart_amba_port *uap)
+{
+       struct uart_port *port = &uap->port;
+       u32 cr;
+
+       /* Enable transmitter */
+       cr = pl011_read(uap, REG_CR);
+       cr |= UART011_CR_TXE;
+
+       /* Disable receiver if half-duplex */
+       if (!(port->rs485.flags & SER_RS485_RX_DURING_TX))
+               cr &= ~UART011_CR_RXE;
+
+       if (port->rs485.flags & SER_RS485_RTS_ON_SEND)
+               cr &= ~UART011_CR_RTS;
+       else
+               cr |= UART011_CR_RTS;
+
+       pl011_write(cr, uap, REG_CR);
+
+       if (port->rs485.delay_rts_before_send)
+               mdelay(port->rs485.delay_rts_before_send);
+
+       uap->rs485_tx_started = true;
+}
+
 static void pl011_start_tx(struct uart_port *port)
 {
        struct uart_amba_port *uap =
            container_of(port, struct uart_amba_port, port);
 
+       if ((uap->port.rs485.flags & SER_RS485_ENABLED) &&
+           !uap->rs485_tx_started)
+               pl011_rs485_tx_start(uap);
+
        if (!pl011_dma_tx_start(uap))
                pl011_start_tx_pio(uap);
 }
@@ -1424,42 +1454,12 @@ static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c,
        return true;
 }
 
-static void pl011_rs485_tx_start(struct uart_amba_port *uap)
-{
-       struct uart_port *port = &uap->port;
-       u32 cr;
-
-       /* Enable transmitter */
-       cr = pl011_read(uap, REG_CR);
-       cr |= UART011_CR_TXE;
-
-       /* Disable receiver if half-duplex */
-       if (!(port->rs485.flags & SER_RS485_RX_DURING_TX))
-               cr &= ~UART011_CR_RXE;
-
-       if (port->rs485.flags & SER_RS485_RTS_ON_SEND)
-               cr &= ~UART011_CR_RTS;
-       else
-               cr |= UART011_CR_RTS;
-
-       pl011_write(cr, uap, REG_CR);
-
-       if (port->rs485.delay_rts_before_send)
-               mdelay(port->rs485.delay_rts_before_send);
-
-       uap->rs485_tx_started = true;
-}
-
 /* Returns true if tx interrupts have to be (kept) enabled  */
 static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq)
 {
        struct circ_buf *xmit = &uap->port.state->xmit;
        int count = uap->fifosize >> 1;
 
-       if ((uap->port.rs485.flags & SER_RS485_ENABLED) &&
-           !uap->rs485_tx_started)
-               pl011_rs485_tx_start(uap);
-
        if (uap->port.x_char) {
                if (!pl011_tx_char(uap, uap->port.x_char, from_irq))
                        return true;