Skip to content

Commit

Permalink
serial: amba-pl011: Fix DMA transmission in RS485 mode
Browse files Browse the repository at this point in the history
commit 3b69e32 upstream.

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: 8d47923 ("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>
  • Loading branch information
linosanfilippo-kunbus authored and gregkh committed Mar 1, 2024
1 parent 3e3578c commit 9319ecb
Showing 1 changed file with 30 additions and 30 deletions.
60 changes: 30 additions & 30 deletions drivers/tty/serial/amba-pl011.c
Original file line number Diff line number Diff line change
Expand Up @@ -1345,11 +1345,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);
}
Expand Down Expand Up @@ -1431,42 +1461,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;
Expand Down

0 comments on commit 9319ecb

Please sign in to comment.