[<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