[PATCH v1 19/31] serial: 8250_mxpcie: add break signal support under RS485

Crescent Hsieh posted 31 patches 1 day, 11 hours ago
[PATCH v1 19/31] serial: 8250_mxpcie: add break signal support under RS485
Posted by Crescent Hsieh 1 day, 11 hours ago
Moxa PCIe boards require a specific sequence to send break signals in
RS485 mode. This patch implements the required behavior, while falling
back to the default break mechanism in RS232 and RS422 modes.

Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com>
---
 drivers/tty/serial/8250/8250_mxpcie.c | 52 +++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index 5d1097c166e4..9ba171274221 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -40,6 +40,7 @@
 
 /* Special Function Register (SFR) */
 #define MOXA_PUART_SFR		0x07
+#define MOXA_PUART_SFR_FORCE_TX	BIT(0)
 #define MOXA_PUART_SFR_950	BIT(5)
 
 /* Enhanced Function Register (EFR) */
@@ -390,6 +391,56 @@ static int mxpcie8250_handle_irq(struct uart_port *port)
 	return 1;
 }
 
+static void mxpcie8250_software_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_8250_port *up = up_to_u8250p(port);
+	struct tty_struct *tty = port->state->port.tty;
+	unsigned char tx_byte = 0x01;
+	unsigned int baud, quot;
+	unsigned long flags;
+	u8 sfr;
+
+	uart_port_lock_irqsave(port, &flags);
+
+	if (break_state == -1) {
+		serial_out(up, UART_LCR, up->lcr | UART_LCR_DLAB);
+		serial_out(up, UART_DLL, 0);
+		serial_out(up, UART_DLM, 0);
+		serial_out(up, UART_LCR, up->lcr);
+
+		memcpy(port->membase + MOXA_PUART_TX_FIFO_MEM, &tx_byte, 1);
+
+		sfr = serial_in(up, MOXA_PUART_SFR);
+		serial_out(up, MOXA_PUART_SFR, sfr | MOXA_PUART_SFR_FORCE_TX);
+
+		up->lcr |= UART_LCR_SBC;
+		serial_out(up, UART_LCR, up->lcr);
+	} else {
+		up->lcr &= ~UART_LCR_SBC;
+		serial_out(up, UART_LCR, up->lcr);
+
+		sfr = serial_in(up, MOXA_PUART_SFR);
+		serial_out(up, MOXA_PUART_SFR, sfr &= ~MOXA_PUART_SFR_FORCE_TX);
+
+		serial_out(up, UART_FCR, UART_FCR_CLEAR_XMIT);
+
+		baud = tty_get_baud_rate(tty);
+		quot = uart_get_divisor(port, baud);
+		serial8250_do_set_divisor(port, baud, quot);
+		serial_out(up, UART_LCR, up->lcr);
+	}
+	uart_port_unlock_irqrestore(port, flags);
+}
+
+static void mxpcie8250_break_ctl(struct uart_port *port, int break_state)
+{
+	if (port->rs485.flags & SER_RS485_ENABLED &&
+	    !(port->rs485.flags & SER_RS485_MODE_RS422))
+		mxpcie8250_software_break_ctl(port, break_state);
+	else
+		serial8250_do_break_ctl(port, break_state);
+}
+
 static int mxpcie8250_init(struct pci_dev *pdev)
 {
 	resource_size_t iobar_addr = pci_resource_start(pdev, 2);
@@ -487,6 +538,7 @@ static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id
 	up.port.throttle = mxpcie8250_throttle;
 	up.port.unthrottle = mxpcie8250_unthrottle;
 	up.port.handle_irq = mxpcie8250_handle_irq;
+	up.port.break_ctl = mxpcie8250_break_ctl;
 
 	for (i = 0; i < num_ports; i++) {
 		if (mxpcie8250_setup(pdev, priv, &up, i))
-- 
2.45.2
Re: [PATCH v1 19/31] serial: 8250_mxpcie: add break signal support under RS485
Posted by Andy Shevchenko 20 hours ago
On Sun, Nov 30, 2025 at 12:45 PM Crescent Hsieh
<crescentcy.hsieh@moxa.com> wrote:
>
> Moxa PCIe boards require a specific sequence to send break signals in
> RS485 mode. This patch implements the required behavior, while falling
> back to the default break mechanism in RS232 and RS422 modes.

...

> +static void mxpcie8250_software_break_ctl(struct uart_port *port, int break_state)
> +{
> +       struct uart_8250_port *up = up_to_u8250p(port);
> +       struct tty_struct *tty = port->state->port.tty;
> +       unsigned char tx_byte = 0x01;
> +       unsigned int baud, quot;
> +       unsigned long flags;
> +       u8 sfr;
> +
> +       uart_port_lock_irqsave(port, &flags);
> +
> +       if (break_state == -1) {
> +               serial_out(up, UART_LCR, up->lcr | UART_LCR_DLAB);
> +               serial_out(up, UART_DLL, 0);
> +               serial_out(up, UART_DLM, 0);
> +               serial_out(up, UART_LCR, up->lcr);

> +               memcpy(port->membase + MOXA_PUART_TX_FIFO_MEM, &tx_byte, 1);

My gosh, why?
It's even different to the way you used in the previous change.

> +               sfr = serial_in(up, MOXA_PUART_SFR);
> +               serial_out(up, MOXA_PUART_SFR, sfr | MOXA_PUART_SFR_FORCE_TX);
> +
> +               up->lcr |= UART_LCR_SBC;
> +               serial_out(up, UART_LCR, up->lcr);
> +       } else {
> +               up->lcr &= ~UART_LCR_SBC;
> +               serial_out(up, UART_LCR, up->lcr);
> +
> +               sfr = serial_in(up, MOXA_PUART_SFR);
> +               serial_out(up, MOXA_PUART_SFR, sfr &= ~MOXA_PUART_SFR_FORCE_TX);
> +
> +               serial_out(up, UART_FCR, UART_FCR_CLEAR_XMIT);
> +
> +               baud = tty_get_baud_rate(tty);
> +               quot = uart_get_divisor(port, baud);
> +               serial8250_do_set_divisor(port, baud, quot);
> +               serial_out(up, UART_LCR, up->lcr);
> +       }
> +       uart_port_unlock_irqrestore(port, flags);
> +}

-- 
With Best Regards,
Andy Shevchenko