Skip to content

Commit

Permalink
serial: Do not hold the port lock when setting rx-during-tx GPIO
Browse files Browse the repository at this point in the history
Both the imx and stm32 driver set the rx-during-tx GPIO in rs485_config().
Since this function is called with the port lock held, this can be a
problem in case that setting the GPIO line can sleep (e.g. if a GPIO
expander is used which is connected via SPI or I2C).

Avoid this issue by moving the GPIO setting outside of the port lock into
the serial core and thus making it a generic feature.

Also with commit c54d485 ("serial: stm32: Add support for rs485
RX_DURING_TX output GPIO") the SER_RS485_RX_DURING_TX flag is only set if a
rx-during-tx GPIO is _not_ available, which is wrong. Fix this, too.

Furthermore reset old GPIO settings in case that changing the RS485
configuration failed.

Fixes: c54d485 ("serial: stm32: Add support for rs485 RX_DURING_TX output GPIO")
Fixes: ca530cf ("serial: imx: Add support for RS485 RX_DURING_TX output GPIO")
Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Sascha Hauer <s.hauer@pengutronix.de>
Cc:  <stable@vger.kernel.org>
Signed-off-by: Lino Sanfilippo <l.sanfilippo@kunbus.com>
Link: https://lore.kernel.org/r/20240103061818.564-2-l.sanfilippo@kunbus.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Lino Sanfilippo authored and Greg Kroah-Hartman committed Jan 4, 2024
1 parent da680c0 commit 07c30ea
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 12 deletions.
4 changes: 0 additions & 4 deletions drivers/tty/serial/imx.c
Original file line number Diff line number Diff line change
Expand Up @@ -1948,10 +1948,6 @@ static int imx_uart_rs485_config(struct uart_port *port, struct ktermios *termio
rs485conf->flags & SER_RS485_RX_DURING_TX)
imx_uart_start_rx(port);

if (port->rs485_rx_during_tx_gpio)
gpiod_set_value_cansleep(port->rs485_rx_during_tx_gpio,
!!(rs485conf->flags & SER_RS485_RX_DURING_TX));

return 0;
}

Expand Down
26 changes: 24 additions & 2 deletions drivers/tty/serial/serial_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1407,6 +1407,16 @@ static void uart_set_rs485_termination(struct uart_port *port,
!!(rs485->flags & SER_RS485_TERMINATE_BUS));
}

static void uart_set_rs485_rx_during_tx(struct uart_port *port,
const struct serial_rs485 *rs485)
{
if (!(rs485->flags & SER_RS485_ENABLED))
return;

gpiod_set_value_cansleep(port->rs485_rx_during_tx_gpio,
!!(rs485->flags & SER_RS485_RX_DURING_TX));
}

static int uart_rs485_config(struct uart_port *port)
{
struct serial_rs485 *rs485 = &port->rs485;
Expand All @@ -1418,12 +1428,17 @@ static int uart_rs485_config(struct uart_port *port)

uart_sanitize_serial_rs485(port, rs485);
uart_set_rs485_termination(port, rs485);
uart_set_rs485_rx_during_tx(port, rs485);

uart_port_lock_irqsave(port, &flags);
ret = port->rs485_config(port, NULL, rs485);
uart_port_unlock_irqrestore(port, flags);
if (ret)
if (ret) {
memset(rs485, 0, sizeof(*rs485));
/* unset GPIOs */
gpiod_set_value_cansleep(port->rs485_term_gpio, 0);
gpiod_set_value_cansleep(port->rs485_rx_during_tx_gpio, 0);
}

return ret;
}
Expand Down Expand Up @@ -1462,6 +1477,7 @@ static int uart_set_rs485_config(struct tty_struct *tty, struct uart_port *port,
return ret;
uart_sanitize_serial_rs485(port, &rs485);
uart_set_rs485_termination(port, &rs485);
uart_set_rs485_rx_during_tx(port, &rs485);

uart_port_lock_irqsave(port, &flags);
ret = port->rs485_config(port, &tty->termios, &rs485);
Expand All @@ -1473,8 +1489,14 @@ static int uart_set_rs485_config(struct tty_struct *tty, struct uart_port *port,
port->ops->set_mctrl(port, port->mctrl);
}
uart_port_unlock_irqrestore(port, flags);
if (ret)
if (ret) {
/* restore old GPIO settings */
gpiod_set_value_cansleep(port->rs485_term_gpio,
!!(port->rs485.flags & SER_RS485_TERMINATE_BUS));
gpiod_set_value_cansleep(port->rs485_rx_during_tx_gpio,
!!(port->rs485.flags & SER_RS485_RX_DURING_TX));
return ret;
}

if (copy_to_user(rs485_user, &port->rs485, sizeof(port->rs485)))
return -EFAULT;
Expand Down
8 changes: 2 additions & 6 deletions drivers/tty/serial/stm32-usart.c
Original file line number Diff line number Diff line change
Expand Up @@ -226,12 +226,6 @@ static int stm32_usart_config_rs485(struct uart_port *port, struct ktermios *ter

stm32_usart_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));

if (port->rs485_rx_during_tx_gpio)
gpiod_set_value_cansleep(port->rs485_rx_during_tx_gpio,
!!(rs485conf->flags & SER_RS485_RX_DURING_TX));
else
rs485conf->flags |= SER_RS485_RX_DURING_TX;

if (rs485conf->flags & SER_RS485_ENABLED) {
cr1 = readl_relaxed(port->membase + ofs->cr1);
cr3 = readl_relaxed(port->membase + ofs->cr3);
Expand All @@ -256,6 +250,8 @@ static int stm32_usart_config_rs485(struct uart_port *port, struct ktermios *ter

writel_relaxed(cr3, port->membase + ofs->cr3);
writel_relaxed(cr1, port->membase + ofs->cr1);

rs485conf->flags |= SER_RS485_RX_DURING_TX;
} else {
stm32_usart_clr_bits(port, ofs->cr3,
USART_CR3_DEM | USART_CR3_DEP);
Expand Down

0 comments on commit 07c30ea

Please sign in to comment.