| From adafbbf6895eb0ce41a313c6ee68870ab9aa93cd Mon Sep 17 00:00:00 2001 |
| From: Lukas Wunner <lukas@wunner.de> |
| Date: Sun, 11 Sep 2022 11:02:03 +0200 |
| Subject: serial: stm32: Deassert Transmit Enable on ->rs485_config() |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| From: Lukas Wunner <lukas@wunner.de> |
| |
| commit adafbbf6895eb0ce41a313c6ee68870ab9aa93cd upstream. |
| |
| The STM32 USART can control RS-485 Transmit Enable in hardware. Since |
| commit 7df5081cbf5e ("serial: stm32: Add RS485 RTS GPIO control"), |
| it can alternatively be controlled in software. That was done to allow |
| RS-485 even if the RTS pin is unavailable because it's pinmuxed to a |
| different function. |
| |
| However the commit neglected to deassert Transmit Enable upon invocation |
| of the ->rs485_config() callback. Fix it. |
| |
| Avoid forward declarations by moving stm32_usart_tx_empty(), |
| stm32_usart_rs485_rts_enable() and stm32_usart_rs485_rts_disable() |
| further up in the driver. |
| |
| Fixes: 7df5081cbf5e ("serial: stm32: Add RS485 RTS GPIO control") |
| Cc: stable@vger.kernel.org # v5.9+ |
| Cc: Marek Vasut <marex@denx.de> |
| Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> |
| Signed-off-by: Lukas Wunner <lukas@wunner.de> |
| Link: https://lore.kernel.org/r/6059eab35dba394468335ef640df8b0050fd9dbd.1662886616.git.lukas@wunner.de |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/tty/serial/stm32-usart.c | 100 ++++++++++++++++++++------------------- |
| 1 file changed, 53 insertions(+), 47 deletions(-) |
| |
| --- a/drivers/tty/serial/stm32-usart.c |
| +++ b/drivers/tty/serial/stm32-usart.c |
| @@ -62,6 +62,53 @@ static void stm32_usart_clr_bits(struct |
| writel_relaxed(val, port->membase + reg); |
| } |
| |
| +static unsigned int stm32_usart_tx_empty(struct uart_port *port) |
| +{ |
| + struct stm32_port *stm32_port = to_stm32_port(port); |
| + const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; |
| + |
| + if (readl_relaxed(port->membase + ofs->isr) & USART_SR_TC) |
| + return TIOCSER_TEMT; |
| + |
| + return 0; |
| +} |
| + |
| +static void stm32_usart_rs485_rts_enable(struct uart_port *port) |
| +{ |
| + struct stm32_port *stm32_port = to_stm32_port(port); |
| + struct serial_rs485 *rs485conf = &port->rs485; |
| + |
| + if (stm32_port->hw_flow_control || |
| + !(rs485conf->flags & SER_RS485_ENABLED)) |
| + return; |
| + |
| + if (rs485conf->flags & SER_RS485_RTS_ON_SEND) { |
| + mctrl_gpio_set(stm32_port->gpios, |
| + stm32_port->port.mctrl | TIOCM_RTS); |
| + } else { |
| + mctrl_gpio_set(stm32_port->gpios, |
| + stm32_port->port.mctrl & ~TIOCM_RTS); |
| + } |
| +} |
| + |
| +static void stm32_usart_rs485_rts_disable(struct uart_port *port) |
| +{ |
| + struct stm32_port *stm32_port = to_stm32_port(port); |
| + struct serial_rs485 *rs485conf = &port->rs485; |
| + |
| + if (stm32_port->hw_flow_control || |
| + !(rs485conf->flags & SER_RS485_ENABLED)) |
| + return; |
| + |
| + if (rs485conf->flags & SER_RS485_RTS_ON_SEND) { |
| + mctrl_gpio_set(stm32_port->gpios, |
| + stm32_port->port.mctrl & ~TIOCM_RTS); |
| + } else { |
| + mctrl_gpio_set(stm32_port->gpios, |
| + stm32_port->port.mctrl | TIOCM_RTS); |
| + } |
| +} |
| + |
| static void stm32_usart_config_reg_rs485(u32 *cr1, u32 *cr3, u32 delay_ADE, |
| u32 delay_DDE, u32 baud) |
| { |
| @@ -145,6 +192,12 @@ static int stm32_usart_config_rs485(stru |
| |
| stm32_usart_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); |
| |
| + /* Adjust RTS polarity in case it's driven in software */ |
| + if (stm32_usart_tx_empty(port)) |
| + stm32_usart_rs485_rts_disable(port); |
| + else |
| + stm32_usart_rs485_rts_enable(port); |
| + |
| return 0; |
| } |
| |
| @@ -460,42 +513,6 @@ static void stm32_usart_tc_interrupt_dis |
| 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); |
| - struct serial_rs485 *rs485conf = &port->rs485; |
| - |
| - if (stm32_port->hw_flow_control || |
| - !(rs485conf->flags & SER_RS485_ENABLED)) |
| - return; |
| - |
| - if (rs485conf->flags & SER_RS485_RTS_ON_SEND) { |
| - mctrl_gpio_set(stm32_port->gpios, |
| - stm32_port->port.mctrl | TIOCM_RTS); |
| - } else { |
| - mctrl_gpio_set(stm32_port->gpios, |
| - stm32_port->port.mctrl & ~TIOCM_RTS); |
| - } |
| -} |
| - |
| -static void stm32_usart_rs485_rts_disable(struct uart_port *port) |
| -{ |
| - struct stm32_port *stm32_port = to_stm32_port(port); |
| - struct serial_rs485 *rs485conf = &port->rs485; |
| - |
| - if (stm32_port->hw_flow_control || |
| - !(rs485conf->flags & SER_RS485_ENABLED)) |
| - return; |
| - |
| - if (rs485conf->flags & SER_RS485_RTS_ON_SEND) { |
| - mctrl_gpio_set(stm32_port->gpios, |
| - stm32_port->port.mctrl & ~TIOCM_RTS); |
| - } else { |
| - mctrl_gpio_set(stm32_port->gpios, |
| - stm32_port->port.mctrl | TIOCM_RTS); |
| - } |
| -} |
| - |
| static void stm32_usart_transmit_chars_pio(struct uart_port *port) |
| { |
| struct stm32_port *stm32_port = to_stm32_port(port); |
| @@ -738,17 +755,6 @@ static irqreturn_t stm32_usart_threaded_ |
| return IRQ_HANDLED; |
| } |
| |
| -static unsigned int stm32_usart_tx_empty(struct uart_port *port) |
| -{ |
| - struct stm32_port *stm32_port = to_stm32_port(port); |
| - const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; |
| - |
| - if (readl_relaxed(port->membase + ofs->isr) & USART_SR_TC) |
| - return TIOCSER_TEMT; |
| - |
| - return 0; |
| -} |
| - |
| static void stm32_usart_set_mctrl(struct uart_port *port, unsigned int mctrl) |
| { |
| struct stm32_port *stm32_port = to_stm32_port(port); |