[PATCH 05/15] serial: 8250_mxpcie: offload XON/XOFF flow control to MUEx50 hardware

From: Crescent Hsieh

Date: Mon May 04 2026 - 04:55:36 EST


The MUEx50 UART can handle in-band software flow control (XON/XOFF)
directly in hardware.

Program the on-chip XON/XOFF characters from termios settings and enable
the corresponding MUEx50 flow control modes when IXON or IXOFF is
requested. Provide throttle and unthrottle callbacks so RX can be
stopped and resumed cleanly.

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

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index 89086aa7b228..99fd789b7665 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -54,13 +54,31 @@
#define MOXA_PUART_SFR_950 BIT(5)

/* Enhanced Function Register (EFR) */
+/*
+ * EFR[1:0] - In-Band Receive Flow Control Mode (Compare XON/XOFF):
+ * 00b (0x00) = Disabled
+ * 01b (0x01) = Recognize XON2 & XOFF2 as XOFF character
+ * 10b (0x02) = Recognize XON1 & XOFF1 as XOFF character
+ * 11b (0x03) = Depends on EFR[3:2]
+ * EFR[3:2] - In-Band Transmit Flow Control Mode (Insert XON/XOFF):
+ * 00b (0x00) = Disabled
+ * 01b (0x04) = Use XON2 & XOFF2 as XOFF character
+ * 10b (0x08) = Use XON1 & XOFF1 as XOFF character
+ * 11b (0x0C) = Reserved
+ */
#define MOXA_PUART_EFR 0x0A
+#define MOXA_PUART_EFR_RX_FLOW 0x02 /* Recognize XON1 & XOFF1 as XOFF character */
+#define MOXA_PUART_EFR_TX_FLOW 0x08 /* Use XON1 & XOFF1 as XOFF character */
#define MOXA_PUART_EFR_ENHANCED BIT(4)
#define MOXA_PUART_EFR_AUTO_RTS BIT(6)
#define MOXA_PUART_EFR_AUTO_CTS BIT(7)
#define MOXA_PUART_EFR_RX_FLOW_MASK GENMASK(1, 0)
#define MOXA_PUART_EFR_TX_FLOW_MASK GENMASK(3, 2)

+#define MOXA_PUART_XON1 0x0B
+#define MOXA_PUART_XON2 0x0C
+#define MOXA_PUART_XOFF1 0x0D
+#define MOXA_PUART_XOFF2 0x0E
#define MOXA_PUART_TTL 0x10 /* Tx Interrupt Trigger Level */
#define MOXA_PUART_RTL 0x11 /* Rx Interrupt Trigger Level */
#define MOXA_PUART_FCL 0x12 /* Flow Control Low Trigger Level */
@@ -161,7 +179,7 @@ static void mxpcie8250_set_termios(struct uart_port *port,

serial8250_do_set_termios(port, new, old);

- up->port.status &= ~(UPSTAT_AUTORTS | UPSTAT_AUTOCTS);
+ up->port.status &= ~(UPSTAT_AUTORTS | UPSTAT_AUTOCTS | UPSTAT_AUTOXOFF);

efr = serial_in(up, MOXA_PUART_EFR);
efr &= ~(MOXA_PUART_EFR_AUTO_RTS | MOXA_PUART_EFR_AUTO_CTS);
@@ -170,6 +188,21 @@ static void mxpcie8250_set_termios(struct uart_port *port,
efr |= (MOXA_PUART_EFR_AUTO_RTS | MOXA_PUART_EFR_AUTO_CTS);
up->port.status |= (UPSTAT_AUTORTS | UPSTAT_AUTOCTS);
}
+ /* Set on-chip software flow control character */
+ serial_out(up, MOXA_PUART_XON1, START_CHAR(tty));
+ serial_out(up, MOXA_PUART_XON2, START_CHAR(tty));
+ serial_out(up, MOXA_PUART_XOFF1, STOP_CHAR(tty));
+ serial_out(up, MOXA_PUART_XOFF2, STOP_CHAR(tty));
+
+ efr &= ~(MOXA_PUART_EFR_RX_FLOW_MASK | MOXA_PUART_EFR_TX_FLOW_MASK);
+
+ if (I_IXON(tty))
+ efr |= MOXA_PUART_EFR_RX_FLOW;
+
+ if (I_IXOFF(tty)) {
+ efr |= MOXA_PUART_EFR_TX_FLOW;
+ up->port.status |= UPSTAT_AUTOXOFF;
+ }
serial_out(up, MOXA_PUART_EFR, efr);
}

@@ -207,6 +240,31 @@ static void mxpcie8250_shutdown(struct uart_port *port)
serial8250_do_shutdown(port);
}

+static void mxpcie8250_throttle(struct uart_port *port)
+{
+ unsigned long flags;
+
+ uart_port_lock_irqsave(port, &flags);
+
+ port->ops->stop_rx(port);
+
+ uart_port_unlock_irqrestore(port, flags);
+}
+
+static void mxpcie8250_unthrottle(struct uart_port *port)
+{
+ struct uart_8250_port *up = up_to_u8250p(port);
+ unsigned long flags;
+
+ uart_port_lock_irqsave(port, &flags);
+
+ up->ier |= UART_IER_RLSI | UART_IER_RDI;
+ port->read_status_mask |= UART_LSR_DR;
+ serial_out(up, UART_IER, up->ier);
+
+ uart_port_unlock_irqrestore(port, flags);
+}
+
static void mxpcie8250_init_board(struct pci_dev *pdev, struct mxpcie8250 *priv)
{
void __iomem *bar2_base = priv->bar2_base;
@@ -304,6 +362,8 @@ static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id
up.port.set_termios = mxpcie8250_set_termios;
up.port.startup = mxpcie8250_startup;
up.port.shutdown = mxpcie8250_shutdown;
+ up.port.throttle = mxpcie8250_throttle;
+ up.port.unthrottle = mxpcie8250_unthrottle;

for (i = 0; i < num_ports; i++) {
mxpcie8250_setup_port(pdev, priv, &up, i);
--
2.43.0