Enable on-chip XON/XOFF software flow control via EFR[3:0], and configure
XON/XOFF characters using dedicated registers.
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@moxa.com>
---
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 197d016354c9..ceef6a5e677e 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -41,13 +41,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 */
@@ -152,7 +170,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);
@@ -161,6 +179,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);
}
@@ -195,6 +228,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 int mxpcie8250_init(struct pci_dev *pdev)
{
resource_size_t iobar_addr = pci_resource_start(pdev, 2);
@@ -284,6 +342,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++) {
if (mxpcie8250_setup(pdev, priv, &up, i))
--
2.45.2