serial: stm32: Use TC interrupt to deassert GPIO RTS in RS485 mode
authorMarek Vasut <marex@denx.de>
Sat, 30 Apr 2022 16:28:45 +0000 (18:28 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 8 Dec 2022 10:28:45 +0000 (11:28 +0100)
[ Upstream commit d7c76716169ddc37cf6316ff381d34ea807fbfd7 ]

In case the RS485 mode is emulated using GPIO RTS, use the TC interrupt
to deassert the GPIO RTS, otherwise the GPIO RTS stays asserted after a
transmission ended and the RS485 cannot work.

Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Alexandre Torgue <alexandre.torgue@foss.st.com>
Cc: Erwan Le Ray <erwan.leray@foss.st.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jean Philippe Romain <jean-philippe.romain@foss.st.com>
Cc: Valentin Caron <valentin.caron@foss.st.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-stm32@st-md-mailman.stormreply.com
To: linux-serial@vger.kernel.org
Link: https://lore.kernel.org/r/20220430162845.244655-2-marex@denx.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/tty/serial/stm32-usart.c
drivers/tty/serial/stm32-usart.h

index f2cef5f..86f044a 100644 (file)
@@ -314,6 +314,14 @@ static void stm32_usart_tx_interrupt_enable(struct uart_port *port)
                stm32_usart_set_bits(port, ofs->cr1, USART_CR1_TXEIE);
 }
 
+static void stm32_usart_tc_interrupt_enable(struct uart_port *port)
+{
+       struct stm32_port *stm32_port = to_stm32_port(port);
+       const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+
+       stm32_usart_set_bits(port, ofs->cr1, USART_CR1_TCIE);
+}
+
 static void stm32_usart_tx_interrupt_disable(struct uart_port *port)
 {
        struct stm32_port *stm32_port = to_stm32_port(port);
@@ -325,6 +333,14 @@ static void stm32_usart_tx_interrupt_disable(struct uart_port *port)
                stm32_usart_clr_bits(port, ofs->cr1, USART_CR1_TXEIE);
 }
 
+static void stm32_usart_tc_interrupt_disable(struct uart_port *port)
+{
+       struct stm32_port *stm32_port = to_stm32_port(port);
+       const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+
+       stm32_usart_clr_bits(port, ofs->cr1, USART_CR1_TCIE);
+}
+
 static void stm32_usart_rs485_rts_enable(struct uart_port *port)
 {
        struct stm32_port *stm32_port = to_stm32_port(port);
@@ -462,6 +478,13 @@ static void stm32_usart_transmit_chars(struct uart_port *port)
        u32 isr;
        int ret;
 
+       if (!stm32_port->hw_flow_control &&
+           port->rs485.flags & SER_RS485_ENABLED) {
+               stm32_port->txdone = false;
+               stm32_usart_tc_interrupt_disable(port);
+               stm32_usart_rs485_rts_enable(port);
+       }
+
        if (port->x_char) {
                if (stm32_port->tx_dma_busy)
                        stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
@@ -501,8 +524,14 @@ static void stm32_usart_transmit_chars(struct uart_port *port)
        if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
                uart_write_wakeup(port);
 
-       if (uart_circ_empty(xmit))
+       if (uart_circ_empty(xmit)) {
                stm32_usart_tx_interrupt_disable(port);
+               if (!stm32_port->hw_flow_control &&
+                   port->rs485.flags & SER_RS485_ENABLED) {
+                       stm32_port->txdone = true;
+                       stm32_usart_tc_interrupt_enable(port);
+               }
+       }
 }
 
 static irqreturn_t stm32_usart_interrupt(int irq, void *ptr)
@@ -515,6 +544,13 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr)
 
        sr = readl_relaxed(port->membase + ofs->isr);
 
+       if (!stm32_port->hw_flow_control &&
+           port->rs485.flags & SER_RS485_ENABLED &&
+           (sr & USART_SR_TC)) {
+               stm32_usart_tc_interrupt_disable(port);
+               stm32_usart_rs485_rts_disable(port);
+       }
+
        if ((sr & USART_SR_RTOF) && ofs->icr != UNDEF_REG)
                writel_relaxed(USART_ICR_RTOCF,
                               port->membase + ofs->icr);
@@ -612,8 +648,10 @@ static void stm32_usart_start_tx(struct uart_port *port)
 {
        struct circ_buf *xmit = &port->state->xmit;
 
-       if (uart_circ_empty(xmit) && !port->x_char)
+       if (uart_circ_empty(xmit) && !port->x_char) {
+               stm32_usart_rs485_rts_disable(port);
                return;
+       }
 
        stm32_usart_rs485_rts_enable(port);
 
index 07ac291..ad63351 100644 (file)
@@ -267,6 +267,7 @@ struct stm32_port {
        bool hw_flow_control;
        bool swap;               /* swap RX & TX pins */
        bool fifoen;
+       bool txdone;
        int rxftcfg;            /* RX FIFO threshold CFG      */
        int txftcfg;            /* TX FIFO threshold CFG      */
        bool wakeup_src;