All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 00/14] serial: 8250: add Moxa MUEx50 PCIe board support
@ 2026-07-29  6:05 Crescent Hsieh
  2026-07-29  6:05 ` [PATCH v4 01/14] serial: 8250: split Moxa PCIe serial board support out of 8250_pci Crescent Hsieh
                   ` (14 more replies)
  0 siblings, 15 replies; 18+ messages in thread
From: Crescent Hsieh @ 2026-07-29  6:05 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Andy Shevchenko
  Cc: FangpingFP.Cheng, Epson.Chiang, EddieSY.Lin, linux-kernel,
	linux-serial, Crescent Hsieh

This series consolidates Moxa PCIe multiport serial board support under a
dedicated 8250_mxpcie driver and adds MUEx50 UART support needed by these
boards.

The first part splits the existing Moxa PCIe board handling out of
8250_pci into a separate driver while preserving the existing probe flow
and device IDs. The series then introduces the MUEx50 UART port type and
enables the board-specific UART setup, including enhanced mode and FIFO
trigger programming.

The driver adds hardware-assisted flow control support for MUEx50 UARTs,
including automatic RTS/CTS handling and XON/XOFF offload. It also adds a
custom IRQ path and uses the MUEx50 memory-mapped FIFO windows to improve
RX and TX data movement.

Later patches add serial interface mode switching, RS485 break handling,
and per-port private state for MUEx50-specific configuration. The driver
supports switching the serial interface mode through the RS485
configuration path, covering RS232, RS422, and RS485 modes supported by
the boards.

The final part extends 8250 core callbacks where needed so low-level
drivers can override break control and rx_trig_bytes handling. The
8250_mxpcie driver uses these hooks to implement RS485 break handling via
MUEx50 features and to expose the MUEx50 programmable RX trigger level
through the generic rx_trig_bytes sysfs interface.

---
Changes in v4:
  - Drop [PATCH v3 10/15], the workqueue-based TX wakeup patch.
  - Reorder [PATCH v3 09/15] to [PATCH v4 13/14].
  - [PATCH v4 01/14]
    - Remove the unused 8250_pcilib.h include,
      MODULE_IMPORT_NS("SERIAL_8250_PCI"), and select SERIAL_8250_PCILIB
      in Kconfig.
  - [PATCH v4 03/14]
    - Replace FIFO trigger level and flow-control threshold magic numbers
      with named default constants.
  - [PATCH v4 05/14]
    - Replace the EFR flow-control comment and magic values with named
      RX/TX field values, and use FIELD_MODIFY() to program the EFR flow
      fields.
  - [PATCH v4 06/14]
    - Split the RX decision logic into mxpcie8250_should_rx() and keep the
      IRQ path responsible for calling the generic RX handler.
  - [PATCH v4 07/14]
    - Switch the RX FIFO path to tty_prepare_flip_string() and remove
      the temporary stack buffer plus tty_insert_flip_string().
  - [PATCH v4 08/14]
    - Rework TX FIFO handling to use uart_port_tx_limited() for common
      serial-core TX handling.
  - [PATCH v4 10/14]
    - Export serial8250_do_break_ctl() with EXPORT_SYMBOL_GPL()
      instead of the SERIAL_8250_PCI namespace.
  - [PATCH v4 14/14]
    - Reuse the RX trigger default constant and change the range check to
      use port->fifosize.

Changes in v3:
  - [PATCH v3 01/15]
    - Add Andy's Reviewed-by tag.
  - [PATCH v3 06/15]
    - Use guard(uart_port_lock_check_sysrq_irqsave) in the IRQ handler,
      remove the now-unused flags variable, and join the LSR read with
      the mxpcie8250_rx_chars() call.
  - [PATCH v3 07/15]
    - Declare the RX FIFO copy loop iterator inside the for loop.
  - [PATCH v3 08/15]
    - Declare the TX FIFO fill loop iterator inside the for loop.

Changes in v2:
  - [PATCH v1 01/15]
    - Sort and trim 8250_mxpcie includes, add __counted_by(num_ports),
      tidy switch fallbacks and loop counters, use pci_set_drvdata() /
      pci_get_drvdata(), and add numbered review links.
  - [PATCH v1 03/15]
    - Document the repeated FIFO clear sequence and the TX FIFO
      read/write pointer clock-domain issue, and declare the FIFO clear
      loop counter inside the for loop.
  - [PATCH v1 05/15]
    - Replace open-coded uart port lock/unlock with
      guard(uart_port_lock_irqsave) in throttle() and unthrottle(), and
      remove the unused flags variable.
  - [PATCH v1 09/15]
    - Preserve __counted_by(num_ports) when converting the flexible
      array from line[] to per-port port[] storage.
  - [PATCH v1 10/15]
    - Add per-port workqueue state in pahole-friendly member order.
  - [PATCH v1 13/15]
    - Use guard(uart_port_lock_irqsave), use serial_dl_write(up, 0)
      instead of direct UART_DLL/UART_DLM writes, and avoid modifying
      sfr inside the serial_out() argument.
  - [PATCH v1 14/15]
    - Move get_rxtrig/set_rxtrig near existing getter/setter callbacks
      in struct uart_port and in the 8250_core.c callback copy path.
  - [PATCH v1 15/15]
    - Use named RX trigger constants, serial_port_in/out(), and aligned
      get_rxtrig/set_rxtrig ordering in both the implementation and
      probe callback assignment.

v3: https://lore.kernel.org/all/20260709053314.435629-1-crescentcy.hsieh@moxa.com/
v2: https://lore.kernel.org/all/20260701034128.218569-1-crescentcy.hsieh@moxa.com/
v1: https://lore.kernel.org/all/20260504084900.22380-1-crescentcy.hsieh@moxa.com/

Crescent Hsieh (14):
  serial: 8250: split Moxa PCIe serial board support out of 8250_pci
  serial: 8250: add Moxa MUEx50 UART port type
  serial: 8250_mxpcie: enable enhanced mode and program FIFO trigger
    levels
  serial: 8250_mxpcie: enable automatic RTS/CTS flow control
  serial: 8250_mxpcie: offload XON/XOFF flow control to MUEx50 hardware
  serial: 8250_mxpcie: add custom handle_irq callback
  serial: 8250_mxpcie: speed up RX using memory-mapped FIFO window
  serial: 8250_mxpcie: speed up TX using memory-mapped FIFO window
  serial: 8250_mxpcie: support serial interface mode switching
  serial: 8250: allow low-level drivers to override break control
  serial: 8250_mxpcie: add break support for RS485 using MUEx50 features
  serial: 8250: allow UART drivers to override rx_trig_bytes handling
  serial: 8250_mxpcie: introduce per-port private data structure
  serial: 8250_mxpcie: implement rx_trig_bytes callbacks via MUEx50 RTL

 drivers/tty/serial/8250/8250_core.c   |   6 +
 drivers/tty/serial/8250/8250_mxpcie.c | 630 ++++++++++++++++++++++++++
 drivers/tty/serial/8250/8250_pci.c    | 208 +--------
 drivers/tty/serial/8250/8250_port.c   |  33 +-
 drivers/tty/serial/8250/Kconfig       |  10 +
 drivers/tty/serial/8250/Makefile      |   1 +
 include/linux/serial_8250.h           |   1 +
 include/linux/serial_core.h           |   3 +
 include/uapi/linux/serial_core.h      |   3 +
 9 files changed, 687 insertions(+), 208 deletions(-)
 create mode 100644 drivers/tty/serial/8250/8250_mxpcie.c

-- 
2.45.2

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v4 01/14] serial: 8250: split Moxa PCIe serial board support out of 8250_pci
  2026-07-29  6:05 [PATCH v4 00/14] serial: 8250: add Moxa MUEx50 PCIe board support Crescent Hsieh
@ 2026-07-29  6:05 ` Crescent Hsieh
  2026-07-29  6:05 ` [PATCH v4 02/14] serial: 8250: add Moxa MUEx50 UART port type Crescent Hsieh
                   ` (13 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Crescent Hsieh @ 2026-07-29  6:05 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Andy Shevchenko
  Cc: FangpingFP.Cheng, Epson.Chiang, EddieSY.Lin, linux-kernel,
	linux-serial, Crescent Hsieh

The Moxa PCIe multiport serial boards are currently handled as part of
8250_pci.c. In preparation for adding Moxa-specific UART features and
optimizations, move the Moxa PCIe implementation into a dedicated
driver.

This introduces drivers/tty/serial/8250/8250_mxpcie.c and wires it up
via Kconfig and Makefile, while preserving the existing probe flow and
device IDs.

This change was suggested during earlier reviews by Andy Shevchenko [1][2].

No functional change intended.

Link: https://lore.kernel.org/all/ZmQovC6TbDpTb3c8@surfacebook.localdomain/ [1]
Link: https://lore.kernel.org/all/CAHp75VeDsVt0GQYUFxLM+obfmqXBPa3hM3YMsFbc26uzWZG-SQ@mail.gmail.com/ [2]
Suggested-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
---
 drivers/tty/serial/8250/8250_mxpcie.c | 288 ++++++++++++++++++++++++++
 drivers/tty/serial/8250/8250_pci.c    | 208 +------------------
 drivers/tty/serial/8250/Kconfig       |  10 +
 drivers/tty/serial/8250/Makefile      |   1 +
 4 files changed, 302 insertions(+), 205 deletions(-)
 create mode 100644 drivers/tty/serial/8250/8250_mxpcie.c

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
new file mode 100644
index 000000000000..6947487f5169
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Moxa PCIe multiport serial device driver
+ *
+ * Copyright (C) 2025 Moxa Inc. (support@moxa.com)
+ * Author: Crescent Hsieh <crescentcy.hsieh@moxa.com>
+ */
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+
+#include <linux/8250_pci.h>
+#include <linux/serial_8250.h>
+
+#include "8250.h"
+
+#define PCI_DEVICE_ID_MOXA_CP102E	0x1024
+#define PCI_DEVICE_ID_MOXA_CP102EL	0x1025
+#define PCI_DEVICE_ID_MOXA_CP102N	0x1027
+#define PCI_DEVICE_ID_MOXA_CP104EL_A	0x1045
+#define PCI_DEVICE_ID_MOXA_CP104N	0x1046
+#define PCI_DEVICE_ID_MOXA_CP112N	0x1121
+#define PCI_DEVICE_ID_MOXA_CP114EL	0x1144
+#define PCI_DEVICE_ID_MOXA_CP114N	0x1145
+#define PCI_DEVICE_ID_MOXA_CP116E_A_A	0x1160
+#define PCI_DEVICE_ID_MOXA_CP116E_A_B	0x1161
+#define PCI_DEVICE_ID_MOXA_CP118EL_A	0x1182
+#define PCI_DEVICE_ID_MOXA_CP118E_A_I	0x1183
+#define PCI_DEVICE_ID_MOXA_CP132EL	0x1322
+#define PCI_DEVICE_ID_MOXA_CP132N	0x1323
+#define PCI_DEVICE_ID_MOXA_CP134EL_A	0x1342
+#define PCI_DEVICE_ID_MOXA_CP134N	0x1343
+#define PCI_DEVICE_ID_MOXA_CP138E_A	0x1381
+#define PCI_DEVICE_ID_MOXA_CP168EL_A	0x1683
+
+/* Bits in PCI device ID encoding board capabilities */
+#define MOXA_DEV_ID_IFACE_MASK	GENMASK(11, 8)	/* Supported serial interface */
+#define MOXA_DEV_ID_NPORTS_MASK	GENMASK(7, 4)	/* Number of UART ports */
+
+/* UART */
+#define MOXA_PUART_BASE_BAUD		921600
+#define MOXA_PUART_OFFSET		0x200
+
+#define MOXA_GPIO_DIRECTION	0x09
+#define MOXA_GPIO_OUTPUT	0x0A
+
+#define MOXA_GPIO_PIN2	BIT(2)
+
+#define MOXA_UIR_OFFSET		0x04
+#define MOXA_UIR_RS232		0x00
+#define MOXA_UIR_RS422		0x01
+#define MOXA_UIR_RS485_4W	0x0B
+#define MOXA_UIR_RS485_2W	0x0F
+
+#define MOXA_EVEN_RS_MASK	GENMASK(3, 0)
+#define MOXA_ODD_RS_MASK	GENMASK(7, 4)
+
+struct mxpcie8250 {
+	unsigned int supp_rs;
+	unsigned int num_ports;
+	void __iomem *bar1_base; /* UART registers (MMIO) */
+	void __iomem *bar2_base; /* UIR / GPIO / CPLD (IO) */
+	int line[] __counted_by(num_ports);
+};
+
+enum {
+	MOXA_SUPP_RS232 = BIT(0),
+	MOXA_SUPP_RS422 = BIT(1),
+	MOXA_SUPP_RS485 = BIT(2),
+};
+
+static bool mxpcie8250_is_mini_pcie(unsigned short device)
+{
+	if (device == PCI_DEVICE_ID_MOXA_CP102N ||
+	    device == PCI_DEVICE_ID_MOXA_CP104N ||
+	    device == PCI_DEVICE_ID_MOXA_CP112N ||
+	    device == PCI_DEVICE_ID_MOXA_CP114N ||
+	    device == PCI_DEVICE_ID_MOXA_CP132N ||
+	    device == PCI_DEVICE_ID_MOXA_CP134N)
+		return true;
+
+	return false;
+}
+
+static unsigned int mxpcie8250_get_supp_rs(unsigned short device)
+{
+	switch (device & MOXA_DEV_ID_IFACE_MASK) {
+	case 0x0000:
+	case 0x0600:
+		return MOXA_SUPP_RS232;
+	case 0x0100:
+		return MOXA_SUPP_RS232 | MOXA_SUPP_RS422 | MOXA_SUPP_RS485;
+	case 0x0300:
+		return MOXA_SUPP_RS422 | MOXA_SUPP_RS485;
+	default:
+		return 0;
+	}
+}
+
+static unsigned short mxpcie8250_get_nports(unsigned short device)
+{
+	switch (device) {
+	case PCI_DEVICE_ID_MOXA_CP116E_A_A:
+	case PCI_DEVICE_ID_MOXA_CP116E_A_B:
+		return 8;
+	default:
+		return FIELD_GET(MOXA_DEV_ID_NPORTS_MASK, device);
+	}
+}
+
+static void mxpcie8250_set_interface(struct mxpcie8250 *priv,
+				     unsigned int port_idx,
+				     u8 mode)
+{
+	void __iomem *uir_addr = priv->bar2_base + MOXA_UIR_OFFSET + port_idx / 2;
+	u8 cval;
+
+	cval = ioread8(uir_addr);
+
+	if (port_idx % 2)
+		cval = FIELD_MODIFY(MOXA_ODD_RS_MASK, &cval, mode);
+	else
+		cval = FIELD_MODIFY(MOXA_EVEN_RS_MASK, &cval, mode);
+
+	iowrite8(cval, uir_addr);
+}
+
+static void mxpcie8250_init_board(struct pci_dev *pdev, struct mxpcie8250 *priv)
+{
+	void __iomem *bar2_base = priv->bar2_base;
+	unsigned short device = pdev->device;
+	u8 cval;
+
+	/* Initial terminator */
+	if (device == PCI_DEVICE_ID_MOXA_CP114EL ||
+	    device == PCI_DEVICE_ID_MOXA_CP118EL_A) {
+		iowrite8(0xff, bar2_base + MOXA_GPIO_DIRECTION);
+		iowrite8(0x00, bar2_base + MOXA_GPIO_OUTPUT);
+	}
+	/*
+	 * Enable hardware buffer to prevent break signal output when system boots up.
+	 * This hardware buffer is only supported on Mini PCIe series.
+	 */
+	if (mxpcie8250_is_mini_pcie(device)) {
+		/* Set GPIO direction */
+		cval = ioread8(bar2_base + MOXA_GPIO_DIRECTION);
+		cval |= MOXA_GPIO_PIN2;
+		iowrite8(cval, bar2_base + MOXA_GPIO_DIRECTION);
+		/* Enable low GPIO */
+		cval = ioread8(bar2_base + MOXA_GPIO_OUTPUT);
+		cval &= ~MOXA_GPIO_PIN2;
+		iowrite8(cval, bar2_base + MOXA_GPIO_OUTPUT);
+	}
+}
+
+static void mxpcie8250_setup_port(struct pci_dev *pdev,
+				  struct mxpcie8250 *priv,
+				  struct uart_8250_port *up,
+				  int idx)
+{
+	unsigned short device = pdev->device;
+	int offset = idx * MOXA_PUART_OFFSET;
+	u8 init_mode = MOXA_UIR_RS232;
+
+	if (!(priv->supp_rs & MOXA_SUPP_RS232))
+		init_mode = MOXA_UIR_RS422;
+
+	mxpcie8250_set_interface(priv, idx, init_mode);
+
+	if (idx == 3 &&
+	    (device == PCI_DEVICE_ID_MOXA_CP104EL_A ||
+	     device == PCI_DEVICE_ID_MOXA_CP114EL   ||
+	     device == PCI_DEVICE_ID_MOXA_CP134EL_A))
+		offset = 7 * MOXA_PUART_OFFSET;
+
+	up->port.mapbase = pci_resource_start(pdev, FL_BASE1) + offset;
+	up->port.membase = pcim_iomap_table(pdev)[FL_BASE1] + offset;
+}
+
+static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct device *dev = &pdev->dev;
+	struct uart_8250_port up = {};
+	struct mxpcie8250 *priv;
+	unsigned short device = pdev->device;
+	unsigned int num_ports;
+	int ret;
+
+	ret = pcim_enable_device(pdev);
+	if (ret)
+		return ret;
+
+	num_ports = mxpcie8250_get_nports(device);
+
+	priv = devm_kzalloc(dev, struct_size(priv, line, num_ports), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->num_ports = num_ports;
+	priv->supp_rs = mxpcie8250_get_supp_rs(device);
+
+	priv->bar1_base = pcim_iomap(pdev, FL_BASE1, 0);
+	if (!priv->bar1_base)
+		return -ENOMEM;
+
+	priv->bar2_base = pcim_iomap(pdev, FL_BASE2, 0);
+	if (!priv->bar2_base)
+		return -ENOMEM;
+
+	mxpcie8250_init_board(pdev, priv);
+
+	up.port.dev = dev;
+	up.port.irq = pdev->irq;
+	up.port.uartclk = MOXA_PUART_BASE_BAUD * 16;
+	up.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
+
+	up.port.iotype = UPIO_MEM;
+	up.port.iobase = 0;
+	up.port.regshift = 0;
+
+	for (unsigned int i = 0; i < num_ports; i++) {
+		mxpcie8250_setup_port(pdev, priv, &up, i);
+
+		dev_dbg(dev, "Setup PCI port: port %lx, irq %d, type %d\n",
+			up.port.iobase, up.port.irq, up.port.iotype);
+
+		priv->line[i] = serial8250_register_8250_port(&up);
+		if (priv->line[i] < 0) {
+			dev_err(dev,
+				"Couldn't register serial port %lx, irq %d, type %d, error %d\n",
+				up.port.iobase, up.port.irq,
+				up.port.iotype, priv->line[i]);
+			break;
+		}
+	}
+	pci_set_drvdata(pdev, priv);
+
+	return 0;
+}
+
+static void mxpcie8250_remove(struct pci_dev *pdev)
+{
+	struct mxpcie8250 *priv = pci_get_drvdata(pdev);
+
+	for (unsigned int i = 0; i < priv->num_ports; i++)
+		serial8250_unregister_port(priv->line[i]);
+}
+
+static const struct pci_device_id mxpcie8250_pci_ids[] = {
+	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102E) },
+	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102EL) },
+	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102N) },
+	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104EL_A) },
+	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104N) },
+	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP112N) },
+	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114EL) },
+	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114N) },
+	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_A) },
+	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_B) },
+	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118EL_A) },
+	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118E_A_I) },
+	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132EL) },
+	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132N) },
+	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134EL_A) },
+	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134N) },
+	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP138E_A) },
+	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168EL_A) },
+	{ }
+};
+MODULE_DEVICE_TABLE(pci, mxpcie8250_pci_ids);
+
+static struct pci_driver mxpcie8250_pci_driver = {
+	.name		= "8250_mxpcie",
+	.id_table	= mxpcie8250_pci_ids,
+	.probe		= mxpcie8250_probe,
+	.remove		= mxpcie8250_remove,
+};
+module_pci_driver(mxpcie8250_pci_driver);
+
+MODULE_AUTHOR("Moxa Inc.");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Moxa PCIe Multiport Serial Device Driver");
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 152f914c599d..fa172477409a 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -76,25 +76,6 @@
 #define PCI_DEVICE_ID_WCHIC_CH384_4S	0x3470
 #define PCI_DEVICE_ID_WCHIC_CH384_8S	0x3853
 
-#define PCI_DEVICE_ID_MOXA_CP102E	0x1024
-#define PCI_DEVICE_ID_MOXA_CP102EL	0x1025
-#define PCI_DEVICE_ID_MOXA_CP102N	0x1027
-#define PCI_DEVICE_ID_MOXA_CP104EL_A	0x1045
-#define PCI_DEVICE_ID_MOXA_CP104N	0x1046
-#define PCI_DEVICE_ID_MOXA_CP112N	0x1121
-#define PCI_DEVICE_ID_MOXA_CP114EL	0x1144
-#define PCI_DEVICE_ID_MOXA_CP114N	0x1145
-#define PCI_DEVICE_ID_MOXA_CP116E_A_A	0x1160
-#define PCI_DEVICE_ID_MOXA_CP116E_A_B	0x1161
-#define PCI_DEVICE_ID_MOXA_CP118EL_A	0x1182
-#define PCI_DEVICE_ID_MOXA_CP118E_A_I	0x1183
-#define PCI_DEVICE_ID_MOXA_CP132EL	0x1322
-#define PCI_DEVICE_ID_MOXA_CP132N	0x1323
-#define PCI_DEVICE_ID_MOXA_CP134EL_A	0x1342
-#define PCI_DEVICE_ID_MOXA_CP134N	0x1343
-#define PCI_DEVICE_ID_MOXA_CP138E_A	0x1381
-#define PCI_DEVICE_ID_MOXA_CP168EL_A	0x1683
-
 /* Unknown vendors/cards - this should not be in linux/pci_ids.h */
 #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584	0x1584
 #define PCI_SUBDEVICE_ID_UNKNOWN_0x1588	0x1588
@@ -1994,138 +1975,6 @@ pci_sunix_setup(struct serial_private *priv,
 	return setup_port(priv, port, bar, offset, 0);
 }
 
-#define MOXA_PUART_GPIO_EN	0x09
-#define MOXA_PUART_GPIO_OUT	0x0A
-
-#define MOXA_GPIO_PIN2	BIT(2)
-
-#define MOXA_RS232	0x00
-#define MOXA_RS422	0x01
-#define MOXA_RS485_4W	0x0B
-#define MOXA_RS485_2W	0x0F
-#define MOXA_UIR_OFFSET	0x04
-#define MOXA_EVEN_RS_MASK	GENMASK(3, 0)
-#define MOXA_ODD_RS_MASK	GENMASK(7, 4)
-
-enum {
-	MOXA_SUPP_RS232 = BIT(0),
-	MOXA_SUPP_RS422 = BIT(1),
-	MOXA_SUPP_RS485 = BIT(2),
-};
-
-static unsigned short moxa_get_nports(unsigned short device)
-{
-	switch (device) {
-	case PCI_DEVICE_ID_MOXA_CP116E_A_A:
-	case PCI_DEVICE_ID_MOXA_CP116E_A_B:
-		return 8;
-	}
-
-	return FIELD_GET(0x00F0, device);
-}
-
-static bool pci_moxa_is_mini_pcie(unsigned short device)
-{
-	if (device == PCI_DEVICE_ID_MOXA_CP102N	||
-	    device == PCI_DEVICE_ID_MOXA_CP104N	||
-	    device == PCI_DEVICE_ID_MOXA_CP112N	||
-	    device == PCI_DEVICE_ID_MOXA_CP114N ||
-	    device == PCI_DEVICE_ID_MOXA_CP132N ||
-	    device == PCI_DEVICE_ID_MOXA_CP134N)
-		return true;
-
-	return false;
-}
-
-static unsigned int pci_moxa_supported_rs(struct pci_dev *dev)
-{
-	switch (dev->device & 0x0F00) {
-	case 0x0000:
-	case 0x0600:
-		return MOXA_SUPP_RS232;
-	case 0x0100:
-		return MOXA_SUPP_RS232 | MOXA_SUPP_RS422 | MOXA_SUPP_RS485;
-	case 0x0300:
-		return MOXA_SUPP_RS422 | MOXA_SUPP_RS485;
-	}
-	return 0;
-}
-
-static int pci_moxa_set_interface(const struct pci_dev *dev,
-				  unsigned int port_idx,
-				  u8 mode)
-{
-	resource_size_t iobar_addr = pci_resource_start(dev, 2);
-	resource_size_t UIR_addr = iobar_addr + MOXA_UIR_OFFSET + port_idx / 2;
-	u8 val;
-
-	val = inb(UIR_addr);
-
-	if (port_idx % 2) {
-		val &= ~MOXA_ODD_RS_MASK;
-		val |= FIELD_PREP(MOXA_ODD_RS_MASK, mode);
-	} else {
-		val &= ~MOXA_EVEN_RS_MASK;
-		val |= FIELD_PREP(MOXA_EVEN_RS_MASK, mode);
-	}
-	outb(val, UIR_addr);
-
-	return 0;
-}
-
-static int pci_moxa_init(struct pci_dev *dev)
-{
-	unsigned short device = dev->device;
-	resource_size_t iobar_addr = pci_resource_start(dev, 2);
-	unsigned int i, num_ports = moxa_get_nports(device);
-	u8 val, init_mode = MOXA_RS232;
-
-	if (!IS_ENABLED(CONFIG_HAS_IOPORT))
-		return serial_8250_warn_need_ioport(dev);
-
-	if (!(pci_moxa_supported_rs(dev) & MOXA_SUPP_RS232)) {
-		init_mode = MOXA_RS422;
-	}
-	for (i = 0; i < num_ports; ++i)
-		pci_moxa_set_interface(dev, i, init_mode);
-
-	/*
-	 * Enable hardware buffer to prevent break signal output when system boots up.
-	 * This hardware buffer is only supported on Mini PCIe series.
-	 */
-	if (pci_moxa_is_mini_pcie(device)) {
-		/* Set GPIO direction */
-		val = inb(iobar_addr + MOXA_PUART_GPIO_EN);
-		val |= MOXA_GPIO_PIN2;
-		outb(val, iobar_addr + MOXA_PUART_GPIO_EN);
-		/* Enable low GPIO */
-		val = inb(iobar_addr + MOXA_PUART_GPIO_OUT);
-		val &= ~MOXA_GPIO_PIN2;
-		outb(val, iobar_addr + MOXA_PUART_GPIO_OUT);
-	}
-
-	return num_ports;
-}
-
-static int
-pci_moxa_setup(struct serial_private *priv,
-		const struct pciserial_board *board,
-		struct uart_8250_port *port, int idx)
-{
-	unsigned int bar = FL_GET_BASE(board->flags);
-	int offset;
-
-	if (!IS_ENABLED(CONFIG_HAS_IOPORT))
-		return serial_8250_warn_need_ioport(priv->dev);
-
-	if (board->num_ports == 4 && idx == 3)
-		offset = 7 * board->uart_offset;
-	else
-		offset = idx * board->uart_offset;
-
-	return setup_port(priv, port, bar, offset, 0);
-}
-
 /*
  * Master list of serial port init/setup/exit quirks.
  * This does not describe the general nature of the port.
@@ -2941,17 +2790,6 @@ static struct pci_serial_quirk pci_serial_quirks[] = {
 		.setup		= pci_fintek_setup,
 		.init		= pci_fintek_init,
 	},
-	/*
-	 * MOXA
-	 */
-	{
-		.vendor		= PCI_VENDOR_ID_MOXA,
-		.device		= PCI_ANY_ID,
-		.subvendor	= PCI_ANY_ID,
-		.subdevice	= PCI_ANY_ID,
-		.init		= pci_moxa_init,
-		.setup		= pci_moxa_setup,
-	},
 	{
 		.vendor		= 0x1c29,
 		.device		= 0x1204,
@@ -3169,9 +3007,6 @@ enum pci_board_num_t {
 	pbn_titan_2_4000000,
 	pbn_titan_4_4000000,
 	pbn_titan_8_4000000,
-	pbn_moxa_2,
-	pbn_moxa_4,
-	pbn_moxa_8,
 };
 
 /*
@@ -3943,24 +3778,6 @@ static struct pciserial_board pci_boards[] = {
 		.uart_offset	= 0x200,
 		.first_offset	= 0x1000,
 	},
-	[pbn_moxa_2] = {
-		.flags		= FL_BASE1,
-		.num_ports      = 2,
-		.base_baud      = 921600,
-		.uart_offset	= 0x200,
-	},
-	[pbn_moxa_4] = {
-		.flags		= FL_BASE1,
-		.num_ports      = 4,
-		.base_baud      = 921600,
-		.uart_offset	= 0x200,
-	},
-	[pbn_moxa_8] = {
-		.flags		= FL_BASE1,
-		.num_ports      = 8,
-		.base_baud      = 921600,
-		.uart_offset	= 0x200,
-	},
 };
 
 #define REPORT_CONFIG(option) \
@@ -4014,6 +3831,9 @@ static const struct pci_device_id blacklist[] = {
 	{ PCI_VDEVICE(PERICOM, PCI_ANY_ID), REPORT_8250_CONFIG(PERICOM), },
 	{ PCI_VDEVICE(ACCESSIO, PCI_ANY_ID), REPORT_8250_CONFIG(PERICOM), },
 
+	/* Moxa devices */
+	{ PCI_VDEVICE(MOXA, PCI_ANY_ID), REPORT_8250_CONFIG(MOXA), },
+
 	/* End of the black list */
 	{ }
 };
@@ -5851,28 +5671,6 @@ static const struct pci_device_id serial_pci_tbl[] = {
 		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
 		pbn_ni8430_4 },
 
-	/*
-	 * MOXA
-	 */
-	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102E),	    pbn_moxa_2 },
-	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102EL),    pbn_moxa_2 },
-	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102N),	    pbn_moxa_2 },
-	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104EL_A),  pbn_moxa_4 },
-	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104N),	    pbn_moxa_4 },
-	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP112N),	    pbn_moxa_2 },
-	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114EL),    pbn_moxa_4 },
-	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114N),	    pbn_moxa_4 },
-	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_A), pbn_moxa_8 },
-	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_B), pbn_moxa_8 },
-	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118EL_A),  pbn_moxa_8 },
-	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118E_A_I), pbn_moxa_8 },
-	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132EL),    pbn_moxa_2 },
-	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132N),     pbn_moxa_2 },
-	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134EL_A),  pbn_moxa_4 },
-	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134N),	    pbn_moxa_4 },
-	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP138E_A),   pbn_moxa_8 },
-	{ PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168EL_A),  pbn_moxa_8 },
-
 	/*
 	* ADDI-DATA GmbH communication cards <info@addi-data.com>
 	*/
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index f64ef0819cd4..c3151a91e39c 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -157,6 +157,16 @@ config SERIAL_8250_EXAR
 	  422x PCIe serial cards that are not covered by the more generic
 	  SERIAL_8250_PCI option.
 
+config SERIAL_8250_MOXA_PCIE
+	tristate "8250/16550 Moxa PCIe device support"
+	depends on SERIAL_8250 && PCI
+	default SERIAL_8250
+	help
+	  Say Y here if you have a Moxa PCIe serial card.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called 8250_mxpcie.
+
 config SERIAL_8250_HP300
 	tristate
 	depends on SERIAL_8250 && HP300
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 9ec4d5fe64de..be78fd6b337a 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_SERIAL_8250_LPC18XX)	+= 8250_lpc18xx.o
 obj-$(CONFIG_SERIAL_8250_LPSS)		+= 8250_lpss.o
 obj-$(CONFIG_SERIAL_8250_MEN_MCB)	+= 8250_men_mcb.o
 obj-$(CONFIG_SERIAL_8250_MID)		+= 8250_mid.o
+obj-$(CONFIG_SERIAL_8250_MOXA_PCIE)	+= 8250_mxpcie.o
 obj-$(CONFIG_SERIAL_8250_MT6577)	+= 8250_mtk.o
 obj-$(CONFIG_SERIAL_8250_NI)		+= 8250_ni.o
 obj-$(CONFIG_SERIAL_OF_PLATFORM)	+= 8250_of.o
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v4 02/14] serial: 8250: add Moxa MUEx50 UART port type
  2026-07-29  6:05 [PATCH v4 00/14] serial: 8250: add Moxa MUEx50 PCIe board support Crescent Hsieh
  2026-07-29  6:05 ` [PATCH v4 01/14] serial: 8250: split Moxa PCIe serial board support out of 8250_pci Crescent Hsieh
@ 2026-07-29  6:05 ` Crescent Hsieh
  2026-07-29  6:05 ` [PATCH v4 03/14] serial: 8250_mxpcie: enable enhanced mode and program FIFO trigger levels Crescent Hsieh
                   ` (12 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Crescent Hsieh @ 2026-07-29  6:05 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Andy Shevchenko
  Cc: FangpingFP.Cheng, Epson.Chiang, EddieSY.Lin, linux-kernel,
	linux-serial, Crescent Hsieh

Add a new 8250 port type for the Moxa MUEx50 UART and describe its basic
FIFO size and trigger characteristics in the 8250 port configuration
table.

The 8250_mxpcie driver sets UPF_FIXED_TYPE and uses PORT_MUEX50 so that
the generic 8250 core applies the correct defaults.

Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com>
---
 drivers/tty/serial/8250/8250_mxpcie.c | 3 ++-
 drivers/tty/serial/8250/8250_port.c   | 8 ++++++++
 include/uapi/linux/serial_core.h      | 3 +++
 3 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index 6947487f5169..7234740a760a 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -218,7 +218,8 @@ static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id
 	up.port.dev = dev;
 	up.port.irq = pdev->irq;
 	up.port.uartclk = MOXA_PUART_BASE_BAUD * 16;
-	up.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
+	up.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
+	up.port.type = PORT_MUEX50;
 
 	up.port.iotype = UPIO_MEM;
 	up.port.iobase = 0;
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 719faf92aa8a..a17fdb5d68d2 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -310,6 +310,14 @@ static const struct serial8250_config uart_config[] = {
 		.rxtrig_bytes	= {1, 8, 16, 30},
 		.flags		= UART_CAP_FIFO | UART_CAP_AFE,
 	},
+	[PORT_MUEX50] = {
+		.name		= "Moxa MUEx50 UART",
+		.fifo_size	= 128,
+		.tx_loadsz	= 128,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+		.rxtrig_bytes	= {15, 31, 63, 111},
+		.flags		= UART_CAP_FIFO,
+	},
 };
 
 /* Uart divisor latch read */
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 9c007a106330..377884e3856a 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -100,6 +100,9 @@
 /* TXX9 type number */
 #define PORT_TXX9	64
 
+/* Moxa MUEx50 UART */
+#define PORT_MUEX50     65
+
 /*Digi jsm */
 #define PORT_JSM        69
 
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v4 03/14] serial: 8250_mxpcie: enable enhanced mode and program FIFO trigger levels
  2026-07-29  6:05 [PATCH v4 00/14] serial: 8250: add Moxa MUEx50 PCIe board support Crescent Hsieh
  2026-07-29  6:05 ` [PATCH v4 01/14] serial: 8250: split Moxa PCIe serial board support out of 8250_pci Crescent Hsieh
  2026-07-29  6:05 ` [PATCH v4 02/14] serial: 8250: add Moxa MUEx50 UART port type Crescent Hsieh
@ 2026-07-29  6:05 ` Crescent Hsieh
  2026-07-29  6:05 ` [PATCH v4 04/14] serial: 8250_mxpcie: enable automatic RTS/CTS flow control Crescent Hsieh
                   ` (11 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Crescent Hsieh @ 2026-07-29  6:05 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Andy Shevchenko
  Cc: FangpingFP.Cheng, Epson.Chiang, EddieSY.Lin, linux-kernel,
	linux-serial, Crescent Hsieh

The MUEx50 UART provides an enhanced register set and programmable FIFO
trigger levels for RX, TX, and flow control.

Enable enhanced mode during port startup and program the MUEx50 FIFO
trigger registers according to the configured port settings. Clear the
programmed state again during shutdown to restore the default UART
configuration.

The TX FIFO write pointer and read pointer are driven by different
clocks. Clear the FIFOs repeatedly during startup so both pointers are
reset before programming the trigger levels.

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

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index 7234740a760a..a73cd44c55d3 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -46,6 +46,23 @@
 /* UART */
 #define MOXA_PUART_BASE_BAUD		921600
 #define MOXA_PUART_OFFSET		0x200
+#define MOXA_PUART_TX_TRIG_DEFAULT	0
+#define MOXA_PUART_RX_TRIG_DEFAULT	96
+#define MOXA_PUART_RX_FLOW_LOW_DEFAULT	16
+#define MOXA_PUART_RX_FLOW_HIGH_DEFAULT	110
+
+/* Special Function Register (SFR) */
+#define MOXA_PUART_SFR		0x07
+#define MOXA_PUART_SFR_950	BIT(5)
+
+/* Enhanced Function Register (EFR) */
+#define MOXA_PUART_EFR				0x0A
+#define MOXA_PUART_EFR_ENHANCED			BIT(4)
+
+#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 */
+#define MOXA_PUART_FCH		0x13	/* Flow Control High Trigger Level */
 
 #define MOXA_GPIO_DIRECTION	0x09
 #define MOXA_GPIO_OUTPUT	0x0A
@@ -131,6 +148,49 @@ static void mxpcie8250_set_interface(struct mxpcie8250 *priv,
 	iowrite8(cval, uir_addr);
 }
 
+static int mxpcie8250_startup(struct uart_port *port)
+{
+	struct uart_8250_port *up = up_to_u8250p(port);
+	int ret;
+
+	ret = serial8250_do_startup(port);
+	if (ret)
+		return ret;
+
+	/*
+	 * The TX FIFO write pointer (w_ptr) and read pointer (r_ptr)
+	 * are driven by different clocks: w_ptr uses the PCIe clock
+	 * and r_ptr uses the UART clock. When TX FIFO flush is requested,
+	 * w_ptr may be cleared before r_ptr, so the UART can still observe
+	 * pending TX data.
+	 *
+	 * It is recommended to clear the FIFOs at least 5 times to ensure
+	 * both pointers are reset.
+	 */
+	for (unsigned int i = 0; i < 5; ++i)
+		serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+
+	serial_out(up, MOXA_PUART_EFR, MOXA_PUART_EFR_ENHANCED);
+	serial_out(up, MOXA_PUART_SFR, MOXA_PUART_SFR_950);
+
+	serial_out(up, MOXA_PUART_TTL, MOXA_PUART_TX_TRIG_DEFAULT);
+	serial_out(up, MOXA_PUART_RTL, MOXA_PUART_RX_TRIG_DEFAULT);
+	serial_out(up, MOXA_PUART_FCL, MOXA_PUART_RX_FLOW_LOW_DEFAULT);
+	serial_out(up, MOXA_PUART_FCH, MOXA_PUART_RX_FLOW_HIGH_DEFAULT);
+
+	return 0;
+}
+
+static void mxpcie8250_shutdown(struct uart_port *port)
+{
+	struct uart_8250_port *up = up_to_u8250p(port);
+
+	serial_out(up, MOXA_PUART_EFR, 0);
+	serial_out(up, MOXA_PUART_SFR, 0);
+
+	serial8250_do_shutdown(port);
+}
+
 static void mxpcie8250_init_board(struct pci_dev *pdev, struct mxpcie8250 *priv)
 {
 	void __iomem *bar2_base = priv->bar2_base;
@@ -225,6 +285,9 @@ static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id
 	up.port.iobase = 0;
 	up.port.regshift = 0;
 
+	up.port.startup = mxpcie8250_startup;
+	up.port.shutdown = mxpcie8250_shutdown;
+
 	for (unsigned int i = 0; i < num_ports; i++) {
 		mxpcie8250_setup_port(pdev, priv, &up, i);
 
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v4 04/14] serial: 8250_mxpcie: enable automatic RTS/CTS flow control
  2026-07-29  6:05 [PATCH v4 00/14] serial: 8250: add Moxa MUEx50 PCIe board support Crescent Hsieh
                   ` (2 preceding siblings ...)
  2026-07-29  6:05 ` [PATCH v4 03/14] serial: 8250_mxpcie: enable enhanced mode and program FIFO trigger levels Crescent Hsieh
@ 2026-07-29  6:05 ` Crescent Hsieh
  2026-07-29  6:05 ` [PATCH v4 05/14] serial: 8250_mxpcie: offload XON/XOFF flow control to MUEx50 hardware Crescent Hsieh
                   ` (10 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Crescent Hsieh @ 2026-07-29  6:05 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Andy Shevchenko
  Cc: FangpingFP.Cheng, Epson.Chiang, EddieSY.Lin, linux-kernel,
	linux-serial, Crescent Hsieh

The MUEx50 UART supports automatic RTS/CTS flow control via the enhanced
feature register.

Implement a mxpcie-specific set_termios() callback that enables MUEx50
auto-RTS/auto-CTS when CRTSCTS is requested and disables it otherwise.
Keep the 8250 port status flags in sync with the hardware configuration.

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

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index a73cd44c55d3..1124c7d951b3 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -58,6 +58,10 @@
 /* Enhanced Function Register (EFR) */
 #define MOXA_PUART_EFR				0x0A
 #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_TTL		0x10	/* Tx Interrupt Trigger Level */
 #define MOXA_PUART_RTL		0x11	/* Rx Interrupt Trigger Level */
@@ -148,6 +152,29 @@ static void mxpcie8250_set_interface(struct mxpcie8250 *priv,
 	iowrite8(cval, uir_addr);
 }
 
+static void mxpcie8250_set_termios(struct uart_port *port,
+				   struct ktermios *new,
+				   const struct ktermios *old)
+{
+	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);
+
+	efr = serial_in(up, MOXA_PUART_EFR);
+	efr &= ~(MOXA_PUART_EFR_AUTO_RTS | MOXA_PUART_EFR_AUTO_CTS);
+
+	if (cflag & CRTSCTS) {
+		efr |= (MOXA_PUART_EFR_AUTO_RTS | MOXA_PUART_EFR_AUTO_CTS);
+		up->port.status |= (UPSTAT_AUTORTS | UPSTAT_AUTOCTS);
+	}
+	serial_out(up, MOXA_PUART_EFR, efr);
+}
+
 static int mxpcie8250_startup(struct uart_port *port)
 {
 	struct uart_8250_port *up = up_to_u8250p(port);
@@ -285,6 +312,7 @@ static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id
 	up.port.iobase = 0;
 	up.port.regshift = 0;
 
+	up.port.set_termios = mxpcie8250_set_termios;
 	up.port.startup = mxpcie8250_startup;
 	up.port.shutdown = mxpcie8250_shutdown;
 
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v4 05/14] serial: 8250_mxpcie: offload XON/XOFF flow control to MUEx50 hardware
  2026-07-29  6:05 [PATCH v4 00/14] serial: 8250: add Moxa MUEx50 PCIe board support Crescent Hsieh
                   ` (3 preceding siblings ...)
  2026-07-29  6:05 ` [PATCH v4 04/14] serial: 8250_mxpcie: enable automatic RTS/CTS flow control Crescent Hsieh
@ 2026-07-29  6:05 ` Crescent Hsieh
  2026-07-30  5:49   ` Jiri Slaby
  2026-07-29  6:05 ` [PATCH v4 06/14] serial: 8250_mxpcie: add custom handle_irq callback Crescent Hsieh
                   ` (9 subsequent siblings)
  14 siblings, 1 reply; 18+ messages in thread
From: Crescent Hsieh @ 2026-07-29  6:05 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Andy Shevchenko
  Cc: FangpingFP.Cheng, Epson.Chiang, EddieSY.Lin, linux-kernel,
	linux-serial, Crescent Hsieh

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@moxa.com>
---
 drivers/tty/serial/8250/8250_mxpcie.c | 55 +++++++++++++++++++++++++--
 1 file changed, 52 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index 1124c7d951b3..0517caaf2d26 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -61,8 +61,20 @@
 #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_RX_FLOW_DISABLED		0x0
+#define MOXA_PUART_EFR_RX_FLOW_XON2_XOFF2	0x1
+#define MOXA_PUART_EFR_RX_FLOW_XON1_XOFF1	0x2
+#define MOXA_PUART_EFR_RX_FLOW_COPY_TX		0x3
 #define MOXA_PUART_EFR_TX_FLOW_MASK		GENMASK(3, 2)
-
+#define MOXA_PUART_EFR_TX_FLOW_DISABLED		0x0
+#define MOXA_PUART_EFR_TX_FLOW_XON2_XOFF2	0x1
+#define MOXA_PUART_EFR_TX_FLOW_XON1_XOFF1	0x2
+#define MOXA_PUART_EFR_TX_FLOW_RESERVED		0x3
+
+#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 */
@@ -159,11 +171,11 @@ static void mxpcie8250_set_termios(struct uart_port *port,
 	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;
+	u8 efr, val;
 
 	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);
@@ -172,6 +184,23 @@ 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);
+
+	val = I_IXON(tty) ? MOXA_PUART_EFR_RX_FLOW_XON1_XOFF1 : MOXA_PUART_EFR_RX_FLOW_DISABLED;
+	efr = FIELD_MODIFY(MOXA_PUART_EFR_RX_FLOW_MASK, &efr, val);
+
+	val = I_IXOFF(tty) ? MOXA_PUART_EFR_TX_FLOW_XON1_XOFF1 : MOXA_PUART_EFR_TX_FLOW_DISABLED;
+	efr = FIELD_MODIFY(MOXA_PUART_EFR_TX_FLOW_MASK, &efr, val);
+
+	if (I_IXOFF(tty))
+		up->port.status |= UPSTAT_AUTOXOFF;
+
 	serial_out(up, MOXA_PUART_EFR, efr);
 }
 
@@ -218,6 +247,24 @@ static void mxpcie8250_shutdown(struct uart_port *port)
 	serial8250_do_shutdown(port);
 }
 
+static void mxpcie8250_throttle(struct uart_port *port)
+{
+	guard(uart_port_lock_irqsave)(port);
+
+	port->ops->stop_rx(port);
+}
+
+static void mxpcie8250_unthrottle(struct uart_port *port)
+{
+	struct uart_8250_port *up = up_to_u8250p(port);
+
+	guard(uart_port_lock_irqsave)(port);
+
+	up->ier |= UART_IER_RLSI | UART_IER_RDI;
+	port->read_status_mask |= UART_LSR_DR;
+	serial_out(up, UART_IER, up->ier);
+}
+
 static void mxpcie8250_init_board(struct pci_dev *pdev, struct mxpcie8250 *priv)
 {
 	void __iomem *bar2_base = priv->bar2_base;
@@ -315,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 (unsigned int i = 0; i < num_ports; i++) {
 		mxpcie8250_setup_port(pdev, priv, &up, i);
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v4 06/14] serial: 8250_mxpcie: add custom handle_irq callback
  2026-07-29  6:05 [PATCH v4 00/14] serial: 8250: add Moxa MUEx50 PCIe board support Crescent Hsieh
                   ` (4 preceding siblings ...)
  2026-07-29  6:05 ` [PATCH v4 05/14] serial: 8250_mxpcie: offload XON/XOFF flow control to MUEx50 hardware Crescent Hsieh
@ 2026-07-29  6:05 ` Crescent Hsieh
  2026-07-29  6:05 ` [PATCH v4 07/14] serial: 8250_mxpcie: speed up RX using memory-mapped FIFO window Crescent Hsieh
                   ` (8 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Crescent Hsieh @ 2026-07-29  6:05 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Andy Shevchenko
  Cc: FangpingFP.Cheng, Epson.Chiang, EddieSY.Lin, linux-kernel,
	linux-serial, Crescent Hsieh

Add a mxpcie-specific handle_irq() implementation for Moxa PCIe serial
ports.

This keeps the interrupt handling self-contained in the driver and
provides a hook point for MUEx50-specific RX/TX paths added in subsequent
patches. The handler processes RX, updates modem status, and handles TX
when THRE is asserted.

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

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index 0517caaf2d26..2b177ed8ddc5 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -265,6 +265,47 @@ static void mxpcie8250_unthrottle(struct uart_port *port)
 	serial_out(up, UART_IER, up->ier);
 }
 
+static bool mxpcie8250_should_rx(struct uart_8250_port *up, u16 lsr)
+{
+	struct uart_port *port = &up->port;
+
+	if (!(lsr & (UART_LSR_DR | UART_LSR_BI)))
+		return false;
+
+	if (!(port->status & (UPSTAT_AUTOCTS | UPSTAT_AUTORTS)))
+		return true;
+	if (lsr & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS))
+		return true;
+	if (port->read_status_mask & UART_LSR_DR)
+		return true;
+
+	return false;
+}
+
+static int mxpcie8250_handle_irq(struct uart_port *port)
+{
+	struct uart_8250_port *up = up_to_u8250p(port);
+	u16 lsr;
+	u8 iir;
+
+	iir = serial_in(up, UART_IIR);
+	if (iir & UART_IIR_NO_INT)
+		return 0;
+
+	guard(uart_port_lock_check_sysrq_irqsave)(port);
+
+	lsr = serial_lsr_in(up);
+	if (mxpcie8250_should_rx(up, lsr))
+		lsr = serial8250_rx_chars(up, lsr);
+
+	serial8250_modem_status(up);
+
+	if ((lsr & UART_LSR_THRE) && (up->ier & UART_IER_THRI))
+		serial8250_tx_chars(up);
+
+	return 1;
+}
+
 static void mxpcie8250_init_board(struct pci_dev *pdev, struct mxpcie8250 *priv)
 {
 	void __iomem *bar2_base = priv->bar2_base;
@@ -364,6 +405,7 @@ static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id
 	up.port.shutdown = mxpcie8250_shutdown;
 	up.port.throttle = mxpcie8250_throttle;
 	up.port.unthrottle = mxpcie8250_unthrottle;
+	up.port.handle_irq = mxpcie8250_handle_irq;
 
 	for (unsigned int i = 0; i < num_ports; i++) {
 		mxpcie8250_setup_port(pdev, priv, &up, i);
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v4 07/14] serial: 8250_mxpcie: speed up RX using memory-mapped FIFO window
  2026-07-29  6:05 [PATCH v4 00/14] serial: 8250: add Moxa MUEx50 PCIe board support Crescent Hsieh
                   ` (5 preceding siblings ...)
  2026-07-29  6:05 ` [PATCH v4 06/14] serial: 8250_mxpcie: add custom handle_irq callback Crescent Hsieh
@ 2026-07-29  6:05 ` Crescent Hsieh
  2026-07-29  6:05 ` [PATCH v4 08/14] serial: 8250_mxpcie: speed up TX " Crescent Hsieh
                   ` (7 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Crescent Hsieh @ 2026-07-29  6:05 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Andy Shevchenko
  Cc: FangpingFP.Cheng, Epson.Chiang, EddieSY.Lin, linux-kernel,
	linux-serial, Crescent Hsieh

The MUEx50 UART provides a memory-mapped RX FIFO data window along with
an RX FIFO byte counter.

When no break or error conditions are present, read received data in
bulk via the MMIO FIFO window and push it to the tty layer in one
operation. Fall back to the generic 8250 RX path for break and error
handling.

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

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index 2b177ed8ddc5..cfef7eee81a8 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -13,6 +13,7 @@
 #include <linux/ioport.h>
 #include <linux/module.h>
 #include <linux/pci.h>
+#include <linux/tty_flip.h>
 #include <linux/types.h>
 
 #include <linux/8250_pci.h>
@@ -79,6 +80,9 @@
 #define MOXA_PUART_RTL		0x11	/* Rx Interrupt Trigger Level */
 #define MOXA_PUART_FCL		0x12	/* Flow Control Low Trigger Level */
 #define MOXA_PUART_FCH		0x13	/* Flow Control High Trigger Level */
+#define MOXA_PUART_RX_FIFO_CNT	0x15	/* Rx FIFO Data Counter */
+
+#define MOXA_PUART_RX_FIFO_MEM	0x100	/* Memory Space to Rx FIFO Data Register */
 
 #define MOXA_GPIO_DIRECTION	0x09
 #define MOXA_GPIO_OUTPUT	0x0A
@@ -265,6 +269,26 @@ static void mxpcie8250_unthrottle(struct uart_port *port)
 	serial_out(up, UART_IER, up->ier);
 }
 
+static void mxpcie8250_rx_chars(struct uart_8250_port *up)
+{
+	struct uart_port *port = &up->port;
+	struct tty_port *tport = &port->state->port;
+	unsigned int count;
+	u8 *buf;
+
+	count = serial_in(up, MOXA_PUART_RX_FIFO_CNT);
+	count = min(count, port->fifosize);
+	count = tty_prepare_flip_string(tport, &buf, count);
+	if (!count)
+		return;
+
+	for (unsigned int i = 0; i < count; ++i)
+		buf[i] = serial_in(up, MOXA_PUART_RX_FIFO_MEM + i);
+
+	port->icount.rx += count;
+	tty_flip_buffer_push(tport);
+}
+
 static bool mxpcie8250_should_rx(struct uart_8250_port *up, u16 lsr)
 {
 	struct uart_port *port = &up->port;
@@ -295,8 +319,12 @@ static int mxpcie8250_handle_irq(struct uart_port *port)
 	guard(uart_port_lock_check_sysrq_irqsave)(port);
 
 	lsr = serial_lsr_in(up);
-	if (mxpcie8250_should_rx(up, lsr))
-		lsr = serial8250_rx_chars(up, lsr);
+	if (mxpcie8250_should_rx(up, lsr)) {
+		if (!(lsr & UART_LSR_BRK_ERROR_BITS))
+			mxpcie8250_rx_chars(up);
+		else
+			lsr = serial8250_rx_chars(up, lsr);
+	}
 
 	serial8250_modem_status(up);
 
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v4 08/14] serial: 8250_mxpcie: speed up TX using memory-mapped FIFO window
  2026-07-29  6:05 [PATCH v4 00/14] serial: 8250: add Moxa MUEx50 PCIe board support Crescent Hsieh
                   ` (6 preceding siblings ...)
  2026-07-29  6:05 ` [PATCH v4 07/14] serial: 8250_mxpcie: speed up RX using memory-mapped FIFO window Crescent Hsieh
@ 2026-07-29  6:05 ` Crescent Hsieh
  2026-07-29  6:05 ` [PATCH v4 09/14] serial: 8250_mxpcie: support serial interface mode switching Crescent Hsieh
                   ` (6 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Crescent Hsieh @ 2026-07-29  6:05 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Andy Shevchenko
  Cc: FangpingFP.Cheng, Epson.Chiang, EddieSY.Lin, linux-kernel,
	linux-serial, Crescent Hsieh

The MUEx50 UART provides a memory-mapped TX FIFO data window along with
a TX FIFO level counter.

Fill the TX FIFO in bulk via the MMIO FIFO window based on available
FIFO space, using uart_port_tx_limited() for the common serial-core TX
handling.

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

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index cfef7eee81a8..53f304a17fef 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -81,8 +81,10 @@
 #define MOXA_PUART_FCL		0x12	/* Flow Control Low Trigger Level */
 #define MOXA_PUART_FCH		0x13	/* Flow Control High Trigger Level */
 #define MOXA_PUART_RX_FIFO_CNT	0x15	/* Rx FIFO Data Counter */
+#define MOXA_PUART_TX_FIFO_CNT	0x16	/* Tx FIFO Data Counter */
 
 #define MOXA_PUART_RX_FIFO_MEM	0x100	/* Memory Space to Rx FIFO Data Register */
+#define MOXA_PUART_TX_FIFO_MEM	0x100	/* Memory Space to Tx FIFO Data Register */
 
 #define MOXA_GPIO_DIRECTION	0x09
 #define MOXA_GPIO_OUTPUT	0x0A
@@ -306,6 +308,18 @@ static bool mxpcie8250_should_rx(struct uart_8250_port *up, u16 lsr)
 	return false;
 }
 
+static void mxpcie8250_tx_chars(struct uart_8250_port *up)
+{
+	struct uart_port *port = &up->port;
+	unsigned int offset = 0;
+	unsigned char c;
+
+	uart_port_tx_limited(port, c, port->fifosize - serial_in(up, MOXA_PUART_TX_FIFO_CNT),
+			     true,
+			     serial_out(up, MOXA_PUART_TX_FIFO_MEM + offset++, c),
+			     ({}));
+}
+
 static int mxpcie8250_handle_irq(struct uart_port *port)
 {
 	struct uart_8250_port *up = up_to_u8250p(port);
@@ -329,7 +343,7 @@ static int mxpcie8250_handle_irq(struct uart_port *port)
 	serial8250_modem_status(up);
 
 	if ((lsr & UART_LSR_THRE) && (up->ier & UART_IER_THRI))
-		serial8250_tx_chars(up);
+		mxpcie8250_tx_chars(up);
 
 	return 1;
 }
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v4 09/14] serial: 8250_mxpcie: support serial interface mode switching
  2026-07-29  6:05 [PATCH v4 00/14] serial: 8250: add Moxa MUEx50 PCIe board support Crescent Hsieh
                   ` (7 preceding siblings ...)
  2026-07-29  6:05 ` [PATCH v4 08/14] serial: 8250_mxpcie: speed up TX " Crescent Hsieh
@ 2026-07-29  6:05 ` Crescent Hsieh
  2026-07-29  6:05 ` [PATCH v4 10/14] serial: 8250: allow low-level drivers to override break control Crescent Hsieh
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Crescent Hsieh @ 2026-07-29  6:05 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Andy Shevchenko
  Cc: FangpingFP.Cheng, Epson.Chiang, EddieSY.Lin, linux-kernel,
	linux-serial, Crescent Hsieh

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 <crescentcy.hsieh@moxa.com>
---
 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 53f304a17fef..927615f2d724 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)
@@ -385,9 +421,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


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v4 10/14] serial: 8250: allow low-level drivers to override break control
  2026-07-29  6:05 [PATCH v4 00/14] serial: 8250: add Moxa MUEx50 PCIe board support Crescent Hsieh
                   ` (8 preceding siblings ...)
  2026-07-29  6:05 ` [PATCH v4 09/14] serial: 8250_mxpcie: support serial interface mode switching Crescent Hsieh
@ 2026-07-29  6:05 ` Crescent Hsieh
  2026-07-29  6:05 ` [PATCH v4 11/14] serial: 8250_mxpcie: add break support for RS485 using MUEx50 features Crescent Hsieh
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Crescent Hsieh @ 2026-07-29  6:05 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Andy Shevchenko
  Cc: FangpingFP.Cheng, Epson.Chiang, EddieSY.Lin, linux-kernel,
	linux-serial, Crescent Hsieh

Some UARTs require driver-specific handling for break signaling, which
cannot be expressed by the generic 8250 break implementation alone.

Add an optional uart_port break_ctl callback and route
serial8250_break_ctl() through it when provided. Rename the existing
8250 implementation to serial8250_do_break_ctl() and export it so
low-level drivers can reuse the default 8250 behavior when appropriate.

Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com>
---
 drivers/tty/serial/8250/8250_core.c |  2 ++
 drivers/tty/serial/8250/8250_port.c | 11 ++++++++++-
 include/linux/serial_8250.h         |  1 +
 include/linux/serial_core.h         |  1 +
 4 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index bfa421ab3253..0a3355eb4bc3 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -796,6 +796,8 @@ int serial8250_register_8250_port(const struct uart_8250_port *up)
 		uart->port.startup = up->port.startup;
 	if (up->port.shutdown)
 		uart->port.shutdown = up->port.shutdown;
+	if (up->port.break_ctl)
+		uart->port.break_ctl = up->port.break_ctl;
 	if (up->port.pm)
 		uart->port.pm = up->port.pm;
 	if (up->port.handle_break)
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index a17fdb5d68d2..6325784e5e73 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -1937,7 +1937,7 @@ static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
 		serial8250_do_set_mctrl(port, mctrl);
 }
 
-static void serial8250_break_ctl(struct uart_port *port, int break_state)
+void serial8250_do_break_ctl(struct uart_port *port, int break_state)
 {
 	struct uart_8250_port *up = up_to_u8250p(port);
 
@@ -1950,6 +1950,15 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state)
 		up->lcr &= ~UART_LCR_SBC;
 	serial_port_out(port, UART_LCR, up->lcr);
 }
+EXPORT_SYMBOL_GPL(serial8250_do_break_ctl);
+
+static void serial8250_break_ctl(struct uart_port *port, int break_state)
+{
+	if (port->break_ctl)
+		port->break_ctl(port, break_state);
+	else
+		serial8250_do_break_ctl(port, break_state);
+}
 
 /* Returns true if @bits were set, false on timeout */
 static bool wait_for_lsr(struct uart_8250_port *up, int bits)
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index 01efdce0fda0..5ae00dede026 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -192,6 +192,7 @@ void serial8250_do_shutdown(struct uart_port *port);
 void serial8250_do_pm(struct uart_port *port, unsigned int state,
 		      unsigned int oldstate);
 void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl);
+void serial8250_do_break_ctl(struct uart_port *port, int break_state);
 void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud,
 			       unsigned int quot);
 int fsl8250_handle_irq(struct uart_port *port);
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 110ad4e2aef9..f6ba3e7b09f1 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -463,6 +463,7 @@ struct uart_port {
 	void			(*shutdown)(struct uart_port *port);
 	void			(*throttle)(struct uart_port *port);
 	void			(*unthrottle)(struct uart_port *port);
+	void			(*break_ctl)(struct uart_port *port, int break_state);
 	int			(*handle_irq)(struct uart_port *);
 	void			(*pm)(struct uart_port *, unsigned int state,
 				      unsigned int old);
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v4 11/14] serial: 8250_mxpcie: add break support for RS485 using MUEx50 features
  2026-07-29  6:05 [PATCH v4 00/14] serial: 8250: add Moxa MUEx50 PCIe board support Crescent Hsieh
                   ` (9 preceding siblings ...)
  2026-07-29  6:05 ` [PATCH v4 10/14] serial: 8250: allow low-level drivers to override break control Crescent Hsieh
@ 2026-07-29  6:05 ` Crescent Hsieh
  2026-07-30  6:01   ` Jiri Slaby
  2026-07-29  6:06 ` [PATCH v4 12/14] serial: 8250: allow UART drivers to override rx_trig_bytes handling Crescent Hsieh
                   ` (3 subsequent siblings)
  14 siblings, 1 reply; 18+ messages in thread
From: Crescent Hsieh @ 2026-07-29  6:05 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Andy Shevchenko
  Cc: FangpingFP.Cheng, Epson.Chiang, EddieSY.Lin, linux-kernel,
	linux-serial, Crescent Hsieh

On MUEx50, break signaling under RS485 requires a driver-specific
sequence and cannot be handled correctly by the generic 8250 break
implementation alone.

Implement a mxpcie break_ctl callback that performs MUEx50-specific
break handling when RS485 is enabled and fall back to the default 8250
break handling for other modes.

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

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index 927615f2d724..878fc64c4451 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -54,6 +54,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) */
@@ -384,6 +385,53 @@ 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;
+	u8 sfr;
+
+	guard(uart_port_lock_irqsave)(port);
+
+	if (break_state == -1) {
+		serial_out(up, UART_LCR, up->lcr | UART_LCR_DLAB);
+		serial_dl_write(up, 0);
+		serial_out(up, UART_LCR, up->lcr);
+
+		serial_out(up, MOXA_PUART_TX_FIFO_MEM, tx_byte);
+
+		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);
+	}
+}
+
+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 void mxpcie8250_init_board(struct pci_dev *pdev, struct mxpcie8250 *priv)
 {
 	void __iomem *bar2_base = priv->bar2_base;
@@ -489,6 +537,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 (unsigned int i = 0; i < num_ports; i++) {
 		mxpcie8250_setup_port(pdev, priv, &up, i);
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v4 12/14] serial: 8250: allow UART drivers to override rx_trig_bytes handling
  2026-07-29  6:05 [PATCH v4 00/14] serial: 8250: add Moxa MUEx50 PCIe board support Crescent Hsieh
                   ` (10 preceding siblings ...)
  2026-07-29  6:05 ` [PATCH v4 11/14] serial: 8250_mxpcie: add break support for RS485 using MUEx50 features Crescent Hsieh
@ 2026-07-29  6:06 ` Crescent Hsieh
  2026-07-29  6:06 ` [PATCH v4 13/14] serial: 8250_mxpcie: introduce per-port private data structure Crescent Hsieh
                   ` (2 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Crescent Hsieh @ 2026-07-29  6:06 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Andy Shevchenko
  Cc: FangpingFP.Cheng, Epson.Chiang, EddieSY.Lin, linux-kernel,
	linux-serial, Crescent Hsieh

The rx_trig_bytes sysfs attribute currently relies on 8250-internal
helper functions and assumes a fixed mapping between trigger levels and
FIFO behavior.

Some UARTs provide hardware-specific RX trigger mechanisms that do not
fit this model. Add optional uart_port callbacks for setting and getting
the RX trigger level, and use them when provided, while preserving the
existing 8250 helpers as the default fallback.

Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com>
---
 drivers/tty/serial/8250/8250_core.c |  4 ++++
 drivers/tty/serial/8250/8250_port.c | 14 ++++++++++++--
 include/linux/serial_core.h         |  2 ++
 3 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 0a3355eb4bc3..052c0020a860 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -792,6 +792,10 @@ int serial8250_register_8250_port(const struct uart_8250_port *up)
 		uart->port.get_divisor = up->port.get_divisor;
 	if (up->port.set_divisor)
 		uart->port.set_divisor = up->port.set_divisor;
+	if (up->port.get_rxtrig)
+		uart->port.get_rxtrig = up->port.get_rxtrig;
+	if (up->port.set_rxtrig)
+		uart->port.set_rxtrig = up->port.set_rxtrig;
 	if (up->port.startup)
 		uart->port.startup = up->port.startup;
 	if (up->port.shutdown)
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 6325784e5e73..c2b46ef01d2f 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -2999,9 +2999,14 @@ static ssize_t rx_trig_bytes_show(struct device *dev,
 	struct device_attribute *attr, char *buf)
 {
 	struct tty_port *port = dev_get_drvdata(dev);
+	struct uart_state *state = container_of(port, struct uart_state, port);
+	struct uart_port *uport = state->uart_port;
 	int rxtrig_bytes;
 
-	rxtrig_bytes = do_serial8250_get_rxtrig(port);
+	if (uport->get_rxtrig)
+		rxtrig_bytes = uport->get_rxtrig(uport);
+	else
+		rxtrig_bytes = do_serial8250_get_rxtrig(port);
 	if (rxtrig_bytes < 0)
 		return rxtrig_bytes;
 
@@ -3044,6 +3049,8 @@ static ssize_t rx_trig_bytes_store(struct device *dev,
 	struct device_attribute *attr, const char *buf, size_t count)
 {
 	struct tty_port *port = dev_get_drvdata(dev);
+	struct uart_state *state = container_of(port, struct uart_state, port);
+	struct uart_port *uport = state->uart_port;
 	unsigned char bytes;
 	int ret;
 
@@ -3054,7 +3061,10 @@ static ssize_t rx_trig_bytes_store(struct device *dev,
 	if (ret < 0)
 		return ret;
 
-	ret = do_serial8250_set_rxtrig(port, bytes);
+	if (uport->set_rxtrig)
+		ret = uport->set_rxtrig(uport, bytes);
+	else
+		ret = do_serial8250_set_rxtrig(port, bytes);
 	if (ret < 0)
 		return ret;
 
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index f6ba3e7b09f1..13b0d4117036 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -459,6 +459,8 @@ struct uart_port {
 					       unsigned int baud,
 					       unsigned int quot,
 					       unsigned int quot_frac);
+	int			(*get_rxtrig)(struct uart_port *port);
+	int			(*set_rxtrig)(struct uart_port *port, unsigned char bytes);
 	int			(*startup)(struct uart_port *port);
 	void			(*shutdown)(struct uart_port *port);
 	void			(*throttle)(struct uart_port *port);
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v4 13/14] serial: 8250_mxpcie: introduce per-port private data structure
  2026-07-29  6:05 [PATCH v4 00/14] serial: 8250: add Moxa MUEx50 PCIe board support Crescent Hsieh
                   ` (11 preceding siblings ...)
  2026-07-29  6:06 ` [PATCH v4 12/14] serial: 8250: allow UART drivers to override rx_trig_bytes handling Crescent Hsieh
@ 2026-07-29  6:06 ` Crescent Hsieh
  2026-07-29  6:06 ` [PATCH v4 14/14] serial: 8250_mxpcie: implement rx_trig_bytes callbacks via MUEx50 RTL Crescent Hsieh
  2026-07-30  6:05 ` [PATCH v4 00/14] serial: 8250: add Moxa MUEx50 PCIe board support Jiri Slaby
  14 siblings, 0 replies; 18+ messages in thread
From: Crescent Hsieh @ 2026-07-29  6:06 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Andy Shevchenko
  Cc: FangpingFP.Cheng, Epson.Chiang, EddieSY.Lin, linux-kernel,
	linux-serial, Crescent Hsieh

Introduce a private per-port data structure for the mxpcie driver and
replace the shared flexible array of registered lines with an array of
per-port objects.

This prepares the driver for storing per-port state needed by subsequent
features.

No functional change intended.

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

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index 878fc64c4451..ce0ab5c1df52 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -101,12 +101,16 @@
 #define MOXA_EVEN_RS_MASK	GENMASK(3, 0)
 #define MOXA_ODD_RS_MASK	GENMASK(7, 4)
 
+struct mxpcie8250_port {
+	int line;
+};
+
 struct mxpcie8250 {
 	unsigned int supp_rs;
 	unsigned int num_ports;
 	void __iomem *bar1_base; /* UART registers (MMIO) */
 	void __iomem *bar2_base; /* UIR / GPIO / CPLD (IO) */
-	int line[] __counted_by(num_ports);
+	struct mxpcie8250_port port[] __counted_by(num_ports);
 };
 
 enum {
@@ -504,7 +508,7 @@ static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id
 
 	num_ports = mxpcie8250_get_nports(device);
 
-	priv = devm_kzalloc(dev, struct_size(priv, line, num_ports), GFP_KERNEL);
+	priv = devm_kzalloc(dev, struct_size(priv, port, num_ports), GFP_KERNEL);
 	if (!priv)
 		return -ENOMEM;
 
@@ -545,12 +549,12 @@ static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id
 		dev_dbg(dev, "Setup PCI port: port %lx, irq %d, type %d\n",
 			up.port.iobase, up.port.irq, up.port.iotype);
 
-		priv->line[i] = serial8250_register_8250_port(&up);
-		if (priv->line[i] < 0) {
+		priv->port[i].line = serial8250_register_8250_port(&up);
+		if (priv->port[i].line < 0) {
 			dev_err(dev,
 				"Couldn't register serial port %lx, irq %d, type %d, error %d\n",
 				up.port.iobase, up.port.irq,
-				up.port.iotype, priv->line[i]);
+				up.port.iotype, priv->port[i].line);
 			break;
 		}
 	}
@@ -564,7 +568,7 @@ static void mxpcie8250_remove(struct pci_dev *pdev)
 	struct mxpcie8250 *priv = pci_get_drvdata(pdev);
 
 	for (unsigned int i = 0; i < priv->num_ports; i++)
-		serial8250_unregister_port(priv->line[i]);
+		serial8250_unregister_port(priv->port[i].line);
 }
 
 static const struct pci_device_id mxpcie8250_pci_ids[] = {
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v4 14/14] serial: 8250_mxpcie: implement rx_trig_bytes callbacks via MUEx50 RTL
  2026-07-29  6:05 [PATCH v4 00/14] serial: 8250: add Moxa MUEx50 PCIe board support Crescent Hsieh
                   ` (12 preceding siblings ...)
  2026-07-29  6:06 ` [PATCH v4 13/14] serial: 8250_mxpcie: introduce per-port private data structure Crescent Hsieh
@ 2026-07-29  6:06 ` Crescent Hsieh
  2026-07-30  6:05 ` [PATCH v4 00/14] serial: 8250: add Moxa MUEx50 PCIe board support Jiri Slaby
  14 siblings, 0 replies; 18+ messages in thread
From: Crescent Hsieh @ 2026-07-29  6:06 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Andy Shevchenko
  Cc: FangpingFP.Cheng, Epson.Chiang, EddieSY.Lin, linux-kernel,
	linux-serial, Crescent Hsieh

The MUEx50 UART exposes a programmable RX trigger level via the RTL
register.

Implement uart_port RX trigger set/get callbacks for the mxpcie driver
and wire them up to the generic rx_trig_bytes sysfs interface. Store the
configured trigger level in the per-port private data.

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

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index ce0ab5c1df52..d97cf062908e 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -103,6 +103,7 @@
 
 struct mxpcie8250_port {
 	int line;
+	u8 rx_trig_level;
 };
 
 struct mxpcie8250 {
@@ -251,8 +252,27 @@ static void mxpcie8250_set_termios(struct uart_port *port,
 	serial_out(up, MOXA_PUART_EFR, efr);
 }
 
+static int mxpcie8250_get_rxtrig(struct uart_port *port)
+{
+	return serial_port_in(port, MOXA_PUART_RTL);
+}
+
+static int mxpcie8250_set_rxtrig(struct uart_port *port, unsigned char bytes)
+{
+	struct mxpcie8250 *priv = dev_get_drvdata(port->dev);
+
+	if (bytes > port->fifosize)
+		return -EINVAL;
+
+	serial_port_out(port, MOXA_PUART_RTL, bytes);
+	priv->port[port->port_id].rx_trig_level = bytes;
+
+	return 0;
+}
+
 static int mxpcie8250_startup(struct uart_port *port)
 {
+	struct mxpcie8250 *priv = dev_get_drvdata(port->dev);
 	struct uart_8250_port *up = up_to_u8250p(port);
 	int ret;
 
@@ -277,7 +297,7 @@ static int mxpcie8250_startup(struct uart_port *port)
 	serial_out(up, MOXA_PUART_SFR, MOXA_PUART_SFR_950);
 
 	serial_out(up, MOXA_PUART_TTL, MOXA_PUART_TX_TRIG_DEFAULT);
-	serial_out(up, MOXA_PUART_RTL, MOXA_PUART_RX_TRIG_DEFAULT);
+	serial_out(up, MOXA_PUART_RTL, priv->port[port->port_id].rx_trig_level);
 	serial_out(up, MOXA_PUART_FCL, MOXA_PUART_RX_FLOW_LOW_DEFAULT);
 	serial_out(up, MOXA_PUART_FCH, MOXA_PUART_RX_FLOW_HIGH_DEFAULT);
 
@@ -536,6 +556,8 @@ static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id
 	up.port.regshift = 0;
 
 	up.port.set_termios = mxpcie8250_set_termios;
+	up.port.get_rxtrig = mxpcie8250_get_rxtrig;
+	up.port.set_rxtrig = mxpcie8250_set_rxtrig;
 	up.port.startup = mxpcie8250_startup;
 	up.port.shutdown = mxpcie8250_shutdown;
 	up.port.throttle = mxpcie8250_throttle;
@@ -557,6 +579,7 @@ static int mxpcie8250_probe(struct pci_dev *pdev, const struct pci_device_id *id
 				up.port.iotype, priv->port[i].line);
 			break;
 		}
+		priv->port[i].rx_trig_level = MOXA_PUART_RX_TRIG_DEFAULT;
 	}
 	pci_set_drvdata(pdev, priv);
 
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 05/14] serial: 8250_mxpcie: offload XON/XOFF flow control to MUEx50 hardware
  2026-07-29  6:05 ` [PATCH v4 05/14] serial: 8250_mxpcie: offload XON/XOFF flow control to MUEx50 hardware Crescent Hsieh
@ 2026-07-30  5:49   ` Jiri Slaby
  0 siblings, 0 replies; 18+ messages in thread
From: Jiri Slaby @ 2026-07-30  5:49 UTC (permalink / raw)
  To: Crescent Hsieh, Greg Kroah-Hartman, Andy Shevchenko
  Cc: FangpingFP.Cheng, Epson.Chiang, EddieSY.Lin, linux-kernel,
	linux-serial

On 29. 07. 26, 8:05, Crescent Hsieh wrote:
> 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@moxa.com>
> ---
>   drivers/tty/serial/8250/8250_mxpcie.c | 55 +++++++++++++++++++++++++--
>   1 file changed, 52 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
> index 1124c7d951b3..0517caaf2d26 100644
> --- a/drivers/tty/serial/8250/8250_mxpcie.c
> +++ b/drivers/tty/serial/8250/8250_mxpcie.c
...> @@ -172,6 +184,23 @@ 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);

This is not needed, as you use FIELD_MODIFY(), right?

> +	val = I_IXON(tty) ? MOXA_PUART_EFR_RX_FLOW_XON1_XOFF1 : MOXA_PUART_EFR_RX_FLOW_DISABLED;
> +	efr = FIELD_MODIFY(MOXA_PUART_EFR_RX_FLOW_MASK, &efr, val);
> +
> +	val = I_IXOFF(tty) ? MOXA_PUART_EFR_TX_FLOW_XON1_XOFF1 : MOXA_PUART_EFR_TX_FLOW_DISABLED;
> +	efr = FIELD_MODIFY(MOXA_PUART_EFR_TX_FLOW_MASK, &efr, val);

Assigning back to efr is unnecessary. The reg is updated in-place.

thanks,
-- 
js
suse labs

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 11/14] serial: 8250_mxpcie: add break support for RS485 using MUEx50 features
  2026-07-29  6:05 ` [PATCH v4 11/14] serial: 8250_mxpcie: add break support for RS485 using MUEx50 features Crescent Hsieh
@ 2026-07-30  6:01   ` Jiri Slaby
  0 siblings, 0 replies; 18+ messages in thread
From: Jiri Slaby @ 2026-07-30  6:01 UTC (permalink / raw)
  To: Crescent Hsieh, Greg Kroah-Hartman, Andy Shevchenko
  Cc: FangpingFP.Cheng, Epson.Chiang, EddieSY.Lin, linux-kernel,
	linux-serial

On 29. 07. 26, 8:05, Crescent Hsieh wrote:
> On MUEx50, break signaling under RS485 requires a driver-specific
> sequence and cannot be handled correctly by the generic 8250 break
> implementation alone.
> 
> Implement a mxpcie break_ctl callback that performs MUEx50-specific
> break handling when RS485 is enabled and fall back to the default 8250
> break handling for other modes.
> 
> Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com>
> ---
>   drivers/tty/serial/8250/8250_mxpcie.c | 49 +++++++++++++++++++++++++++
>   1 file changed, 49 insertions(+)
> 
> diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
> index 927615f2d724..878fc64c4451 100644
> --- a/drivers/tty/serial/8250/8250_mxpcie.c
> +++ b/drivers/tty/serial/8250/8250_mxpcie.c
> @@ -54,6 +54,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) */
> @@ -384,6 +385,53 @@ 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;

Do you actually need the variable? If so, it should be u8, given you 
call byte and use it as such.

> +	unsigned int baud, quot;
> +	u8 sfr;
> +
> +	guard(uart_port_lock_irqsave)(port);
> +
> +	if (break_state == -1) {
> +		serial_out(up, UART_LCR, up->lcr | UART_LCR_DLAB);
> +		serial_dl_write(up, 0);
> +		serial_out(up, UART_LCR, up->lcr);
> +
> +		serial_out(up, MOXA_PUART_TX_FIFO_MEM, tx_byte);

thanks,
-- 
js
suse labs

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v4 00/14] serial: 8250: add Moxa MUEx50 PCIe board support
  2026-07-29  6:05 [PATCH v4 00/14] serial: 8250: add Moxa MUEx50 PCIe board support Crescent Hsieh
                   ` (13 preceding siblings ...)
  2026-07-29  6:06 ` [PATCH v4 14/14] serial: 8250_mxpcie: implement rx_trig_bytes callbacks via MUEx50 RTL Crescent Hsieh
@ 2026-07-30  6:05 ` Jiri Slaby
  14 siblings, 0 replies; 18+ messages in thread
From: Jiri Slaby @ 2026-07-30  6:05 UTC (permalink / raw)
  To: Crescent Hsieh, Greg Kroah-Hartman, Andy Shevchenko
  Cc: FangpingFP.Cheng, Epson.Chiang, EddieSY.Lin, linux-kernel,
	linux-serial

On 29. 07. 26, 8:05, Crescent Hsieh wrote:
> This series consolidates Moxa PCIe multiport serial board support under a
> dedicated 8250_mxpcie driver and adds MUEx50 UART support needed by these
> boards.
> 
> The first part splits the existing Moxa PCIe board handling out of
> 8250_pci into a separate driver while preserving the existing probe flow
> and device IDs. The series then introduces the MUEx50 UART port type and
> enables the board-specific UART setup, including enhanced mode and FIFO
> trigger programming.
> 
> The driver adds hardware-assisted flow control support for MUEx50 UARTs,
> including automatic RTS/CTS handling and XON/XOFF offload. It also adds a
> custom IRQ path and uses the MUEx50 memory-mapped FIFO windows to improve
> RX and TX data movement.
> 
> Later patches add serial interface mode switching, RS485 break handling,
> and per-port private state for MUEx50-specific configuration. The driver
> supports switching the serial interface mode through the RS485
> configuration path, covering RS232, RS422, and RS485 modes supported by
> the boards.
> 
> The final part extends 8250 core callbacks where needed so low-level
> drivers can override break control and rx_trig_bytes handling. The
> 8250_mxpcie driver uses these hooks to implement RS485 break handling via
> MUEx50 features and to expose the MUEx50 programmable RX trigger level
> through the generic rx_trig_bytes sysfs interface.
> 
> ---
> Changes in v4:

This looks very well from my POV, except the two nits. Thanks for this.

Let's see what others say ;).

-- 
js
suse labs

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2026-07-30  6:05 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-29  6:05 [PATCH v4 00/14] serial: 8250: add Moxa MUEx50 PCIe board support Crescent Hsieh
2026-07-29  6:05 ` [PATCH v4 01/14] serial: 8250: split Moxa PCIe serial board support out of 8250_pci Crescent Hsieh
2026-07-29  6:05 ` [PATCH v4 02/14] serial: 8250: add Moxa MUEx50 UART port type Crescent Hsieh
2026-07-29  6:05 ` [PATCH v4 03/14] serial: 8250_mxpcie: enable enhanced mode and program FIFO trigger levels Crescent Hsieh
2026-07-29  6:05 ` [PATCH v4 04/14] serial: 8250_mxpcie: enable automatic RTS/CTS flow control Crescent Hsieh
2026-07-29  6:05 ` [PATCH v4 05/14] serial: 8250_mxpcie: offload XON/XOFF flow control to MUEx50 hardware Crescent Hsieh
2026-07-30  5:49   ` Jiri Slaby
2026-07-29  6:05 ` [PATCH v4 06/14] serial: 8250_mxpcie: add custom handle_irq callback Crescent Hsieh
2026-07-29  6:05 ` [PATCH v4 07/14] serial: 8250_mxpcie: speed up RX using memory-mapped FIFO window Crescent Hsieh
2026-07-29  6:05 ` [PATCH v4 08/14] serial: 8250_mxpcie: speed up TX " Crescent Hsieh
2026-07-29  6:05 ` [PATCH v4 09/14] serial: 8250_mxpcie: support serial interface mode switching Crescent Hsieh
2026-07-29  6:05 ` [PATCH v4 10/14] serial: 8250: allow low-level drivers to override break control Crescent Hsieh
2026-07-29  6:05 ` [PATCH v4 11/14] serial: 8250_mxpcie: add break support for RS485 using MUEx50 features Crescent Hsieh
2026-07-30  6:01   ` Jiri Slaby
2026-07-29  6:06 ` [PATCH v4 12/14] serial: 8250: allow UART drivers to override rx_trig_bytes handling Crescent Hsieh
2026-07-29  6:06 ` [PATCH v4 13/14] serial: 8250_mxpcie: introduce per-port private data structure Crescent Hsieh
2026-07-29  6:06 ` [PATCH v4 14/14] serial: 8250_mxpcie: implement rx_trig_bytes callbacks via MUEx50 RTL Crescent Hsieh
2026-07-30  6:05 ` [PATCH v4 00/14] serial: 8250: add Moxa MUEx50 PCIe board support Jiri Slaby

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.