[PATCH v5 09/14] serial: 8250_mxpcie: support serial interface mode switching
Crescent Hsieh <[email protected]> Fri, 31 Jul 2026 15:48:15 +0800
| Newsgroups | org.kernel.vger.linux-serial,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
Moxa PCIe multiport serial boards support switching the serial interface mode between RS232, RS422, RS485-2W, and RS485-4W via on-board control registers. Implement an rs485_config() callback and map TIOCSRS485 requests to the corresponding hardware modes using serial_rs485 flags: - RS232 = (no flags set) - RS422 = SER_RS485_ENABLED | SER_RS485_MODE_RS422 - RS485_2W (half-duplex) = SER_RS485_ENABLED - RS485_4W (full-duplex) = SER_RS485_ENABLED | SER_RS485_RX_DURING_TX This allows users to reconfigure the serial mode at runtime via ioctl(). Signed-off-by: Crescent Hsieh <[email protected]> --- drivers/tty/serial/8250/8250_mxpcie.c | 45 +++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c index e864fb0624e5..93f0af958d11 100644 --- a/drivers/tty/serial/8250/8250_mxpcie.c +++ b/drivers/tty/serial/8250/8250_mxpcie.c @@ -114,6 +114,10 @@ enum { MOXA_SUPP_RS485 = BIT(2), }; +static const struct serial_rs485 mxpcie8250_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX | SER_RS485_MODE_RS422, +}; + static bool mxpcie8250_is_mini_pcie(unsigned short device) { if (device == PCI_DEVICE_ID_MOXA_CP102N || @@ -170,6 +174,38 @@ static void mxpcie8250_set_interface(struct mxpcie8250 *priv, iowrite8(cval, uir_addr); } +/* + * Moxa PCIe multiport serial boards support switching serial interfaces + * via the ioctl() command "TIOCSRS485". Supported modes and corresponding + * flags in "serial_rs485": + * + * RS232 = (no flags set) + * RS422 = SER_RS485_ENABLED | SER_RS485_MODE_RS422 + * RS485_2W (half-duplex) = SER_RS485_ENABLED + * RS485_4W (full-duplex) = SER_RS485_ENABLED | SER_RS485_RX_DURING_TX + */ +static int mxpcie8250_rs485_config(struct uart_port *port, + struct ktermios *termios, + struct serial_rs485 *rs485) +{ + struct mxpcie8250 *priv = dev_get_drvdata(port->dev); + u8 mode = MOXA_UIR_RS232; + + if (rs485->flags & SER_RS485_ENABLED) { + if (rs485->flags & SER_RS485_MODE_RS422) + mode = MOXA_UIR_RS422; + else if (rs485->flags & SER_RS485_RX_DURING_TX) + mode = MOXA_UIR_RS485_4W; + else + mode = MOXA_UIR_RS485_2W; + } else if (!(priv->supp_rs & MOXA_SUPP_RS232)) { + return -ENODEV; + } + mxpcie8250_set_interface(priv, port->port_id, mode); + + return 0; +} + static void mxpcie8250_set_termios(struct uart_port *port, struct ktermios *new, const struct ktermios *old) @@ -383,9 +419,14 @@ static void mxpcie8250_setup_port(struct pci_dev *pdev, int offset = idx * MOXA_PUART_OFFSET; u8 init_mode = MOXA_UIR_RS232; - if (!(priv->supp_rs & MOXA_SUPP_RS232)) + if (priv->supp_rs & MOXA_SUPP_RS485) { + up->port.rs485_config = mxpcie8250_rs485_config; + up->port.rs485_supported = mxpcie8250_rs485_supported; + } + if (!(priv->supp_rs & MOXA_SUPP_RS232)) { init_mode = MOXA_UIR_RS422; - + up->port.rs485.flags = SER_RS485_ENABLED | SER_RS485_MODE_RS422; + } mxpcie8250_set_interface(priv, idx, init_mode); if (idx == 3 && -- 2.45.2