[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251130104222.63077-6-crescentcy.hsieh@moxa.com>
Date: Sun, 30 Nov 2025 18:41:56 +0800
From: Crescent Hsieh <crescentcy.hsieh@...a.com>
To: gregkh@...uxfoundation.org,
jirislaby@...nel.org,
ilpo.jarvinen@...ux.intel.com,
andy.shevchenko@...il.com
Cc: linux-kernel@...r.kernel.org,
linux-serial@...r.kernel.org,
crescentcy.hsieh@...a.com
Subject: [PATCH v1 05/31] serial: 8250_mxupci: enable on-chip software flow control
Extend set_termios() to support on-chip software flow control using the
Enhanced Feature Register (EFR). When IXON/IXOFF is enabled in termios,
EFR[3:0] is configured to enable in-band XON/XOFF flow control.
Corresponding XON1/XOFF1 characters are written via EFR page 0.
This patch also implements throttle() and unthrottle() to handle receive
pause and resume based on software flow control.
Signed-off-by: Crescent Hsieh <crescentcy.hsieh@...a.com>
---
drivers/tty/serial/8250/8250_mxupci.c | 75 ++++++++++++++++++++++++++-
1 file changed, 74 insertions(+), 1 deletion(-)
diff --git a/drivers/tty/serial/8250/8250_mxupci.c b/drivers/tty/serial/8250/8250_mxupci.c
index 54e50cd3f472..99bd3e504549 100644
--- a/drivers/tty/serial/8250/8250_mxupci.c
+++ b/drivers/tty/serial/8250/8250_mxupci.c
@@ -45,17 +45,37 @@
/* 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) = Recognize XON1, XON2 & XOFF1, XOFF2 as XOFF character
+ * 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) = Use XON1, XON2 & XOFF1, XOFF2 as XOFF character
* EFR[7:6] - Enhanced Register Page Select:
* 00b (0x00) = Software flow control characters
* 01b (0x40) = FIFO trigger level
* 10b (0x80) = Clock, ID, reset
* 11b (0xC0) = Alias of Page 2 (same behavior as 10b)
*/
+#define MOXA_UART_EFR_RX_FLOW 0x02 /* Recognize XON1 & XOFF1 as XOFF character */
+#define MOXA_UART_EFR_TX_FLOW 0x08 /* Use XON1 & XOFF1 as XOFF character */
#define MOXA_UART_EFR_PAGE_0 0x00 /* Software flow control characters */
#define MOXA_UART_EFR_PAGE_1 0x40 /* FIFO trigger level */
#define MOXA_UART_EFR_PAGE_2 0x80 /* Clock, ID, reset */
+#define MOXA_UART_EFR_RX_FLOW_MASK GENMASK(1, 0)
+#define MOXA_UART_EFR_TX_FLOW_MASK GENMASK(3, 2)
#define MOXA_UART_EFR_PAGE_MASK GENMASK(7, 6)
+/* Enhanced Registers Page 0 */
+#define MOXA_UART_XON1 0x04
+#define MOXA_UART_XON2 0x05
+#define MOXA_UART_XOFF1 0x06
+#define MOXA_UART_XOFF2 0x07
+
/* Enhanced Registers Page 1 */
#define MOXA_UART_RBRTL 0x04 /* Flow Control Low Trigger Level */
#define MOXA_UART_RBRTH 0x05 /* Flow Control High Trigger Level */
@@ -84,10 +104,11 @@ static void mxupci8250_set_termios(struct uart_port *port, struct ktermios *new,
struct uart_8250_port *up = up_to_u8250p(port);
struct tty_struct *tty = port->state->port.tty;
unsigned int cflag = tty->termios.c_cflag;
+ u8 efr;
serial8250_do_set_termios(port, new, old);
- up->port.status &= ~(UPSTAT_AUTORTS | UPSTAT_AUTOCTS);
+ up->port.status &= ~(UPSTAT_AUTORTS | UPSTAT_AUTOCTS | UPSTAT_AUTOXOFF);
up->mcr &= ~UART_MCR_AFE;
@@ -96,6 +117,31 @@ static void mxupci8250_set_termios(struct uart_port *port, struct ktermios *new,
up->port.status |= (UPSTAT_AUTORTS | UPSTAT_AUTOCTS);
}
serial_out(up, UART_MCR, up->mcr);
+
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+
+ efr = serial_in(up, UART_EFR);
+ efr &= ~MOXA_UART_EFR_PAGE_MASK;
+ efr |= MOXA_UART_EFR_PAGE_0;
+ serial_out(up, UART_EFR, efr);
+
+ /* Set on-chip software flow control character */
+ serial_out(up, MOXA_UART_XON1, START_CHAR(tty));
+ serial_out(up, MOXA_UART_XON2, START_CHAR(tty));
+ serial_out(up, MOXA_UART_XOFF1, STOP_CHAR(tty));
+ serial_out(up, MOXA_UART_XOFF2, STOP_CHAR(tty));
+
+ efr &= ~(MOXA_UART_EFR_RX_FLOW_MASK | MOXA_UART_EFR_TX_FLOW_MASK);
+
+ if (I_IXON(tty))
+ efr |= MOXA_UART_EFR_RX_FLOW;
+
+ if (I_IXOFF(tty)) {
+ efr |= MOXA_UART_EFR_TX_FLOW;
+ up->port.status |= UPSTAT_AUTOXOFF;
+ }
+ serial_out(up, UART_EFR, efr);
+ serial_out(up, UART_LCR, up->lcr);
}
static int mxupci8250_startup(struct uart_port *port)
@@ -141,6 +187,31 @@ static void mxupci8250_shutdown(struct uart_port *port)
serial8250_do_shutdown(port);
}
+static void mxupci8250_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 mxupci8250_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 int mxupci8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct uart_8250_port up;
@@ -175,6 +246,8 @@ static int mxupci8250_probe(struct pci_dev *pdev, const struct pci_device_id *id
up.port.set_termios = mxupci8250_set_termios;
up.port.startup = mxupci8250_startup;
up.port.shutdown = mxupci8250_shutdown;
+ up.port.throttle = mxupci8250_throttle;
+ up.port.unthrottle = mxupci8250_unthrottle;
for (i = 0; i < num_ports; i++) {
if (serial8250_pci_setup_port(pdev, &up, FL_GET_BASE(FL_BASE2), i * MOXA_UART_OFFSET, 0))
--
2.45.2
Powered by blists - more mailing lists