Re: [PATCH v7] ptp: ocp: add CPLD ISP support for ADVA TimeCard X1

From: Vadim Fedorenko

Date: Thu Jul 30 2026 - 17:43:14 EST


On 30.07.2026 14:43, Sagi Maimon wrote:
The ADVA TimeCard X1 (PCI device 0x0410) uses a Lattice MachXO3 CPLD
that is programmed over I2C using in-system programming (ISP).

The CPLD is connected to a secondary I2C bus shared with the onboard
MicroBlaze soft CPU.

Add support for CPLD access and firmware updates on the ADVA TimeCard X1
board by arbitration of the shared I2C bus, CPLD ISP command handling,
status polling, and firmware upload operations using the firmware-upload
subsystem.

Add the following X1-only user-visible interfaces:

/sys/class/timecard/ocpN/cpld_device_id
report the 32-bit Lattice MachXO3 CPLD device ID

/sys/class/timecard/ocpN/cpld_status
report the CPLD status register, including the DONE,
BUSY, and FAILED indicators

Firmware updates are performed through the firmware-upload framework,
which acquires ownership of the shared I2C bus, erases the CPLD
configuration flash, programs the image page-by-page, and activates
the new image using the MachXO3 REFRESH command.

All CPLD operations are serialized and coordinated with the MicroBlaze
firmware to ensure exclusive access to the shared I2C bus. The added
interfaces are available only on ADVA TimeCard X1 boards.

Signed-off-by: Sagi Maimon <maimon.sagi@xxxxxxxxx>

[...]

+ * Send a 4-byte command then read data back without an intermediate STOP
+ * (Lattice combined write→repeated-START→read).
+ */
+static int adva_x1_cpld_cmd_read(struct ptp_ocp *bp,
+ u32 cmd_be, u8 *out, u8 out_len)
+{
+ u8 cmd[4] = {
+ (cmd_be >> 24) & 0xFF,
+ (cmd_be >> 16) & 0xFF,
+ (cmd_be >> 8) & 0xFF,
+ cmd_be & 0xFF,
+ };

this is open-coded be32_to_cpu(), please, convert the code to use it.
maybe make adva_x1_i2c_xfer() buffer pointers as void* to avoid extra cast.

+ return adva_x1_i2c_xfer(bp, ADVA_CPLD_ADDR, cmd, 4, out, out_len, true);
+}
+
+static int adva_x1_cpld_read_status(struct ptp_ocp *bp, u32 *status)
+{
+ u8 buf[4];
+ int ret;
+
+ ret = adva_x1_cpld_cmd_read(bp, CPLD_CMD_READ_STATUS, buf, 4);
+ if (ret)
+ return ret;
+ *status = ((u32)buf[0] << 24) | ((u32)buf[1] << 16) |
+ ((u32)buf[2] << 8) | (u32)buf[3];

here we have cpu_to_be32() I believe - please use lib functions.

+ return 0;
+}
+
+static int adva_x1_cpld_wait_ready(struct ptp_ocp *bp, unsigned int max_ms)
+{
+ u32 status;
+ unsigned int elapsed = 0;
+
+ while (elapsed < max_ms) {
+ if (adva_x1_cpld_read_status(bp, &status))
+ return -EIO;
+ if (status & CPLD_STATUS_FAILED)
+ return -EIO;
+ if (!(status & CPLD_STATUS_BUSY))
+ return 0;
+ usleep_range(100000, 101000);
+ elapsed += 100;
+ }
+ return -ETIMEDOUT;
+}
+
+/*
+ * cpld_device_id - show the Lattice device ID of the TAP CPLD.
+ *
+ * Returns the 32-bit ID as a hex string, e.g. "0x612bc043\n".
+ * Lattice LCMXO3LF-210 reports 0x612BC043.
+ */
+static ssize_t
+cpld_device_id_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct ptp_ocp *bp = dev_get_drvdata(dev);
+ u8 data[4];
+ u32 id = 0;
+ int ret;
+
+ mutex_lock(&bp->cpld_lock);
+ ret = adva_x1_mblaze_acquire(bp);
+ if (ret)
+ goto out;
+ ret = adva_x1_mux_select(bp, ADVA_MUX_CHANNEL);
+ if (ret)
+ goto release;
+ ret = adva_x1_cpld_cmd_read(bp, CPLD_CMD_READ_ID, data, 4);
+ if (!ret)
+ id = ((u32)data[0] << 24) | ((u32)data[1] << 16) |
+ ((u32)data[2] << 8) | (u32)data[3];

ditto

+ adva_x1_mux_select(bp, -1);
+release:
+ adva_x1_mblaze_release(bp);
+out:
+ mutex_unlock(&bp->cpld_lock);
+ return ret ? ret : sysfs_emit(buf, "0x%08x\n", id);
+}
+static DEVICE_ATTR_RO(cpld_device_id);
+