[PATCH v1 30/31] serial: 8250_mxpcie: Add sysfs to control pull state via CPLD

Crescent Hsieh posted 31 patches 1 day, 11 hours ago
[PATCH v1 30/31] serial: 8250_mxpcie: Add sysfs to control pull state via CPLD
Posted by Crescent Hsieh 1 day, 11 hours ago
Some Moxa PCIe boards provide a CPLD-controlled pull/bias state.
Expose a per-port sysfs attribute to read and set the pull state from
userspace.

This patch:
- Defines MOXA_CPLD_PULL_STATE_FLAG and {ENABLE,DISABLE} values.
- Implements mxpcie8250_cpld_{set,get}_pull_state() with board_lock
  to serialize CPLD access.
- Exports "mxpcie8250_pull_state" in the driver's attr group.
- Restricts support to CPLD-based models (CP118E-A-I, CP138E-A,
  CP134EL-A, CP116E-A-A, CP116E-A-B); return -ENODEV on others.

Example:
  # Enable pull state on ttyS*
  echo 1 > /sys/class/tty/ttyS*/mxpcie8250_pull_state

  # Query pull state
  cat /sys/class/tty/ttyS*/mxpcie8250_pull_state
  enabled

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

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index 9dcb91b917a0..e9e3d03b7712 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -110,6 +110,7 @@
 #define MOXA_CPLD_DATA_MASK	0x1F	/* Pin0 ~ Pin4 */
 #define MOXA_CPLD_CTRL_MASK	0xE0	/* Pin5 ~ Pin7 */
 
+#define MOXA_CPLD_PULL_STATE_FLAG	0x01
 #define MOXA_CPLD_TERMINATOR_FLAG	0x02
 
 #define MOXA_CPLD_READ	0
@@ -121,6 +122,9 @@
 #define MOXA_TERMINATOR_ENABLE	1
 #define MOXA_TERMINATOR_DISABLE	0
 
+#define MOXA_PULL_STATE_ENABLE	1
+#define MOXA_PULL_STATE_DISABLE	0
+
 #define MOXA_UIR_OFFSET		0x04
 #define MOXA_UIR_RS232		0x00
 #define MOXA_UIR_RS422		0x01
@@ -509,6 +513,59 @@ static int mxpcie8250_cpld_get_terminator(resource_size_t iobar_addr, u8 port_id
 	return MOXA_TERMINATOR_DISABLE;
 }
 
+/**
+ * mxpcie8250_cpld_set_pull_state() - Set the pull state of the specified port
+ * @iobar_addr:	The base address of the GPIO I/O region
+ * @port_idx:	The port index (0 to 7)
+ * @state:	Desired pull state (MOXA_PULL_STATE_ENABLE or MOXA_PULL_STATE_DISABLE)
+ *
+ * Updates the pull state setting in the CPLD for the specified port by reading
+ * the current state, modifying the pull state bit, and writing the updated
+ * state back to the CPLD.
+ */
+static void mxpcie8250_cpld_set_pull_state(resource_size_t iobar_addr, u8 port_idx, u8 state)
+{
+	u8 addr, data;
+
+	addr = MOXA_CPLD_GET_STATE_BASE + port_idx;
+	mxpcie8250_cpld_read(iobar_addr, addr, &data);
+
+	data = data & MOXA_CPLD_STATE_MASK;
+
+	if (state == MOXA_PULL_STATE_ENABLE)
+		data |= MOXA_CPLD_PULL_STATE_FLAG;
+	else
+		data &= ~MOXA_CPLD_PULL_STATE_FLAG;
+
+	addr = MOXA_CPLD_SET_STATE_BASE + port_idx;
+	mxpcie8250_cpld_write(iobar_addr, addr, data);
+}
+
+/**
+ * mxpcie8250_cpld_get_pull_state() - Get the pull state of the specified port
+ * @iobar_addr:	The base address of the GPIO I/O region
+ * @port_idx:	The port index (0 to 7)
+ *
+ * Reads the pull state from the CPLD by accessing the appropriate GET_STATE
+ * register for the specified port using GPIO-based communication.
+ *
+ * Returns:
+ * 	MOXA_PULL_STATE_ENABLE  if pull state is enabled,
+ * 	MOXA_PULL_STATE_DISABLE if pull state is disabled.
+ */
+static int mxpcie8250_cpld_get_pull_state(resource_size_t iobar_addr, u8 port_idx)
+{
+	u8 addr, data;
+
+	addr = MOXA_CPLD_GET_STATE_BASE + port_idx;
+	mxpcie8250_cpld_read(iobar_addr, addr, &data);
+
+	if (data & MOXA_CPLD_PULL_STATE_FLAG)
+		return MOXA_PULL_STATE_ENABLE;
+
+	return MOXA_PULL_STATE_DISABLE;
+}
+
 static bool mxpcie8250_is_mini_pcie(unsigned short device)
 {
 	if (device == PCI_DEVICE_ID_MOXA_CP102N ||
@@ -1003,10 +1060,86 @@ static ssize_t mxpcie8250_terminator_show(struct device *dev,
 	return sysfs_emit(buf, "disabled\n");
 }
 
+static ssize_t mxpcie8250_pull_state_store(struct device *dev,
+					   struct device_attribute *attr,
+					   const char *buf,
+					   size_t count)
+{
+	struct tty_port *tport = dev_get_drvdata(dev);
+	struct uart_state *ustate = container_of(tport, struct uart_state, port);
+	struct uart_port *port = ustate->uart_port;
+	struct pci_dev *pdev = to_pci_dev(port->dev);
+	struct mxpcie8250 *priv = pci_get_drvdata(pdev);
+	resource_size_t iobar_addr = pci_resource_start(pdev, 2);
+	int ret;
+	u8 state;
+
+	if (!count)
+		return -EINVAL;
+
+	ret = kstrtou8(buf, 10, &state);
+
+	if (ret < 0)
+		return ret;
+
+	if (state != MOXA_PULL_STATE_ENABLE && state != MOXA_PULL_STATE_DISABLE)
+		return -EINVAL;
+
+	if (priv->port[port->port_id].interface == MOXA_UIR_RS232) {
+		dev_warn(dev, "The serial interface of this port cannot be RS232\n");
+		return -EINVAL;
+	}
+	if (pdev->device == PCI_DEVICE_ID_MOXA_CP118E_A_I ||
+	    pdev->device == PCI_DEVICE_ID_MOXA_CP138E_A   ||
+	    pdev->device == PCI_DEVICE_ID_MOXA_CP134EL_A  ||
+	    pdev->device == PCI_DEVICE_ID_MOXA_CP116E_A_A ||
+	    pdev->device == PCI_DEVICE_ID_MOXA_CP116E_A_B) {
+		spin_lock(&priv->board_lock);
+		mxpcie8250_cpld_set_pull_state(iobar_addr, port->port_id, state);
+		spin_unlock(&priv->board_lock);
+
+		return count;
+	}
+
+	return -ENODEV;
+}
+
+static ssize_t mxpcie8250_pull_state_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct tty_port *tport = dev_get_drvdata(dev);
+	struct uart_state *ustate = container_of(tport, struct uart_state, port);
+	struct uart_port *port = ustate->uart_port;
+	struct pci_dev *pdev = to_pci_dev(port->dev);
+	struct mxpcie8250 *priv = pci_get_drvdata(pdev);
+	resource_size_t iobar_addr = pci_resource_start(pdev, 2);
+	int ret;
+
+	if (pdev->device == PCI_DEVICE_ID_MOXA_CP118E_A_I ||
+	    pdev->device == PCI_DEVICE_ID_MOXA_CP138E_A   ||
+	    pdev->device == PCI_DEVICE_ID_MOXA_CP134EL_A  ||
+	    pdev->device == PCI_DEVICE_ID_MOXA_CP116E_A_A ||
+	    pdev->device == PCI_DEVICE_ID_MOXA_CP116E_A_B) {
+		spin_lock(&priv->board_lock);
+		ret = mxpcie8250_cpld_get_pull_state(iobar_addr, port->port_id);
+		spin_unlock(&priv->board_lock);
+
+		if (ret == MOXA_PULL_STATE_ENABLE)
+			return sysfs_emit(buf, "enabled\n");
+
+		return sysfs_emit(buf, "disabled\n");
+	}
+
+	return -ENODEV;
+}
+
 static DEVICE_ATTR_RW(mxpcie8250_terminator);
+static DEVICE_ATTR_RW(mxpcie8250_pull_state);
 
 static struct attribute *mxpcie8250_dev_attrs[] = {
 	&dev_attr_mxpcie8250_terminator.attr,
+	&dev_attr_mxpcie8250_pull_state.attr,
 	NULL
 };
 
-- 
2.45.2
Re: [PATCH v1 30/31] serial: 8250_mxpcie: Add sysfs to control pull state via CPLD
Posted by Andy Shevchenko 19 hours ago
On Sun, Nov 30, 2025 at 12:46 PM Crescent Hsieh
<crescentcy.hsieh@moxa.com> wrote:
>
> Some Moxa PCIe boards provide a CPLD-controlled pull/bias state.
> Expose a per-port sysfs attribute to read and set the pull state from
> userspace.
>
> This patch:
> - Defines MOXA_CPLD_PULL_STATE_FLAG and {ENABLE,DISABLE} values.
> - Implements mxpcie8250_cpld_{set,get}_pull_state() with board_lock
>   to serialize CPLD access.
> - Exports "mxpcie8250_pull_state" in the driver's attr group.
> - Restricts support to CPLD-based models (CP118E-A-I, CP138E-A,
>   CP134EL-A, CP116E-A-A, CP116E-A-B); return -ENODEV on others.
>
> Example:
>   # Enable pull state on ttyS*
>   echo 1 > /sys/class/tty/ttyS*/mxpcie8250_pull_state
>
>   # Query pull state
>   cat /sys/class/tty/ttyS*/mxpcie8250_pull_state
>   enabled

Okay, this all reminds me of the pin control subsystem. It has already
established an interface to do things, why do we need this custom one
here?

-- 
With Best Regards,
Andy Shevchenko