lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251130104222.63077-27-crescentcy.hsieh@moxa.com>
Date: Sun, 30 Nov 2025 18:42:17 +0800
From: Crescent Hsieh <crescentcy.hsieh@...a.com>
To: gregkh@...uxfoundation.org,
	jirislaby@...nel.org,
	ilpo.jarvinen@...ux.intel.com,
	andy.shevchenko@...il.com
Cc: linux-kernel@...r.kernel.org,
	linux-serial@...r.kernel.org,
	crescentcy.hsieh@...a.com
Subject: [PATCH v1 26/31] serial: 8250_mxpcie: add basic CPLD helper functions

Introduce a set of helper functions to access the on-board CPLD on
Moxa PCIe serial devices through the GPIO I/O space. These helpers
cover:

- Initializing the CPLD-related GPIO pins to a safe default state
- Enabling/disabling the CPLD chip select
- Switching between read/write and address/data modes
- Performing single-byte read and write transactions using GPIO
  bit-banging, with simple delay and retry logic

These functions do not affect the UART datapath and are not yet used
by the driver. They are added as a preparation step for follow-up
patches that will implement more complex CPLD-based features.

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

diff --git a/drivers/tty/serial/8250/8250_mxpcie.c b/drivers/tty/serial/8250/8250_mxpcie.c
index 6e727b77c105..88ab918fd000 100644
--- a/drivers/tty/serial/8250/8250_mxpcie.c
+++ b/drivers/tty/serial/8250/8250_mxpcie.c
@@ -6,6 +6,7 @@
  * Author: Crescent Hsieh <crescentcy.hsieh@...a.com>
  */
 
+#include <linux/delay.h>
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/tty_flip.h>
@@ -80,10 +81,18 @@
 #define MOXA_PUART_TX_FIFO_MEM	0x100	/* Memory Space to Tx FIFO Data Register */
 
 /* GPIO */
+#define MOXA_GPIO_INPUT		0x08
 #define MOXA_GPIO_DIRECTION	0x09
 #define MOXA_GPIO_OUTPUT	0x0A
 
+#define MOXA_GPIO_PIN0	BIT(0)
+#define MOXA_GPIO_PIN1	BIT(1)
 #define MOXA_GPIO_PIN2	BIT(2)
+#define MOXA_GPIO_PIN3	BIT(3)
+#define MOXA_GPIO_PIN4	BIT(4)
+#define MOXA_GPIO_PIN5	BIT(5)	/* Address/Data Pin */
+#define MOXA_GPIO_PIN6	BIT(6)	/* Read/Write Pin */
+#define MOXA_GPIO_PIN7	BIT(7)	/* Chip Select Pin */
 
 #define MOXA_GPIO_STATE_INPUT	0
 #define MOXA_GPIO_STATE_OUTPUT	1
@@ -91,6 +100,21 @@
 #define MOXA_GPIO_LOW	0
 #define MOXA_GPIO_HIGH	1
 
+/* CPLD */
+#define MOXA_CPLD_RETRY_CNT	5
+
+#define MOXA_CPLD_GET_STATE_BASE	0x10
+#define MOXA_CPLD_SET_STATE_BASE	0x18
+
+#define MOXA_CPLD_DATA_MASK	0x1F	/* Pin0 ~ Pin4 */
+#define MOXA_CPLD_CTRL_MASK	0xE0	/* Pin5 ~ Pin7 */
+
+#define MOXA_CPLD_READ	0
+#define MOXA_CPLD_WRITE	1
+
+#define MOXA_CPLD_ADDRESS	0
+#define MOXA_CPLD_DATA		1
+
 #define MOXA_UIR_OFFSET		0x04
 #define MOXA_UIR_RS232		0x00
 #define MOXA_UIR_RS422		0x01
@@ -211,6 +235,218 @@ static void mxpcie8250_gpio_get_all(resource_size_t iobar_addr, u8 *data, u8 off
 	*data = inb(iobar_addr + offset);
 }
 
+/**
+ * mxpcie8250_cpld_init() - Initialize CPLD control GPIO pins
+ * @iobar_addr:	The base address of the GPIO I/O region
+ *
+ * Initialize the GPIO pins used to control the CPLD. Also sets all GPIO pins
+ * to output and drives them HIGH to ensure a safe default state.
+ */
+static void mxpcie8250_cpld_init(resource_size_t iobar_addr)
+{
+	mxpcie8250_gpio_init(iobar_addr);
+
+	mxpcie8250_gpio_set(iobar_addr, MOXA_GPIO_PIN7, MOXA_GPIO_HIGH);
+	mxpcie8250_gpio_set(iobar_addr, MOXA_GPIO_PIN6, MOXA_GPIO_HIGH);
+	mxpcie8250_gpio_set(iobar_addr, MOXA_GPIO_PIN5, MOXA_GPIO_HIGH);
+	mxpcie8250_gpio_set(iobar_addr, MOXA_GPIO_PIN0, MOXA_GPIO_HIGH);
+	mxpcie8250_gpio_set(iobar_addr, MOXA_GPIO_PIN1, MOXA_GPIO_HIGH);
+	mxpcie8250_gpio_set(iobar_addr, MOXA_GPIO_PIN2, MOXA_GPIO_HIGH);
+	mxpcie8250_gpio_set(iobar_addr, MOXA_GPIO_PIN3, MOXA_GPIO_HIGH);
+	mxpcie8250_gpio_set(iobar_addr, MOXA_GPIO_PIN4, MOXA_GPIO_HIGH);
+}
+
+/**
+ * mxpcie8250_cpld_enable() - Enable the CPLD
+ * @iobar_addr:	The base address of the GPIO I/O region
+ *
+ * Enables the CPLD by pulling the chip select pin low.
+ */
+static void mxpcie8250_cpld_enable(resource_size_t iobar_addr)
+{
+	mxpcie8250_gpio_set(iobar_addr, MOXA_GPIO_PIN7, MOXA_GPIO_LOW);
+}
+
+/**
+ * mxpcie8250_cpld_disable() - Disable the CPLD and reset all GPIO pins
+ * @iobar_addr:	The base address of the GPIO I/O region
+ *
+ * Disables the CPLD by pulling the chip select pin high. Also resets all GPIO
+ * pins to output and drives them HIGH to ensure a safe default state.
+ */
+static void mxpcie8250_cpld_disable(resource_size_t iobar_addr)
+{
+	mxpcie8250_gpio_set(iobar_addr, MOXA_GPIO_PIN7, MOXA_GPIO_HIGH);
+
+	/* Set all GPIO pins to output state and pull them HIGH */
+	mxpcie8250_gpio_set_all(iobar_addr, 0xff, MOXA_GPIO_DIRECTION);
+	mxpcie8250_gpio_set_all(iobar_addr, 0xff, MOXA_GPIO_OUTPUT);
+}
+
+/**
+ * mxpcie8250_cpld_set_direction() - Set CPLD read/write direction
+ * @iobar_addr:	The base address of the GPIO I/O region
+ * @direction:	Desired CPLD direction (MOXA_CPLD_READ or MOXA_CPLD_WRITE)
+ *
+ * Sets the CPLD read/write direction by changing the state of the read/write
+ * control pin.
+ */
+static void mxpcie8250_cpld_set_direction(resource_size_t iobar_addr, int direction)
+{
+	if (direction == MOXA_CPLD_READ)
+		mxpcie8250_gpio_set(iobar_addr, MOXA_GPIO_PIN6, MOXA_GPIO_HIGH);
+	else
+		mxpcie8250_gpio_set(iobar_addr, MOXA_GPIO_PIN6, MOXA_GPIO_LOW);
+}
+
+/**
+ * mxpcie8250_cpld_set_mode() - Set CPLD address/data mode
+ * @iobar_addr:	The base address of the GPIO I/O region
+ * @mode:	Desired CPLD mode (MOXA_CPLD_ADDRESS or MOXA_CPLD_DATA)
+ *
+ * Sets the CPLD addr/data mode by changing the state of the address/data
+ * control pin.
+ */
+static void mxpcie8250_cpld_set_mode(resource_size_t iobar_addr, int mode)
+{
+	if (mode == MOXA_CPLD_ADDRESS)
+		mxpcie8250_gpio_set(iobar_addr, MOXA_GPIO_PIN5, MOXA_GPIO_LOW);
+	else
+		mxpcie8250_gpio_set(iobar_addr, MOXA_GPIO_PIN5, MOXA_GPIO_HIGH);
+}
+
+/**
+ * mxpcie8250_cpld_read() - Read a byte from the CPLD at a specified address
+ * @iobar_addr:	The base address of the GPIO I/O region
+ * @addr:	Address in the CPLD to read from
+ * @data:	The buffer to store the read value
+ *
+ * Reads a single byte of data from the CPLD at the given address using
+ * GPIO-based communication.
+ */
+static void mxpcie8250_cpld_read(resource_size_t iobar_addr, u8 addr, u8 *data)
+{
+	u8 saved_state, new_state;
+	u8 samples[MOXA_CPLD_RETRY_CNT], votes[MOXA_CPLD_RETRY_CNT];
+	int i, j;
+
+	/* Perform multiple read attempts with majority voting */
+	for (i = 0; i < MOXA_CPLD_RETRY_CNT; i++) {
+		/* Set read/write pin to read state */
+		mxpcie8250_cpld_set_direction(iobar_addr, MOXA_CPLD_READ);
+		/* Set address/data bus pins to output for address phase */
+		mxpcie8250_gpio_set_direction(iobar_addr, MOXA_GPIO_PIN0, MOXA_GPIO_STATE_OUTPUT);
+		mxpcie8250_gpio_set_direction(iobar_addr, MOXA_GPIO_PIN1, MOXA_GPIO_STATE_OUTPUT);
+		mxpcie8250_gpio_set_direction(iobar_addr, MOXA_GPIO_PIN2, MOXA_GPIO_STATE_OUTPUT);
+		mxpcie8250_gpio_set_direction(iobar_addr, MOXA_GPIO_PIN3, MOXA_GPIO_STATE_OUTPUT);
+		mxpcie8250_gpio_set_direction(iobar_addr, MOXA_GPIO_PIN4, MOXA_GPIO_STATE_OUTPUT);
+		/* Backup current GPIO output state */
+		mxpcie8250_gpio_get_all(iobar_addr, &saved_state, MOXA_GPIO_OUTPUT);
+		/* Prepare address to GPIO bus */
+		new_state = saved_state & MOXA_CPLD_CTRL_MASK;
+		new_state |= (addr & MOXA_CPLD_DATA_MASK);
+		/* Output address to GPIO bus */
+		mxpcie8250_gpio_set_all(iobar_addr, new_state, MOXA_GPIO_OUTPUT);
+		/* Switch to address mode (address/data pin) */
+		mxpcie8250_cpld_set_mode(iobar_addr, MOXA_CPLD_ADDRESS);
+		/* Enable CPLD by pulling chip select pin low*/
+		mxpcie8250_cpld_enable(iobar_addr);
+		/* Wait for CPLD timing (about 70 ns) */
+		mdelay(1);
+		/* Switch to data mode (address/data pin) */
+		mxpcie8250_cpld_set_mode(iobar_addr, MOXA_CPLD_DATA);
+		/* Set address/data bus pins to input for data phase */
+		mxpcie8250_gpio_set_direction(iobar_addr, MOXA_GPIO_PIN0, MOXA_GPIO_STATE_INPUT);
+		mxpcie8250_gpio_set_direction(iobar_addr, MOXA_GPIO_PIN1, MOXA_GPIO_STATE_INPUT);
+		mxpcie8250_gpio_set_direction(iobar_addr, MOXA_GPIO_PIN2, MOXA_GPIO_STATE_INPUT);
+		mxpcie8250_gpio_set_direction(iobar_addr, MOXA_GPIO_PIN3, MOXA_GPIO_STATE_INPUT);
+		mxpcie8250_gpio_set_direction(iobar_addr, MOXA_GPIO_PIN4, MOXA_GPIO_STATE_INPUT);
+		/* Wait for CPLD timing (about 70 ns) */
+		mdelay(1);
+		/* Read data bus pins */
+		mxpcie8250_gpio_get_all(iobar_addr, data, MOXA_GPIO_INPUT);
+		*data &= MOXA_CPLD_DATA_MASK;
+		/* No need to restore read/write pin (defaults to read); disable CPLD */
+		mxpcie8250_cpld_disable(iobar_addr);
+		/* Store read value for voting */
+		samples[i] = *data;
+		votes[i] = 0;
+
+		for (j = i - 1; j >= 0; j--) {
+			if (samples[j] == samples[i])
+				votes[i]++;
+		}
+		/* Perform majority voting to select stable value */
+		if (votes[i] >= (MOXA_CPLD_RETRY_CNT / 2))
+			break;
+	}
+}
+
+/**
+ * mxpcie8250_cpld_write() - Write a byte to the CPLD at a specified address
+ * @iobar_addr:	The base address of the GPIO I/O region
+ * @addr:	Address in the CPLD to write to
+ * @data:	Data byte to write
+ *
+ * Writes a single byte of data to the CPLD at the given address using
+ * GPIO-based communication. Includes verification with optional retry.
+ */
+static void mxpcie8250_cpld_write(resource_size_t iobar_addr, u8 addr, u8 data)
+{
+	u8 saved_state, new_state, verify_data;
+	int retry_cnt;
+
+	for (retry_cnt = 0; retry_cnt < MOXA_CPLD_RETRY_CNT; retry_cnt++) {
+		/* Set read/write pin to write state */
+		mxpcie8250_cpld_set_direction(iobar_addr, MOXA_CPLD_WRITE);
+		/* Set data bus pins to output for address phase */
+		mxpcie8250_gpio_set_direction(iobar_addr, MOXA_GPIO_PIN0, MOXA_GPIO_STATE_OUTPUT);
+		mxpcie8250_gpio_set_direction(iobar_addr, MOXA_GPIO_PIN1, MOXA_GPIO_STATE_OUTPUT);
+		mxpcie8250_gpio_set_direction(iobar_addr, MOXA_GPIO_PIN2, MOXA_GPIO_STATE_OUTPUT);
+		mxpcie8250_gpio_set_direction(iobar_addr, MOXA_GPIO_PIN3, MOXA_GPIO_STATE_OUTPUT);
+		mxpcie8250_gpio_set_direction(iobar_addr, MOXA_GPIO_PIN4, MOXA_GPIO_STATE_OUTPUT);
+		/* Backup current GPIO output state */
+		mxpcie8250_gpio_get_all(iobar_addr, &saved_state, MOXA_GPIO_OUTPUT);
+		/* Prepare bus value with address bits */
+		new_state = saved_state & MOXA_CPLD_CTRL_MASK;
+		new_state |= (addr & MOXA_CPLD_DATA_MASK);
+		/* Output address to GPIO bus */
+		mxpcie8250_gpio_set_all(iobar_addr, new_state, MOXA_GPIO_OUTPUT);
+		/* Switch to address mode (address/data pin)*/
+		mxpcie8250_cpld_set_mode(iobar_addr, MOXA_CPLD_ADDRESS);
+		/* Enable CPLD by pulling chip select pin low */
+		mxpcie8250_cpld_enable(iobar_addr);
+		/* Wait for CPLD timing (about 70 ns) */
+		mdelay(1);
+		/* Set data bus pins to output for data phase */
+		mxpcie8250_gpio_set_direction(iobar_addr, MOXA_GPIO_PIN0, MOXA_GPIO_STATE_OUTPUT);
+		mxpcie8250_gpio_set_direction(iobar_addr, MOXA_GPIO_PIN1, MOXA_GPIO_STATE_OUTPUT);
+		mxpcie8250_gpio_set_direction(iobar_addr, MOXA_GPIO_PIN2, MOXA_GPIO_STATE_OUTPUT);
+		mxpcie8250_gpio_set_direction(iobar_addr, MOXA_GPIO_PIN3, MOXA_GPIO_STATE_OUTPUT);
+		mxpcie8250_gpio_set_direction(iobar_addr, MOXA_GPIO_PIN4, MOXA_GPIO_STATE_OUTPUT);
+		/* Switch to data mode (address/data pin) */
+		mxpcie8250_cpld_set_mode(iobar_addr, MOXA_CPLD_DATA);
+		/* Backup current GPIO output state */
+		mxpcie8250_gpio_get_all(iobar_addr, &saved_state, MOXA_GPIO_OUTPUT);
+		/* Prepare bus value with data bits */
+		new_state = saved_state & MOXA_CPLD_CTRL_MASK;
+		new_state |= (data & MOXA_CPLD_DATA_MASK);
+		/* Output data to GPIO bus */
+		mxpcie8250_gpio_set_all(iobar_addr, new_state, MOXA_GPIO_OUTPUT);
+		/* Wait for CPLD timing (about 70 ns) */
+		mdelay(1);
+		/* Disable CPLD by releasing chip select pin */
+		mxpcie8250_cpld_disable(iobar_addr);
+
+		if (addr & MOXA_CPLD_SET_STATE_BASE) {
+			mxpcie8250_cpld_read(iobar_addr, ((addr & ~MOXA_CPLD_SET_STATE_BASE) | MOXA_CPLD_GET_STATE_BASE), &verify_data);
+
+			if (verify_data == data)
+				break;
+		}
+	}
+}
+
 static bool mxpcie8250_is_mini_pcie(unsigned short device)
 {
 	if (device == PCI_DEVICE_ID_MOXA_CP102N ||
@@ -585,6 +821,10 @@ static int mxpcie8250_init(struct pci_dev *pdev)
 	resource_size_t iobar_addr = pci_resource_start(pdev, 2);
 	u8 cval;
 
+	mxpcie8250_cpld_init(iobar_addr);
+
+	outb(0x0f, iobar_addr + MOXA_GPIO_DIRECTION);
+
 	/* Initial terminator */
 	if (pdev->device == PCI_DEVICE_ID_MOXA_CP114EL ||
 	    pdev->device == PCI_DEVICE_ID_MOXA_CP118EL_A) {
-- 
2.45.2


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ