[PATCH v1 26/31] serial: 8250_mxpcie: add basic CPLD helper functions

Crescent Hsieh posted 31 patches 1 day, 11 hours ago
[PATCH v1 26/31] serial: 8250_mxpcie: add basic CPLD helper functions
Posted by Crescent Hsieh 1 day, 11 hours ago
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@moxa.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@moxa.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
Re: [PATCH v1 26/31] serial: 8250_mxpcie: add basic CPLD helper functions
Posted by Andy Shevchenko 19 hours ago
On Sun, Nov 30, 2025 at 12:45 PM Crescent Hsieh
<crescentcy.hsieh@moxa.com> wrote:
>
> 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.

...

> +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 */

Why?! Is the HW so buggy?

> +       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*/

MIssed space before */.

> +               mxpcie8250_cpld_enable(iobar_addr);
> +               /* Wait for CPLD timing (about 70 ns) */

> +               mdelay(1);

Why atomic?
And 70ns is more than 10x times less than 1ms!

> +               /* 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);

Ditto.

> +               /* 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--) {

  j = i
  while (j--) {

is much easier to understand.

However, the "votes" approach has to be explained "Why?". Bugs in HW?
Other stuff?

> +                       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);

This block is repeated at least three times (just with a different
direction), make it a helper and call as many times as you need.

> +               /* 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);

See above.

> +               /* 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);

See above.

> +               /* 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);

See above

> +               /* 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;
> +               }
> +       }
> +}


-- 
With Best Regards,
Andy Shevchenko