AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/14 07:27 flow patch-triage
1m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "The patch modifies documentation and the ptp_ocp driver (drivers/ptp/ptp_ocp.c), adding CPLD flash and status support for ADVA TimeCard X1 devices. This driver binds specifically to vendor-specific PCIe timecard hardware (such as ADVA TimeCard X1, Meta Timecard) that is not emulated in standard QEMU or GCE virtualization environments. As such, the driver cannot be probed and the modified code is structurally unreachable for syzkaller fuzzing.",
  "WorthFuzzing": false
}

1/1 2026/09/14 07:27 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 73bee881681bb3e5540a748b5cece5bb408bc78d\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Mon Sep 14 07:27:18 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/Documentation/ABI/testing/sysfs-timecard b/Documentation/ABI/testing/sysfs-timecard\nindex 3ae41b7634ac9..6ddca452bc633 100644\n--- a/Documentation/ABI/testing/sysfs-timecard\n+++ b/Documentation/ABI/testing/sysfs-timecard\n@@ -11,6 +11,34 @@ Contact:\tJonathan Lemon \u003cjonathan.lemon@gmail.com\u003e\n Description:\tThis directory contains the attributes of the Nth timecard\n \t\tregistered.\n \n+What:\t\t/sys/class/timecard/ocpN/cpld_status\n+Date:\t\tJuly 2026\n+Contact:\tSagi Maimon \u003cmaimon.sagi@gmail.com\u003e\n+Description:\t(RO, root only) The flags set in the status register of the\n+\t\tTAP CPLD, space separated, or an empty line when none is\n+\t\tset.  Only present on ADVA x1 TAP boards (PCI ID\n+\t\t0xad5a:0x0410).\n+\n+\t\t==========  ================================================\n+\t\tdone        the configuration flash was programmed\n+\t\t\t    successfully and is active\n+\t\tbusy        an internal operation is in progress\n+\t\tfailed      the last ISC operation failed\n+\t\t==========  ================================================\n+\n+\t\tFor example \"busy\" while programming, or \"busy failed\" if a\n+\t\tfailure is latched while another operation runs.\n+\n+\t\tA read arbitrates for the shared I2C bus and reprograms the\n+\t\ton-card mux, so it is restricted to root.  The Lattice device\n+\t\tID of the CPLD is read once by the driver, shortly after\n+\t\tprobe, and reported from that cached value as the fixed\n+\t\t\"cpld.id\" version by devlink dev info.\n+\n+\t\tNew CPLD firmware is programmed with devlink dev flash,\n+\t\tselecting the \"fw.cpld\" component; see\n+\t\tDocumentation/networking/devlink/ptp_ocp.rst.\n+\n What:\t\t/sys/class/timecard/ocpN/available_clock_sources\n Date:\t\tSeptember 2021\n Contact:\tJonathan Lemon \u003cjonathan.lemon@gmail.com\u003e\ndiff --git a/Documentation/networking/devlink/index.rst b/Documentation/networking/devlink/index.rst\nindex d4a83fdcff7fe..1af780c811ee2 100644\n--- a/Documentation/networking/devlink/index.rst\n+++ b/Documentation/networking/devlink/index.rst\n@@ -99,6 +99,7 @@ parameters, info versions, and other features it supports.\n    nfp\n    octeontx2\n    prestera\n+   ptp_ocp\n    qed\n    sfc\n    stmmac\ndiff --git a/Documentation/networking/devlink/ptp_ocp.rst b/Documentation/networking/devlink/ptp_ocp.rst\nnew file mode 100644\nindex 0000000000000..37e9f1ebd1a81\n--- /dev/null\n+++ b/Documentation/networking/devlink/ptp_ocp.rst\n@@ -0,0 +1,70 @@\n+.. SPDX-License-Identifier: GPL-2.0\n+\n+========================\n+ptp_ocp devlink support\n+========================\n+\n+This document describes the devlink features implemented by the ``ptp_ocp``\n+device driver: the info versions it reports and its flash update support.\n+\n+Info versions\n+=============\n+\n+The ``ptp_ocp`` driver reports the following versions\n+\n+.. list-table:: devlink info versions implemented\n+   :widths: 5 5 90\n+\n+   * - Name\n+     - Type\n+     - Description\n+   * - ``fw``\n+     - running\n+     - Version of the firmware running on the card.  Reported as ``loader``\n+       instead when the card is running the firmware loader.\n+   * - ``board.id``\n+     - fixed\n+     - Board identifier, read from the on-card EEPROM.\n+   * - ``cpld.id``\n+     - fixed\n+     - Lattice device ID (IDCODE) of the TAP CPLD, formatted as ``0x%08x``.\n+       Only present on ADVA TimeCard X1 boards, which are the only ones\n+       carrying that CPLD.  Reading it claims the shared I2C bus and\n+       reprograms the on-card mux, so the driver does that once from its\n+       own worker and reports the cached value here; the version is\n+       omitted until that read has succeeded.\n+   * - ``fw.cpld``\n+     - running\n+     - USERCODE of the image programmed into the TAP CPLD, formatted as\n+       ``0x%08x``.  Read together with ``cpld.id`` and reported the same\n+       way.  This is the component name to pass to ``devlink dev flash``\n+       to update the CPLD.\n+\n+Flash update\n+============\n+\n+The driver implements ``devlink dev flash`` for two separate targets,\n+selected with the component name.\n+\n+.. list-table:: Flash components\n+   :widths: 20 80\n+\n+   * - Component\n+     - Description\n+   * - (none)\n+     - The card's own flash, written through the SPI controller the driver\n+       exposes.  The card runs the new image after its next reset.\n+   * - ``fw.cpld``\n+     - The configuration flash of the TAP CPLD on ADVA TimeCard X1 boards,\n+       programmed over I2C with the MachXO3 in-system programming commands\n+       and activated with a REFRESH, so the new image runs immediately.\n+       The image is the raw configuration bitstream, a whole number of\n+       16-byte pages; container formats such as ``.jed`` are rejected.\n+\n+Programming the CPLD claims the shared I2C bus for the whole cycle, so\n+reads of the card's EEPROM block until it completes.  Progress is reported\n+with the standard devlink status notifications.\n+\n+Example::\n+\n+    $ devlink dev flash pci/0000:02:00.0 file adva-cpld.bin component fw.cpld\ndiff --git a/MAINTAINERS b/MAINTAINERS\nindex 0e04d92d1b098..113f314346177 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -20479,6 +20479,7 @@ OPENCOMPUTE PTP CLOCK DRIVER\n M:\tVadim Fedorenko \u003cvadim.fedorenko@linux.dev\u003e\n L:\tnetdev@vger.kernel.org\n S:\tMaintained\n+F:\tDocumentation/networking/devlink/ptp_ocp.rst\n F:\tdrivers/ptp/ptp_ocp.c\n \n OPENCORES I2C BUS DRIVER\ndiff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c\nindex 3d26ec1f7b9ec..58b90b2b282fe 100644\n--- a/drivers/ptp/ptp_ocp.c\n+++ b/drivers/ptp/ptp_ocp.c\n@@ -20,10 +20,14 @@\n #include \u003clinux/spi/altera.h\u003e\n #include \u003cnet/devlink.h\u003e\n #include \u003clinux/i2c.h\u003e\n+#include \u003clinux/iopoll.h\u003e\n #include \u003clinux/mtd/mtd.h\u003e\n #include \u003clinux/nvmem-consumer.h\u003e\n #include \u003clinux/crc16.h\u003e\n #include \u003clinux/dpll.h\u003e\n+#include \u003clinux/unaligned.h\u003e\n+#include \u003clinux/delay.h\u003e\n+#include \u003clinux/firmware.h\u003e\n \n #define PCI_DEVICE_ID_META_TIMECARD\t\t0x0400\n \n@@ -85,6 +89,8 @@ struct ptp_ocp_adva_info {\n \tu8\t\t\t\tsignals_nr;\n \tu8\t\t\t\tfreq_in_nr;\n \tconst struct ocp_attr_group\t*attr_groups;\n+\t/* x1: TAP CPLD present */\n+\tbool\t\t\t\thas_cpld;\n };\n \n #define OCP_CTRL_ENABLE\t\tBIT(0)\n@@ -163,7 +169,8 @@ struct gpio_reg {\n \tu32\tgpio1;\n \tu32\t__pad0;\n \tu32\tgpio2;\n-\tu32\t__pad1;\n+\t/* adva_x1: I2C bus ownership register; reserved on other variants */\n+\tu32\ti2c_bus_ctrl;\n };\n \n struct irig_master_reg {\n@@ -416,6 +423,27 @@ struct ptp_ocp {\n \tdpll_tracker tracker;\n \tint signals_nr;\n \tint freq_in_nr;\n+\t/* adva_x1 CPLD I2C (internal use only) */\n+\t/* serialises CPLD operations */\n+\tstruct mutex\t\tcpld_lock;\n+\t/* I2C adapter nr; -1 if absent */\n+\tint\t\t\tcpld_i2c_adap_nr;\n+\t/* claimed adapter; valid under cpld_lock */\n+\tstruct i2c_adapter\t*cpld_adap;\n+\t/* scratch, write half then read half; valid under cpld_lock */\n+\tu8\t\t\t*cpld_buf;\n+\t/* Lattice device ID; 0 if unread */\n+\tu32\t\t\tcpld_id;\n+\t/* USERCODE of the programmed image; valid once cpld_id is set */\n+\tu32\t\t\tcpld_usercode;\n+\t/* the one-shot ID read is finished, successfully or not */\n+\tbool\t\t\tcpld_id_tried;\n+\t/* failed ID read attempts so far */\n+\tunsigned int\t\tcpld_id_attempts;\n+\t/* x1 TAP CPLD present */\n+\tbool\t\t\thas_cpld;\n+\t/* EN_CFG_TP issued but not yet REFRESH'd */\n+\tbool\t\t\tcpld_in_config_mode;\n };\n \n #define OCP_REQ_TIMESTAMP\tBIT(0)\n@@ -452,6 +480,13 @@ static int ptp_ocp_adva_board_init(struct ptp_ocp *bp, struct ocp_resource *r);\n static const struct ocp_sma_op ocp_adva_sma_op;\n static const struct ocp_sma_op ocp_adva_x1_sma_op;\n \n+/* Flash component naming the CPLD image, as reported by -\u003einfo_get(). */\n+#define ADVA_CPLD_COMPONENT\t\"fw.cpld\"\n+\n+static int adva_x1_cpld_flash(struct ptp_ocp *bp, struct devlink *devlink,\n+\t\t\t      const struct firmware *fw,\n+\t\t\t      struct netlink_ext_ack *extack);\n+\n static const struct ocp_attr_group fb_timecard_groups[];\n \n static const struct ocp_attr_group art_timecard_groups[];\n@@ -1273,6 +1308,7 @@ static struct ocp_resource ocp_adva_x1_resource[] = {\n \t\t\t.signals_nr   = 4,\n \t\t\t.freq_in_nr   = 4,\n \t\t\t.attr_groups  = adva_timecard_x1_groups,\n+\t\t\t.has_cpld     = true,\n \t\t},\n \t},\n \t{ }\n@@ -2133,6 +2169,17 @@ ptp_ocp_devlink_flash_update(struct devlink *devlink,\n \tconst char *msg;\n \tint err;\n \n+\tif (params-\u003ecomponent) {\n+\t\tif (!bp-\u003ehas_cpld ||\n+\t\t    strcmp(params-\u003ecomponent, ADVA_CPLD_COMPONENT)) {\n+\t\t\tNL_SET_ERR_MSG_MOD(extack,\n+\t\t\t\t\t   \"unsupported flash component\");\n+\t\t\treturn -EOPNOTSUPP;\n+\t\t}\n+\n+\t\treturn adva_x1_cpld_flash(bp, devlink, params-\u003efw, extack);\n+\t}\n+\n \tdev = ptp_ocp_find_flash(bp);\n \tif (!dev) {\n \t\tdev_err(\u0026bp-\u003epdev-\u003edev, \"Can't find Flash SPI adapter\\n\");\n@@ -2155,10 +2202,13 @@ static int\n ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,\n \t\t\t struct netlink_ext_ack *extack)\n {\n+\tenum devlink_info_version_type ver_type =\n+\t\t\t\t\tDEVLINK_INFO_VERSION_TYPE_COMPONENT;\n \tstruct ptp_ocp *bp = devlink_priv(devlink);\n \tconst char *fw_image;\n \tchar buf[32];\n \tint err;\n+\tu32 id;\n \n \tfw_image = bp-\u003efw_loader ? \"loader\" : \"fw\";\n \tsprintf(buf, \"%d.%d\", bp-\u003efw_tag, bp-\u003efw_version);\n@@ -2166,6 +2216,28 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,\n \tif (err)\n \t\treturn err;\n \n+\t/* Read by the worker, not here: this command is unprivileged and\n+\t * reading the ID claims the I2C bus.  0 means unread - skip it\n+\t * rather than fail, devlink discards the whole reply on error.\n+\t */\n+\tid = READ_ONCE(bp-\u003ecpld_id);\n+\tif (bp-\u003ehas_cpld \u0026\u0026 id) {\n+\t\tsprintf(buf, \"0x%08x\", id);\n+\t\terr = devlink_info_version_fixed_put(req, \"cpld.id\", buf);\n+\t\tif (err)\n+\t\t\treturn err;\n+\n+\t\t/* The flashable component: naming it here is what lets\n+\t\t * \"devlink dev flash ... component fw.cpld\" through, as the\n+\t\t * core matches the name against the versions reported here.\n+\t\t */\n+\t\tsprintf(buf, \"0x%08x\", READ_ONCE(bp-\u003ecpld_usercode));\n+\t\terr = devlink_info_version_running_put_ext(req, \"fw.cpld\", buf,\n+\t\t\t\t\t\t\t   ver_type);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n+\n \tif (!bp-\u003ehas_eeprom_data) {\n \t\tptp_ocp_read_eeprom(bp);\n \t\tif (!bp-\u003ehas_eeprom_data)\n@@ -3199,6 +3271,8 @@ ptp_ocp_adva_board_init(struct ptp_ocp *bp, struct ocp_resource *r)\n \t\treturn err;\n \tptp_ocp_sma_init(bp);\n \n+\tbp-\u003ehas_cpld = info-\u003ehas_cpld;\n+\n \treturn ptp_ocp_init_clock(bp, \u0026info-\u003eservo);\n }\n \n@@ -4226,6 +4300,648 @@ static const struct ocp_attr_group art_timecard_groups[] = {\n \t{ },\n };\n \n+/*\n+ * adva_x1 TAP CPLD (Lattice LCMXO3LF-2100) helpers.\n+ *\n+ * The card has two I2C controllers; Linux registers only 0x00150000.\n+ * The i2c_bus_ctrl handshake re-routes what each is wired to:\n+ *\n+ *   grant 0:  0x150000 -\u003e EEPROMs     0x120000 -\u003e TMC or M.2, MB's choice\n+ *   grant 1:  0x150000 -\u003e TMC bus     0x120000 -\u003e M.2\n+ *\n+ * The PCA9548 at 0x74 and the CPLD at 0x40 behind its channel 0 are on the\n+ * TMC bus, reachable only while the grant is held.  The EEPROMs are not\n+ * behind the adapter for that window, so a CPLD operation holds cpld_lock\n+ * and the adapter lock to keep the EEPROM and nvmem paths off it.\n+ */\n+\n+#define ADVA_MUX_ADDR     0x74\n+#define ADVA_CPLD_ADDR    0x40\n+#define ADVA_MUX_CHANNEL  0\n+\n+/* Longest command or reply; bounds each half of the per-claim scratch. */\n+#define ADVA_CPLD_XFER_MAX 32\n+\n+#define MBLAZE_REQUEST    0x0000aaaaU\n+#define MBLAZE_GRANTED    0x5555aaaaU\n+#define MBLAZE_RELEASE    0x55550000U\n+#define MBLAZE_RETRIES    200\n+#define MBLAZE_RETRY_US   10000\n+\n+/* Lattice LCMXO3LF ISC command codes */\n+#define CPLD_CMD_READ_ID      0xE0000000UL\n+#define CPLD_CMD_READ_USERCODE 0xC0000000UL\n+#define CPLD_CMD_READ_STATUS  0x3C000000UL\n+#define CPLD_CMD_EN_CFG_TP    0x74   /* enable config, transparent mode */\n+#define CPLD_CMD_DIS_CFG      0x26\n+#define CPLD_CMD_ERASE        0x0E\n+#define CPLD_CMD_RESET_ADDR   0x46\n+#define CPLD_CMD_WRITE_PAGE   0x70\n+#define CPLD_CMD_SET_DONE     0x5E\n+#define CPLD_CMD_REFRESH      0x79\n+#define CPLD_PAGE_SIZE        16\n+#define CPLD_POLL_US          10000  /* status poll interval while busy */\n+#define CPLD_ERASE_MS         15000  /* config sector erase, datasheet max */\n+#define CPLD_ID_MAX_ATTEMPTS  10     /* one per sync_work tick */\n+\n+/* Status register bit positions (Lattice LCMXO3LF datasheet) */\n+#define CPLD_STATUS_DONE   BIT(8)\n+#define CPLD_STATUS_BUSY   BIT(12)\n+#define CPLD_STATUS_FAILED BIT(13)\n+#define CPLD_STATUS_ERR    GENMASK(25, 23)\n+\n+/*\n+ * Issue one I2C transaction on the TMC bus: @cmd if not negative, then\n+ * @wlen bytes of @wdata (NULL sends zeros), then @rlen bytes read back.\n+ * The message is built in the scratch buffer from adva_x1_bus_claim(), so\n+ * an upload costs one allocation rather than one per page.  I2C_M_DMA_SAFE\n+ * is not set: the two halves are not separately aligned, so let the core\n+ * bounce.  Caller holds the claim, hence __i2c_transfer().\n+ */\n+static int adva_x1_i2c_xfer(struct ptp_ocp *bp, u8 addr, int cmd,\n+\t\t\t    const void *wdata, u8 wlen,\n+\t\t\t    void *rdata, u8 rlen)\n+{\n+\tu8 *wbuf, *rbuf;\n+\tstruct i2c_adapter *adap;\n+\tstruct i2c_msg msgs[2];\n+\tint nmsgs = 0, ret;\n+\tu16 hdr = cmd \u003e= 0;\n+\n+\tlockdep_assert_held(\u0026bp-\u003ecpld_lock);\n+\n+\tadap = bp-\u003ecpld_adap;\n+\tif (!adap || !bp-\u003ecpld_buf)\n+\t\treturn -ENODEV;\n+\n+\tif (hdr + wlen \u003e ADVA_CPLD_XFER_MAX || rlen \u003e ADVA_CPLD_XFER_MAX)\n+\t\treturn -EINVAL;\n+\n+\twbuf = bp-\u003ecpld_buf;\n+\trbuf = bp-\u003ecpld_buf + ADVA_CPLD_XFER_MAX;\n+\n+\tif (hdr + wlen) {\n+\t\tif (hdr)\n+\t\t\twbuf[0] = cmd;\n+\t\tif (wdata)\n+\t\t\tmemcpy(wbuf + hdr, wdata, wlen);\n+\t\telse\n+\t\t\tmemset(wbuf + hdr, 0, wlen);\n+\t\tmsgs[nmsgs++] = (struct i2c_msg){\n+\t\t\t.addr  = addr,\n+\t\t\t.flags = 0,\n+\t\t\t.len   = hdr + wlen,\n+\t\t\t.buf   = wbuf,\n+\t\t};\n+\t}\n+\tif (rlen) {\n+\t\tmsgs[nmsgs++] = (struct i2c_msg){\n+\t\t\t.addr  = addr,\n+\t\t\t.flags = I2C_M_RD,\n+\t\t\t.len   = rlen,\n+\t\t\t.buf   = rbuf,\n+\t\t};\n+\t}\n+\n+\tret = __i2c_transfer(adap, msgs, nmsgs);\n+\tif (ret != nmsgs)\n+\t\treturn (ret \u003c 0) ? ret : -EIO;\n+\n+\tif (rdata \u0026\u0026 rlen)\n+\t\tmemcpy(rdata, rbuf, rlen);\n+\n+\treturn 0;\n+}\n+\n+/* Hand the segment back, and wait for the firmware to drop the grant before\n+ * the caller unlocks the adapter: a transfer queued behind us would\n+ * otherwise be clocked out while the controller is still on the TMC bus,\n+ * which is what holding the adapter lock across the grant exists to stop.\n+ */\n+static void adva_x1_mblaze_release(struct ptp_ocp *bp)\n+{\n+\tint i;\n+\n+\tif (!bp-\u003epps_select)\n+\t\treturn;\n+\n+\tiowrite32(MBLAZE_RELEASE, \u0026bp-\u003epps_select-\u003ei2c_bus_ctrl);\n+\t/* Flush the posted write, as the acquire path does. */\n+\tioread32(\u0026bp-\u003epps_select-\u003ei2c_bus_ctrl);\n+\n+\tfor (i = 0; i \u003c MBLAZE_RETRIES; i++) {\n+\t\tif (ioread32(\u0026bp-\u003epps_select-\u003ei2c_bus_ctrl) != MBLAZE_GRANTED)\n+\t\t\treturn;\n+\t\tusleep_range(MBLAZE_RETRY_US, MBLAZE_RETRY_US + 1000);\n+\t}\n+\n+\tdev_warn(\u0026bp-\u003epdev-\u003edev, \"TMC bus still granted after release\\n\");\n+}\n+\n+/* Acquire the shared I2C bus from the MicroBlaze firmware.  Returns with no\n+ * request outstanding on failure, so the firmware is never left granting a\n+ * segment to a host that has given up waiting for it.\n+ */\n+static int adva_x1_mblaze_acquire(struct ptp_ocp *bp)\n+{\n+\tu32 val;\n+\tint i;\n+\n+\tif (!bp-\u003epps_select)\n+\t\treturn -ENODEV;\n+\n+\t/* Drop a request left by a caller that died mid-sequence; cpld_lock\n+\t * keeps live ones out.  The read back only flushes the posted write.\n+\t */\n+\tiowrite32(0, \u0026bp-\u003epps_select-\u003ei2c_bus_ctrl);\n+\tioread32(\u0026bp-\u003epps_select-\u003ei2c_bus_ctrl);\n+\n+\tiowrite32(MBLAZE_REQUEST, \u0026bp-\u003epps_select-\u003ei2c_bus_ctrl);\n+\tfor (i = 0; i \u003c MBLAZE_RETRIES; i++) {\n+\t\tusleep_range(MBLAZE_RETRY_US, MBLAZE_RETRY_US + 1000);\n+\t\tval = ioread32(\u0026bp-\u003epps_select-\u003ei2c_bus_ctrl);\n+\t\tif (val == MBLAZE_GRANTED)\n+\t\t\treturn 0;\n+\t}\n+\n+\tadva_x1_mblaze_release(bp);\n+\treturn -ETIMEDOUT;\n+}\n+\n+/* Route the host controller back to the EEPROMs and release the adapter.\n+ * Safe after a failed claim: it also clears a request that was never granted.\n+ */\n+static void adva_x1_bus_release(struct ptp_ocp *bp)\n+{\n+\tstruct i2c_adapter *adap = bp-\u003ecpld_adap;\n+\n+\tif (!adap)\n+\t\treturn;\n+\n+\tadva_x1_mblaze_release(bp);\n+\tbp-\u003ecpld_adap = NULL;\n+\tkfree(bp-\u003ecpld_buf);\n+\tbp-\u003ecpld_buf = NULL;\n+\ti2c_unlock_bus(adap, I2C_LOCK_ROOT_ADAPTER);\n+\ti2c_put_adapter(adap);\n+}\n+\n+static int adva_x1_i2c_adap_match(struct device *dev, const void *data)\n+{\n+\treturn !!i2c_verify_adapter(dev);\n+}\n+\n+/*\n+ * Cache the adapter the CPLD hangs off.  ptp_ocp_i2c_notifier_call() misses\n+ * it when the i2c controller driver is already loaded: the adapter is then\n+ * added from inside platform_device_register(), before bp-\u003ei2c_ctrl is set.\n+ * Only direct children are searched, so a mux adapter cannot match.\n+ */\n+static void adva_x1_cache_i2c_adap(struct ptp_ocp *bp)\n+{\n+\tstruct device *child;\n+\n+\tif (!bp-\u003ehas_cpld || !bp-\u003ei2c_ctrl ||\n+\t    READ_ONCE(bp-\u003ecpld_i2c_adap_nr) \u003e= 0)\n+\t\treturn;\n+\n+\tchild = device_find_child(\u0026bp-\u003ei2c_ctrl-\u003edev, NULL,\n+\t\t\t\t  adva_x1_i2c_adap_match);\n+\tif (!child)\n+\t\treturn;\n+\n+\tWRITE_ONCE(bp-\u003ecpld_i2c_adap_nr, i2c_verify_adapter(child)-\u003enr);\n+\tput_device(child);\n+}\n+\n+/*\n+ * Claim the TMC bus.  The adapter lock is held across the handshake to keep\n+ * the EEPROM, nvmem and at24 paths off the controller while it is routed\n+ * away from the EEPROMs; an EEPROM read blocks until the claim is dropped.\n+ */\n+static int adva_x1_bus_claim(struct ptp_ocp *bp)\n+{\n+\tstruct i2c_adapter *adap;\n+\tint ret;\n+\n+\tlockdep_assert_held(\u0026bp-\u003ecpld_lock);\n+\n+\tadap = i2c_get_adapter(READ_ONCE(bp-\u003ecpld_i2c_adap_nr));\n+\tif (!adap)\n+\t\treturn -ENODEV;\n+\n+\t/* The number is freed before the notifier clears it, so it can\n+\t * already be another adapter's.  Check this is still ours.\n+\t */\n+\tif (!bp-\u003ei2c_ctrl || adap-\u003edev.parent != \u0026bp-\u003ei2c_ctrl-\u003edev) {\n+\t\ti2c_put_adapter(adap);\n+\t\treturn -ENODEV;\n+\t}\n+\n+\t/* One scratch buffer per claim rather than per transfer: an upload\n+\t * holds the claim for the whole image.\n+\t */\n+\tbp-\u003ecpld_buf = kzalloc(2 * ADVA_CPLD_XFER_MAX, GFP_KERNEL);\n+\tif (!bp-\u003ecpld_buf) {\n+\t\ti2c_put_adapter(adap);\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\ti2c_lock_bus(adap, I2C_LOCK_ROOT_ADAPTER);\n+\tbp-\u003ecpld_adap = adap;\n+\n+\tret = adva_x1_mblaze_acquire(bp);\n+\tif (ret)\n+\t\tadva_x1_bus_release(bp);\n+\n+\treturn ret;\n+}\n+\n+/* Select a mux channel, or deselect all with ch \u003c 0 - the power-on state.\n+ * The mux is on the TMC bus, so what it is left set to never affects the\n+ * EEPROM paths.\n+ */\n+static int adva_x1_mux_select(struct ptp_ocp *bp, int ch)\n+{\n+\tu8 val = (ch \u003e= 0) ? BIT(ch) : 0;\n+\n+\treturn adva_x1_i2c_xfer(bp, ADVA_MUX_ADDR, val, NULL, 0, NULL, 0);\n+}\n+\n+/*\n+ * Argument bytes that follow an ISC opcode.  Returns NULL with @nargs set\n+ * when the arguments are all zero: adva_x1_i2c_xfer() zeroes the buffer.\n+ */\n+static const u8 *adva_x1_cpld_args(u8 cmd, u8 *nargs)\n+{\n+\tstatic const u8 en_cfg_tp[] = { 0x08, 0x00 };\n+\t/* cfg sector only */\n+\tstatic const u8 erase_cfg[] = { 0x04, 0x00, 0x00 };\n+\n+\tswitch (cmd) {\n+\tcase CPLD_CMD_EN_CFG_TP:\n+\t\t*nargs = sizeof(en_cfg_tp);\n+\t\treturn en_cfg_tp;\n+\tcase CPLD_CMD_ERASE:\n+\t\t*nargs = sizeof(erase_cfg);\n+\t\treturn erase_cfg;\n+\tcase CPLD_CMD_RESET_ADDR:\n+\tcase CPLD_CMD_SET_DONE:\n+\t\t*nargs = 3;\n+\t\treturn NULL;\n+\tcase CPLD_CMD_DIS_CFG:\n+\tcase CPLD_CMD_REFRESH:\n+\t\t*nargs = 2;\n+\t\treturn NULL;\n+\tdefault:\n+\t\t*nargs = 0;\n+\t\treturn NULL;\n+\t}\n+}\n+\n+/* Send an ISC command with the fixed arguments that belong to it. */\n+static int adva_x1_cpld_write(struct ptp_ocp *bp, u8 cmd)\n+{\n+\tconst u8 *args;\n+\tu8 nargs;\n+\n+\targs = adva_x1_cpld_args(cmd, \u0026nargs);\n+\n+\treturn adva_x1_i2c_xfer(bp, ADVA_CPLD_ADDR, cmd, args, nargs, NULL, 0);\n+}\n+\n+/*\n+ * Send a 4-byte command and read back without an intermediate STOP: two\n+ * messages in one transfer is the Lattice write -\u003e repeated START -\u003e read,\n+ * so no protocol-mangling flag is needed.\n+ */\n+static int adva_x1_cpld_cmd_read(struct ptp_ocp *bp,\n+\t\t\t\t u32 cmd_be, u8 *out, u8 out_len)\n+{\n+\t__be32 cmd = cpu_to_be32(cmd_be);\n+\n+\treturn adva_x1_i2c_xfer(bp, ADVA_CPLD_ADDR, -1, \u0026cmd, 4, out, out_len);\n+}\n+\n+static int adva_x1_cpld_read_status(struct ptp_ocp *bp, u32 *status)\n+{\n+\tu8 buf[4];\n+\tint ret;\n+\n+\tret = adva_x1_cpld_cmd_read(bp, CPLD_CMD_READ_STATUS, buf, 4);\n+\tif (ret)\n+\t\treturn ret;\n+\t*status = get_unaligned_be32(buf);\n+\treturn 0;\n+}\n+\n+/* Poll the status register until the CPLD goes idle, or @max_ms elapses.\n+ * The deadline is on wall time, so the I2C transactions count against it,\n+ * and the status is read once more after it expires before giving up.\n+ */\n+static int adva_x1_cpld_wait_ready(struct ptp_ocp *bp, unsigned int max_ms)\n+{\n+\tu32 status = 0;\n+\tint err = 0, ret;\n+\n+\tret = read_poll_timeout(adva_x1_cpld_read_status, err,\n+\t\t\t\terr || (status \u0026 CPLD_STATUS_FAILED) ||\n+\t\t\t\t!(status \u0026 CPLD_STATUS_BUSY),\n+\t\t\t\tCPLD_POLL_US, max_ms * USEC_PER_MSEC, false,\n+\t\t\t\tbp, \u0026status);\n+\tif (ret)\n+\t\treturn ret;\n+\t/* Keep the transport errno so it reaches userspace as-is; -EIO is\n+\t * reserved for the CPLD itself reporting FAILED.\n+\t */\n+\tif (err)\n+\t\treturn err;\n+\tif (status \u0026 CPLD_STATUS_FAILED)\n+\t\treturn -EIO;\n+\n+\treturn 0;\n+}\n+\n+/* Wait for BUSY to clear, ignoring a pending cancel.  An ISC command issued\n+ * while the part is still erasing or programming may not latch, so an abort\n+ * has to wait the operation out before it can leave configuration mode.\n+ */\n+static int adva_x1_cpld_wait_idle(struct ptp_ocp *bp, unsigned int max_ms)\n+{\n+\tu32 status = 0;\n+\tint err = 0, ret;\n+\n+\tret = read_poll_timeout(adva_x1_cpld_read_status, err,\n+\t\t\t\terr || !(status \u0026 CPLD_STATUS_BUSY),\n+\t\t\t\tCPLD_POLL_US, max_ms * USEC_PER_MSEC, false,\n+\t\t\t\tbp, \u0026status);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn err;\n+}\n+\n+/* Leave transparent configuration mode.  cpld_in_config_mode stays set when\n+ * DIS_CFG does not get through, so a later cleanup() tries again rather than\n+ * recording an exit that never happened.\n+ */\n+static void adva_x1_cpld_exit_config(struct ptp_ocp *bp)\n+{\n+\tint err;\n+\n+\tif (!bp-\u003ecpld_in_config_mode)\n+\t\treturn;\n+\n+\tadva_x1_cpld_wait_idle(bp, CPLD_ERASE_MS);\n+\n+\terr = adva_x1_cpld_write(bp, CPLD_CMD_DIS_CFG);\n+\tif (err)\n+\t\tdev_warn(\u0026bp-\u003epdev-\u003edev,\n+\t\t\t \"CPLD left in configuration mode: %d\\n\", err);\n+\telse\n+\t\tbp-\u003ecpld_in_config_mode = false;\n+}\n+\n+/*\n+ * Read the Lattice device ID and the image USERCODE under one bus claim.\n+ * Done once, off the unprivileged devlink path, which reports the cached\n+ * values.  -EBUSY means cpld_lock is held, so the caller can retry rather\n+ * than wait behind a long CPLD operation.\n+ */\n+static int adva_x1_cpld_read_id(struct ptp_ocp *bp)\n+{\n+\tu32 id, usercode;\n+\tu8 data[4];\n+\tint ret;\n+\n+\tif (!mutex_trylock(\u0026bp-\u003ecpld_lock))\n+\t\treturn -EBUSY;\n+\n+\tret = adva_x1_bus_claim(bp);\n+\tif (ret)\n+\t\tgoto out;\n+\tret = adva_x1_mux_select(bp, ADVA_MUX_CHANNEL);\n+\tif (ret)\n+\t\tgoto release;\n+\tret = adva_x1_cpld_cmd_read(bp, CPLD_CMD_READ_ID, data, 4);\n+\tif (ret)\n+\t\tgoto deselect;\n+\tid = get_unaligned_be32(data);\n+\n+\tret = adva_x1_cpld_cmd_read(bp, CPLD_CMD_READ_USERCODE, data, 4);\n+\tif (ret)\n+\t\tgoto deselect;\n+\tusercode = get_unaligned_be32(data);\n+\n+\t/* cpld_id is the validity flag for both, so publish it last. */\n+\tWRITE_ONCE(bp-\u003ecpld_usercode, usercode);\n+\tWRITE_ONCE(bp-\u003ecpld_id, id);\n+deselect:\n+\tadva_x1_mux_select(bp, -1);\n+release:\n+\tadva_x1_bus_release(bp);\n+out:\n+\tmutex_unlock(\u0026bp-\u003ecpld_lock);\n+\tif (ret)\n+\t\tdev_dbg(\u0026bp-\u003epdev-\u003edev,\n+\t\t\t\"CPLD device ID unavailable: %d\\n\", ret);\n+\n+\treturn ret;\n+}\n+\n+/* cpld_status - show the flags set in the TAP CPLD status register. */\n+static ssize_t\n+cpld_status_show(struct device *dev, struct device_attribute *attr,\n+\t\t char *buf)\n+{\n+\tstruct ptp_ocp *bp = dev_get_drvdata(dev);\n+\tint count = 0;\n+\tu32 st = 0;\n+\tint ret;\n+\n+\t/* A CPLD operation can hold cpld_lock a long time; stay killable. */\n+\tret = mutex_lock_interruptible(\u0026bp-\u003ecpld_lock);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = adva_x1_bus_claim(bp);\n+\tif (ret)\n+\t\tgoto out;\n+\tret = adva_x1_mux_select(bp, ADVA_MUX_CHANNEL);\n+\tif (ret)\n+\t\tgoto release;\n+\tret = adva_x1_cpld_read_status(bp, \u0026st);\n+\tadva_x1_mux_select(bp, -1);\n+release:\n+\tadva_x1_bus_release(bp);\n+out:\n+\tmutex_unlock(\u0026bp-\u003ecpld_lock);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Flags: the names of the bits that are set, nothing when none is,\n+\t * rather than a value per bit in one file.\n+\t */\n+\tif (st \u0026 CPLD_STATUS_DONE)\n+\t\tcount += sysfs_emit_at(buf, count, \"done\");\n+\tif (st \u0026 CPLD_STATUS_BUSY)\n+\t\tcount += sysfs_emit_at(buf, count, \"%sbusy\", count ? \" \" : \"\");\n+\tif (st \u0026 CPLD_STATUS_FAILED)\n+\t\tcount += sysfs_emit_at(buf, count, \"%sfailed\",\n+\t\t\t\t       count ? \" \" : \"\");\n+\n+\treturn count + sysfs_emit_at(buf, count, \"\\n\");\n+}\n+static DEVICE_ATTR_ADMIN_RO(cpld_status);\n+\n+/*\n+ * Program the CPLD configuration flash from @fw and activate it.  Selected\n+ * by the \"fw.cpld\" flash component; the SPI flash keeps the default path.\n+ * Runs under cpld_lock with the i2c adapter lock held, so an EEPROM read\n+ * blocks for as long as programming takes.\n+ */\n+static int adva_x1_cpld_flash(struct ptp_ocp *bp, struct devlink *devlink,\n+\t\t\t      const struct firmware *fw,\n+\t\t\t      struct netlink_ext_ack *extack)\n+{\n+\tsize_t offset;\n+\tint err;\n+\tu32 st;\n+\n+\tif (!fw-\u003esize || fw-\u003esize % CPLD_PAGE_SIZE) {\n+\t\tNL_SET_ERR_MSG_MOD(extack,\n+\t\t\t\t   \"image must be a whole number of 16-byte pages\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tmutex_lock(\u0026bp-\u003ecpld_lock);\n+\n+\terr = adva_x1_bus_claim(bp);\n+\tif (err)\n+\t\tgoto unlock;\n+\terr = adva_x1_mux_select(bp, ADVA_MUX_CHANNEL);\n+\tif (err)\n+\t\tgoto release;\n+\n+\t/* Set before EN_CFG_TP, not after: the CPLD may have entered config\n+\t * mode even if the write errors or the wait times out, and only this\n+\t * makes the exit path send DIS_CFG.  A stray DIS_CFG is harmless;\n+\t * leaving config mode enabled is not.\n+\t */\n+\tbp-\u003ecpld_in_config_mode = true;\n+\n+\terr = adva_x1_cpld_write(bp, CPLD_CMD_EN_CFG_TP);\n+\tif (!err)\n+\t\terr = adva_x1_cpld_wait_ready(bp, 5000);\n+\tif (err)\n+\t\tgoto exit_config;\n+\n+\tdevlink_flash_update_status_notify(devlink, \"Erasing\",\n+\t\t\t\t\t   ADVA_CPLD_COMPONENT, 0, 0);\n+\terr = adva_x1_cpld_write(bp, CPLD_CMD_ERASE);\n+\tif (!err)\n+\t\terr = adva_x1_cpld_wait_ready(bp, CPLD_ERASE_MS);\n+\tif (err)\n+\t\tgoto exit_config;\n+\n+\terr = adva_x1_cpld_write(bp, CPLD_CMD_RESET_ADDR);\n+\tif (err)\n+\t\tgoto exit_config;\n+\n+\tfor (offset = 0; offset \u003c fw-\u003esize; offset += CPLD_PAGE_SIZE) {\n+\t\tu8 args[3 + CPLD_PAGE_SIZE] = { 0x00, 0x00, 0x01 };\n+\n+\t\tmemcpy(\u0026args[3], fw-\u003edata + offset, CPLD_PAGE_SIZE);\n+\t\terr = adva_x1_i2c_xfer(bp, ADVA_CPLD_ADDR, CPLD_CMD_WRITE_PAGE,\n+\t\t\t\t       args, sizeof(args), NULL, 0);\n+\t\tif (!err)\n+\t\t\terr = adva_x1_cpld_wait_ready(bp, 100);\n+\t\tif (err)\n+\t\t\tgoto exit_config;\n+\n+\t\tif (!(offset % (CPLD_PAGE_SIZE * 64)))\n+\t\t\tdevlink_flash_update_status_notify(devlink,\n+\t\t\t\t\t\t\t   \"Programming\",\n+\t\t\t\t\t\t\t   ADVA_CPLD_COMPONENT,\n+\t\t\t\t\t\t\t   offset, fw-\u003esize);\n+\t}\n+\tdevlink_flash_update_status_notify(devlink, \"Programming\",\n+\t\t\t\t\t   ADVA_CPLD_COMPONENT,\n+\t\t\t\t\t   fw-\u003esize, fw-\u003esize);\n+\n+\terr = adva_x1_cpld_write(bp, CPLD_CMD_SET_DONE);\n+\tif (!err)\n+\t\terr = adva_x1_cpld_wait_ready(bp, 1000);\n+\tif (err)\n+\t\tgoto exit_config;\n+\n+\terr = adva_x1_cpld_read_status(bp, \u0026st);\n+\tif (err)\n+\t\tgoto exit_config;\n+\tif (!(st \u0026 CPLD_STATUS_DONE)) {\n+\t\terr = -EIO;\n+\t\tgoto exit_config;\n+\t}\n+\n+\tdevlink_flash_update_status_notify(devlink, \"Activating\",\n+\t\t\t\t\t   ADVA_CPLD_COMPONENT, 0, 0);\n+\terr = adva_x1_cpld_write(bp, CPLD_CMD_REFRESH);\n+\tif (err)\n+\t\tgoto exit_config;\n+\n+\t/* REFRESH reboots the CPLD out of configuration mode, so the exit\n+\t * path must not send DIS_CFG afterwards even if a check below fails.\n+\t */\n+\tbp-\u003ecpld_in_config_mode = false;\n+\n+\t/* The new image is already running, so a segment that is not back\n+\t * yet must not be reported as a failed update: retry the reselect\n+\t * rather than sampling the mux once at a fixed delay.\n+\t */\n+\tmsleep(1500);\n+\terr = read_poll_timeout(adva_x1_mux_select, err, !err, CPLD_POLL_US,\n+\t\t\t\t3000 * USEC_PER_MSEC, false,\n+\t\t\t\tbp, ADVA_MUX_CHANNEL);\n+\tif (err)\n+\t\tgoto deselect;\n+\n+\terr = adva_x1_cpld_wait_ready(bp, 3000);\n+\tif (err)\n+\t\tgoto deselect;\n+\n+\t/* Require DONE set, not busy and no error code, as machxo2-spi.c does\n+\t * after a refresh: without it a CRC or preamble error reads back as a\n+\t * successful update.\n+\t */\n+\terr = adva_x1_cpld_read_status(bp, \u0026st);\n+\tif (err)\n+\t\tgoto deselect;\n+\tif (!(st \u0026 CPLD_STATUS_DONE) || (st \u0026 CPLD_STATUS_BUSY) ||\n+\t    (st \u0026 CPLD_STATUS_ERR)) {\n+\t\tdev_err(\u0026bp-\u003epdev-\u003edev,\n+\t\t\t\"CPLD refresh left status 0x%08x\\n\", st);\n+\t\tNL_SET_ERR_MSG_MOD(extack, \"CPLD did not come back configured\");\n+\t\terr = -EIO;\n+\t\tgoto deselect;\n+\t}\n+\n+\t/* The image on the part changed, so the cached USERCODE has too. */\n+\tWRITE_ONCE(bp-\u003ecpld_id, 0);\n+\tWRITE_ONCE(bp-\u003ecpld_id_tried, false);\n+\tbp-\u003ecpld_id_attempts = 0;\n+\n+exit_config:\n+\tadva_x1_cpld_exit_config(bp);\n+deselect:\n+\tadva_x1_mux_select(bp, -1);\n+release:\n+\tadva_x1_bus_release(bp);\n+unlock:\n+\tmutex_unlock(\u0026bp-\u003ecpld_lock);\n+\n+\treturn err;\n+}\n+\n static struct attribute *adva_timecard_attrs[] = {\n \t\u0026dev_attr_serialnum.attr,\n \t\u0026dev_attr_gnss_sync.attr,\n@@ -4274,6 +4990,7 @@ static struct attribute *adva_timecard_x1_attrs[] = {\n \t\u0026dev_attr_ts_window_adjust.attr,\n \t\u0026dev_attr_utc_tai_offset.attr,\n \t\u0026dev_attr_tod_correction.attr,\n+\t\u0026dev_attr_cpld_status.attr,\n \tNULL,\n };\n \n@@ -4904,6 +5621,7 @@ ptp_ocp_detach(struct ptp_ocp *bp)\n \t\tclk_hw_unregister_fixed_rate(bp-\u003ei2c_clk);\n \tif (bp-\u003en_irqs)\n \t\tpci_free_irq_vectors(bp-\u003epdev);\n+\tmutex_destroy(\u0026bp-\u003ecpld_lock);\n \tdevice_unregister(\u0026bp-\u003edev);\n }\n \n@@ -5052,6 +5770,20 @@ ptp_ocp_sync_work(struct work_struct *work)\n \n \tbp-\u003esync = sync;\n \n+\t/* The adapter can appear after probe, so read the ID from here once\n+\t * it has.  A claim can fail transiently - the firmware may not grant\n+\t * the segment straight after power-up - so retry a bounded number of\n+\t * times.  -EBUSY means cpld_lock is held and does not count.\n+\t */\n+\tif (bp-\u003ehas_cpld \u0026\u0026 !READ_ONCE(bp-\u003ecpld_id_tried) \u0026\u0026\n+\t    READ_ONCE(bp-\u003ecpld_i2c_adap_nr) \u003e= 0) {\n+\t\tint err = adva_x1_cpld_read_id(bp);\n+\n+\t\tif (err != -EBUSY \u0026\u0026\n+\t\t    (!err || ++bp-\u003ecpld_id_attempts \u003e= CPLD_ID_MAX_ATTEMPTS))\n+\t\t\tWRITE_ONCE(bp-\u003ecpld_id_tried, true);\n+\t}\n+\n \tqueue_delayed_work(system_power_efficient_wq, \u0026bp-\u003esync_work, HZ);\n }\n \n@@ -5080,6 +5812,14 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)\n \tif (err)\n \t\tgoto out_disable;\n \n+\t/* Before the first error path that calls ptp_ocp_detach(), so\n+\t * mutex_destroy() runs on an initialised mutex, and before\n+\t * ptp_ocp_register_resources(), so this sentinel cannot overwrite an\n+\t * adapter number the i2c notifier stores during registration.\n+\t */\n+\tmutex_init(\u0026bp-\u003ecpld_lock);\n+\tbp-\u003ecpld_i2c_adap_nr = -1;\n+\n \tINIT_DELAYED_WORK(\u0026bp-\u003esync_work, ptp_ocp_sync_work);\n \n \t/* compat mode.\n@@ -5099,6 +5839,8 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)\n \tif (err)\n \t\tgoto out;\n \n+\tadva_x1_cache_i2c_adap(bp);\n+\n \tbp-\u003eptp = ptp_clock_register(\u0026bp-\u003eptp_info, \u0026pdev-\u003edev);\n \tif (IS_ERR(bp-\u003eptp)) {\n \t\terr = PTR_ERR(bp-\u003eptp);\n@@ -5119,12 +5861,12 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)\n \tif (IS_ERR(bp-\u003edpll)) {\n \t\terr = PTR_ERR(bp-\u003edpll);\n \t\tdev_err(\u0026pdev-\u003edev, \"dpll_device_alloc failed\\n\");\n-\t\tgoto out;\n+\t\tgoto out_devlink;\n \t}\n \n \terr = dpll_device_register(bp-\u003edpll, DPLL_TYPE_PPS, \u0026dpll_ops, bp);\n \tif (err)\n-\t\tgoto out;\n+\t\tgoto out_dpll_put;\n \n \tfor (i = 0; i \u003c OCP_SMA_NUM; i++) {\n \t\tbp-\u003esma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE,\n@@ -5150,7 +5892,16 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)\n \t\tdpll_pin_unregister(bp-\u003edpll, bp-\u003esma[i].dpll_pin, \u0026dpll_pins_ops, \u0026bp-\u003esma[i]);\n \t\tdpll_pin_put(bp-\u003esma[i].dpll_pin, \u0026bp-\u003esma[i].tracker);\n \t}\n+\t/* dpll_device_register() takes a second reference and stores bp; only\n+\t * dpll_device_unregister() drops it.  Without this the device stays\n+\t * visible with a priv pointer into storage devlink_free() will release.\n+\t */\n+\tdpll_device_unregister(bp-\u003edpll, \u0026dpll_ops, bp);\n+out_dpll_put:\n \tdpll_device_put(bp-\u003edpll, \u0026bp-\u003etracker);\n+out_devlink:\n+\t/* detach() destroys cpld_lock; no devlink op may still reach it */\n+\tdevlink_unregister(devlink);\n out:\n \tptp_ocp_detach(bp);\n out_disable:\n@@ -5196,6 +5947,7 @@ ptp_ocp_i2c_notifier_call(struct notifier_block *nb,\n \t\t\t  unsigned long action, void *data)\n {\n \tstruct device *dev, *child = data;\n+\tstruct i2c_adapter *adap;\n \tstruct ptp_ocp *bp;\n \tbool add;\n \n@@ -5219,11 +5971,21 @@ ptp_ocp_i2c_notifier_call(struct notifier_block *nb,\n \n found:\n \tbp = dev_get_drvdata(dev);\n-\tif (add)\n+\tadap = i2c_verify_adapter(child);\n+\tif (add) {\n \t\tptp_ocp_symlink(bp, child, \"i2c\");\n-\telse\n+\t\t/* Only the card's own controller: a mux child added on it\n+\t\t * reaches ptp_ocp through the same walk.  Numbers are\n+\t\t * recycled, so the claim re-checks the parent itself.\n+\t\t */\n+\t\tif (bp-\u003ei2c_ctrl \u0026\u0026 adap-\u003edev.parent == \u0026bp-\u003ei2c_ctrl-\u003edev)\n+\t\t\tWRITE_ONCE(bp-\u003ecpld_i2c_adap_nr, adap-\u003enr);\n+\t} else {\n+\t\t/* invalidate before free, but only for the cached adapter */\n+\t\tif (READ_ONCE(bp-\u003ecpld_i2c_adap_nr) == adap-\u003enr)\n+\t\t\tWRITE_ONCE(bp-\u003ecpld_i2c_adap_nr, -1);\n \t\tsysfs_remove_link(\u0026bp-\u003edev.kobj, \"i2c\");\n-\n+\t}\n \treturn 0;\n }\n \n"
}

2/1 2026/09/14 07:27 agent patch-evaluator
1m Model:
core

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch modifies documentation and the ptp_ocp driver (drivers/ptp/ptp_ocp.c), adding CPLD flash and status support for ADVA TimeCard X1 devices. This driver binds specifically to vendor-specific PCIe timecard hardware (such as ADVA TimeCard X1, Meta Timecard) that is not emulated in standard QEMU or GCE virtualization environments. As such, the driver cannot be probed and the modified code is structurally unreachable for syzkaller fuzzing.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit 73bee881681bb3e5540a748b5cece5bb408bc78d
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Sep 14 07:27:18 2026 +0000

    syz-cluster: applied patch under review

diff --git a/Documentation/ABI/testing/sysfs-timecard b/Documentation/ABI/testing/sysfs-timecard
index 3ae41b7634ac9..6ddca452bc633 100644
--- a/Documentation/ABI/testing/sysfs-timecard
+++ b/Documentation/ABI/testing/sysfs-timecard
@@ -11,6 +11,34 @@ Contact:	Jonathan Lemon <jonathan.lemon@gmail.com>
 Description:	This directory contains the attributes of the Nth timecard
 		registered.
 
+What:		/sys/class/timecard/ocpN/cpld_status
+Date:		July 2026
+Contact:	Sagi Maimon <maimon.sagi@gmail.com>
+Description:	(RO, root only) The flags set in the status register of the
+		TAP CPLD, space separated, or an empty line when none is
+		set.  Only present on ADVA x1 TAP boards (PCI ID
+		0xad5a:0x0410).
+
+		==========  ================================================
+		done        the configuration flash was programmed
+			    successfully and is active
+		busy        an internal operation is in progress
+		failed      the last ISC operation failed
+		==========  ================================================
+
+		For example "busy" while programming, or "busy failed" if a
+		failure is latched while another operation runs.
+
+		A read arbitrates for the shared I2C bus and reprograms the
+		on-card mux, so it is restricted to root.  The Lattice device
+		ID of the CPLD is read once by the driver, shortly after
+		probe, and reported from that cached value as the fixed
+		"cpld.id" version by devlink dev info.
+
+		New CPLD firmware is programmed with devlink dev flash,
+		selecting the "fw.cpld" component; see
+		Documentation/networking/devlink/ptp_ocp.rst.
+
 What:		/sys/class/timecard/ocpN/available_clock_sources
 Date:		September 2021
 Contact:	Jonathan Lemon <jonathan.lemon@gmail.com>
diff --git a/Documentation/networking/devlink/index.rst b/Documentation/networking/devlink/index.rst
index d4a83fdcff7fe..1af780c811ee2 100644
--- a/Documentation/networking/devlink/index.rst
+++ b/Documentation/networking/devlink/index.rst
@@ -99,6 +99,7 @@ parameters, info versions, and other features it supports.
    nfp
    octeontx2
    prestera
+   ptp_ocp
    qed
    sfc
    stmmac
diff --git a/Documentation/networking/devlink/ptp_ocp.rst b/Documentation/networking/devlink/ptp_ocp.rst
new file mode 100644
index 0000000000000..37e9f1ebd1a81
--- /dev/null
+++ b/Documentation/networking/devlink/ptp_ocp.rst
@@ -0,0 +1,70 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+========================
+ptp_ocp devlink support
+========================
+
+This document describes the devlink features implemented by the ``ptp_ocp``
+device driver: the info versions it reports and its flash update support.
+
+Info versions
+=============
+
+The ``ptp_ocp`` driver reports the following versions
+
+.. list-table:: devlink info versions implemented
+   :widths: 5 5 90
+
+   * - Name
+     - Type
+     - Description
+   * - ``fw``
+     - running
+     - Version of the firmware running on the card.  Reported as ``loader``
+       instead when the card is running the firmware loader.
+   * - ``board.id``
+     - fixed
+     - Board identifier, read from the on-card EEPROM.
+   * - ``cpld.id``
+     - fixed
+     - Lattice device ID (IDCODE) of the TAP CPLD, formatted as ``0x%08x``.
+       Only present on ADVA TimeCard X1 boards, which are the only ones
+       carrying that CPLD.  Reading it claims the shared I2C bus and
+       reprograms the on-card mux, so the driver does that once from its
+       own worker and reports the cached value here; the version is
+       omitted until that read has succeeded.
+   * - ``fw.cpld``
+     - running
+     - USERCODE of the image programmed into the TAP CPLD, formatted as
+       ``0x%08x``.  Read together with ``cpld.id`` and reported the same
+       way.  This is the component name to pass to ``devlink dev flash``
+       to update the CPLD.
+
+Flash update
+============
+
+The driver implements ``devlink dev flash`` for two separate targets,
+selected with the component name.
+
+.. list-table:: Flash components
+   :widths: 20 80
+
+   * - Component
+     - Description
+   * - (none)
+     - The card's own flash, written through the SPI controller the driver
+       exposes.  The card runs the new image after its next reset.
+   * - ``fw.cpld``
+     - The configuration flash of the TAP CPLD on ADVA TimeCard X1 boards,
+       programmed over I2C with the MachXO3 in-system programming commands
+       and activated with a REFRESH, so the new image runs immediately.
+       The image is the raw configuration bitstream, a whole number of
+       16-byte pages; container formats such as ``.jed`` are rejected.
+
+Programming the CPLD claims the shared I2C bus for the whole cycle, so
+reads of the card's EEPROM block until it completes.  Progress is reported
+with the standard devlink status notifications.
+
+Example::
+
+    $ devlink dev flash pci/0000:02:00.0 file adva-cpld.bin component fw.cpld
diff --git a/MAINTAINERS b/MAINTAINERS
index 0e04d92d1b098..113f314346177 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20479,6 +20479,7 @@ OPENCOMPUTE PTP CLOCK DRIVER
 M:	Vadim Fedorenko <vadim.fedorenko@linux.dev>
 L:	netdev@vger.kernel.org
 S:	Maintained
+F:	Documentation/networking/devlink/ptp_ocp.rst
 F:	drivers/ptp/ptp_ocp.c
 
 OPENCORES I2C BUS DRIVER
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index 3d26ec1f7b9ec..58b90b2b282fe 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -20,10 +20,14 @@
 #include <linux/spi/altera.h>
 #include <net/devlink.h>
 #include <linux/i2c.h>
+#include <linux/iopoll.h>
 #include <linux/mtd/mtd.h>
 #include <linux/nvmem-consumer.h>
 #include <linux/crc16.h>
 #include <linux/dpll.h>
+#include <linux/unaligned.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
 
 #define PCI_DEVICE_ID_META_TIMECARD		0x0400
 
@@ -85,6 +89,8 @@ struct ptp_ocp_adva_info {
 	u8				signals_nr;
 	u8				freq_in_nr;
 	const struct ocp_attr_group	*attr_groups;
+	/* x1: TAP CPLD present */
+	bool				has_cpld;
 };
 
 #define OCP_CTRL_ENABLE		BIT(0)
@@ -163,7 +169,8 @@ struct gpio_reg {
 	u32	gpio1;
 	u32	__pad0;
 	u32	gpio2;
-	u32	__pad1;
+	/* adva_x1: I2C bus ownership register; reserved on other variants */
+	u32	i2c_bus_ctrl;
 };
 
 struct irig_master_reg {
@@ -416,6 +423,27 @@ struct ptp_ocp {
 	dpll_tracker tracker;
 	int signals_nr;
 	int freq_in_nr;
+	/* adva_x1 CPLD I2C (internal use only) */
+	/* serialises CPLD operations */
+	struct mutex		cpld_lock;
+	/* I2C adapter nr; -1 if absent */
+	int			cpld_i2c_adap_nr;
+	/* claimed adapter; valid under cpld_lock */
+	struct i2c_adapter	*cpld_adap;
+	/* scratch, write half then read half; valid under cpld_lock */
+	u8			*cpld_buf;
+	/* Lattice device ID; 0 if unread */
+	u32			cpld_id;
+	/* USERCODE of the programmed image; valid once cpld_id is set */
+	u32			cpld_usercode;
+	/* the one-shot ID read is finished, successfully or not */
+	bool			cpld_id_tried;
+	/* failed ID read attempts so far */
+	unsigned int		cpld_id_attempts;
+	/* x1 TAP CPLD present */
+	bool			has_cpld;
+	/* EN_CFG_TP issued but not yet REFRESH'd */
+	bool			cpld_in_config_mode;
 };
 
 #define OCP_REQ_TIMESTAMP	BIT(0)
@@ -452,6 +480,13 @@ static int ptp_ocp_adva_board_init(struct ptp_ocp *bp, struct ocp_resource *r);
 static const struct ocp_sma_op ocp_adva_sma_op;
 static const struct ocp_sma_op ocp_adva_x1_sma_op;
 
+/* Flash component naming the CPLD image, as reported by ->info_get(). */
+#define ADVA_CPLD_COMPONENT	"fw.cpld"
+
+static int adva_x1_cpld_flash(struct ptp_ocp *bp, struct devlink *devlink,
+			      const struct firmware *fw,
+			      struct netlink_ext_ack *extack);
+
 static const struct ocp_attr_group fb_timecard_groups[];
 
 static const struct ocp_attr_group art_timecard_groups[];
@@ -1273,6 +1308,7 @@ static struct ocp_resource ocp_adva_x1_resource[] = {
 			.signals_nr   = 4,
 			.freq_in_nr   = 4,
 			.attr_groups  = adva_timecard_x1_groups,
+			.has_cpld     = true,
 		},
 	},
 	{ }
@@ -2133,6 +2169,17 @@ ptp_ocp_devlink_flash_update(struct devlink *devlink,
 	const char *msg;
 	int err;
 
+	if (params->component) {
+		if (!bp->has_cpld ||
+		    strcmp(params->component, ADVA_CPLD_COMPONENT)) {
+			NL_SET_ERR_MSG_MOD(extack,
+					   "unsupported flash component");
+			return -EOPNOTSUPP;
+		}
+
+		return adva_x1_cpld_flash(bp, devlink, params->fw, extack);
+	}
+
 	dev = ptp_ocp_find_flash(bp);
 	if (!dev) {
 		dev_err(&bp->pdev->dev, "Can't find Flash SPI adapter\n");
@@ -2155,10 +2202,13 @@ static int
 ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
 			 struct netlink_ext_ack *extack)
 {
+	enum devlink_info_version_type ver_type =
+					DEVLINK_INFO_VERSION_TYPE_COMPONENT;
 	struct ptp_ocp *bp = devlink_priv(devlink);
 	const char *fw_image;
 	char buf[32];
 	int err;
+	u32 id;
 
 	fw_image = bp->fw_loader ? "loader" : "fw";
 	sprintf(buf, "%d.%d", bp->fw_tag, bp->fw_version);
@@ -2166,6 +2216,28 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
 	if (err)
 		return err;
 
+	/* Read by the worker, not here: this command is unprivileged and
+	 * reading the ID claims the I2C bus.  0 means unread - skip it
+	 * rather than fail, devlink discards the whole reply on error.
+	 */
+	id = READ_ONCE(bp->cpld_id);
+	if (bp->has_cpld && id) {
+		sprintf(buf, "0x%08x", id);
+		err = devlink_info_version_fixed_put(req, "cpld.id", buf);
+		if (err)
+			return err;
+
+		/* The flashable component: naming it here is what lets
+		 * "devlink dev flash ... component fw.cpld" through, as the
+		 * core matches the name against the versions reported here.
+		 */
+		sprintf(buf, "0x%08x", READ_ONCE(bp->cpld_usercode));
+		err = devlink_info_version_running_put_ext(req, "fw.cpld", buf,
+							   ver_type);
+		if (err)
+			return err;
+	}
+
 	if (!bp->has_eeprom_data) {
 		ptp_ocp_read_eeprom(bp);
 		if (!bp->has_eeprom_data)
@@ -3199,6 +3271,8 @@ ptp_ocp_adva_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
 		return err;
 	ptp_ocp_sma_init(bp);
 
+	bp->has_cpld = info->has_cpld;
+
 	return ptp_ocp_init_clock(bp, &info->servo);
 }
 
@@ -4226,6 +4300,648 @@ static const struct ocp_attr_group art_timecard_groups[] = {
 	{ },
 };
 
+/*
+ * adva_x1 TAP CPLD (Lattice LCMXO3LF-2100) helpers.
+ *
+ * The card has two I2C controllers; Linux registers only 0x00150000.
+ * The i2c_bus_ctrl handshake re-routes what each is wired to:
+ *
+ *   grant 0:  0x150000 -> EEPROMs     0x120000 -> TMC or M.2, MB's choice
+ *   grant 1:  0x150000 -> TMC bus     0x120000 -> M.2
+ *
+ * The PCA9548 at 0x74 and the CPLD at 0x40 behind its channel 0 are on the
+ * TMC bus, reachable only while the grant is held.  The EEPROMs are not
+ * behind the adapter for that window, so a CPLD operation holds cpld_lock
+ * and the adapter lock to keep the EEPROM and nvmem paths off it.
+ */
+
+#define ADVA_MUX_ADDR     0x74
+#define ADVA_CPLD_ADDR    0x40
+#define ADVA_MUX_CHANNEL  0
+
+/* Longest command or reply; bounds each half of the per-claim scratch. */
+#define ADVA_CPLD_XFER_MAX 32
+
+#define MBLAZE_REQUEST    0x0000aaaaU
+#define MBLAZE_GRANTED    0x5555aaaaU
+#define MBLAZE_RELEASE    0x55550000U
+#define MBLAZE_RETRIES    200
+#define MBLAZE_RETRY_US   10000
+
+/* Lattice LCMXO3LF ISC command codes */
+#define CPLD_CMD_READ_ID      0xE0000000UL
+#define CPLD_CMD_READ_USERCODE 0xC0000000UL
+#define CPLD_CMD_READ_STATUS  0x3C000000UL
+#define CPLD_CMD_EN_CFG_TP    0x74   /* enable config, transparent mode */
+#define CPLD_CMD_DIS_CFG      0x26
+#define CPLD_CMD_ERASE        0x0E
+#define CPLD_CMD_RESET_ADDR   0x46
+#define CPLD_CMD_WRITE_PAGE   0x70
+#define CPLD_CMD_SET_DONE     0x5E
+#define CPLD_CMD_REFRESH      0x79
+#define CPLD_PAGE_SIZE        16
+#define CPLD_POLL_US          10000  /* status poll interval while busy */
+#define CPLD_ERASE_MS         15000  /* config sector erase, datasheet max */
+#define CPLD_ID_MAX_ATTEMPTS  10     /* one per sync_work tick */
+
+/* Status register bit positions (Lattice LCMXO3LF datasheet) */
+#define CPLD_STATUS_DONE   BIT(8)
+#define CPLD_STATUS_BUSY   BIT(12)
+#define CPLD_STATUS_FAILED BIT(13)
+#define CPLD_STATUS_ERR    GENMASK(25, 23)
+
+/*
+ * Issue one I2C transaction on the TMC bus: @cmd if not negative, then
+ * @wlen bytes of @wdata (NULL sends zeros), then @rlen bytes read back.
+ * The message is built in the scratch buffer from adva_x1_bus_claim(), so
+ * an upload costs one allocation rather than one per page.  I2C_M_DMA_SAFE
+ * is not set: the two halves are not separately aligned, so let the core
+ * bounce.  Caller holds the claim, hence __i2c_transfer().
+ */
+static int adva_x1_i2c_xfer(struct ptp_ocp *bp, u8 addr, int cmd,
+			    const void *wdata, u8 wlen,
+			    void *rdata, u8 rlen)
+{
+	u8 *wbuf, *rbuf;
+	struct i2c_adapter *adap;
+	struct i2c_msg msgs[2];
+	int nmsgs = 0, ret;
+	u16 hdr = cmd >= 0;
+
+	lockdep_assert_held(&bp->cpld_lock);
+
+	adap = bp->cpld_adap;
+	if (!adap || !bp->cpld_buf)
+		return -ENODEV;
+
+	if (hdr + wlen > ADVA_CPLD_XFER_MAX || rlen > ADVA_CPLD_XFER_MAX)
+		return -EINVAL;
+
+	wbuf = bp->cpld_buf;
+	rbuf = bp->cpld_buf + ADVA_CPLD_XFER_MAX;
+
+	if (hdr + wlen) {
+		if (hdr)
+			wbuf[0] = cmd;
+		if (wdata)
+			memcpy(wbuf + hdr, wdata, wlen);
+		else
+			memset(wbuf + hdr, 0, wlen);
+		msgs[nmsgs++] = (struct i2c_msg){
+			.addr  = addr,
+			.flags = 0,
+			.len   = hdr + wlen,
+			.buf   = wbuf,
+		};
+	}
+	if (rlen) {
+		msgs[nmsgs++] = (struct i2c_msg){
+			.addr  = addr,
+			.flags = I2C_M_RD,
+			.len   = rlen,
+			.buf   = rbuf,
+		};
+	}
+
+	ret = __i2c_transfer(adap, msgs, nmsgs);
+	if (ret != nmsgs)
+		return (ret < 0) ? ret : -EIO;
+
+	if (rdata && rlen)
+		memcpy(rdata, rbuf, rlen);
+
+	return 0;
+}
+
+/* Hand the segment back, and wait for the firmware to drop the grant before
+ * the caller unlocks the adapter: a transfer queued behind us would
+ * otherwise be clocked out while the controller is still on the TMC bus,
+ * which is what holding the adapter lock across the grant exists to stop.
+ */
+static void adva_x1_mblaze_release(struct ptp_ocp *bp)
+{
+	int i;
+
+	if (!bp->pps_select)
+		return;
+
+	iowrite32(MBLAZE_RELEASE, &bp->pps_select->i2c_bus_ctrl);
+	/* Flush the posted write, as the acquire path does. */
+	ioread32(&bp->pps_select->i2c_bus_ctrl);
+
+	for (i = 0; i < MBLAZE_RETRIES; i++) {
+		if (ioread32(&bp->pps_select->i2c_bus_ctrl) != MBLAZE_GRANTED)
+			return;
+		usleep_range(MBLAZE_RETRY_US, MBLAZE_RETRY_US + 1000);
+	}
+
+	dev_warn(&bp->pdev->dev, "TMC bus still granted after release\n");
+}
+
+/* Acquire the shared I2C bus from the MicroBlaze firmware.  Returns with no
+ * request outstanding on failure, so the firmware is never left granting a
+ * segment to a host that has given up waiting for it.
+ */
+static int adva_x1_mblaze_acquire(struct ptp_ocp *bp)
+{
+	u32 val;
+	int i;
+
+	if (!bp->pps_select)
+		return -ENODEV;
+
+	/* Drop a request left by a caller that died mid-sequence; cpld_lock
+	 * keeps live ones out.  The read back only flushes the posted write.
+	 */
+	iowrite32(0, &bp->pps_select->i2c_bus_ctrl);
+	ioread32(&bp->pps_select->i2c_bus_ctrl);
+
+	iowrite32(MBLAZE_REQUEST, &bp->pps_select->i2c_bus_ctrl);
+	for (i = 0; i < MBLAZE_RETRIES; i++) {
+		usleep_range(MBLAZE_RETRY_US, MBLAZE_RETRY_US + 1000);
+		val = ioread32(&bp->pps_select->i2c_bus_ctrl);
+		if (val == MBLAZE_GRANTED)
+			return 0;
+	}
+
+	adva_x1_mblaze_release(bp);
+	return -ETIMEDOUT;
+}
+
+/* Route the host controller back to the EEPROMs and release the adapter.
+ * Safe after a failed claim: it also clears a request that was never granted.
+ */
+static void adva_x1_bus_release(struct ptp_ocp *bp)
+{
+	struct i2c_adapter *adap = bp->cpld_adap;
+
+	if (!adap)
+		return;
+
+	adva_x1_mblaze_release(bp);
+	bp->cpld_adap = NULL;
+	kfree(bp->cpld_buf);
+	bp->cpld_buf = NULL;
+	i2c_unlock_bus(adap, I2C_LOCK_ROOT_ADAPTER);
+	i2c_put_adapter(adap);
+}
+
+static int adva_x1_i2c_adap_match(struct device *dev, const void *data)
+{
+	return !!i2c_verify_adapter(dev);
+}
+
+/*
+ * Cache the adapter the CPLD hangs off.  ptp_ocp_i2c_notifier_call() misses
+ * it when the i2c controller driver is already loaded: the adapter is then
+ * added from inside platform_device_register(), before bp->i2c_ctrl is set.
+ * Only direct children are searched, so a mux adapter cannot match.
+ */
+static void adva_x1_cache_i2c_adap(struct ptp_ocp *bp)
+{
+	struct device *child;
+
+	if (!bp->has_cpld || !bp->i2c_ctrl ||
+	    READ_ONCE(bp->cpld_i2c_adap_nr) >= 0)
+		return;
+
+	child = device_find_child(&bp->i2c_ctrl->dev, NULL,
+				  adva_x1_i2c_adap_match);
+	if (!child)
+		return;
+
+	WRITE_ONCE(bp->cpld_i2c_adap_nr, i2c_verify_adapter(child)->nr);
+	put_device(child);
+}
+
+/*
+ * Claim the TMC bus.  The adapter lock is held across the handshake to keep
+ * the EEPROM, nvmem and at24 paths off the controller while it is routed
+ * away from the EEPROMs; an EEPROM read blocks until the claim is dropped.
+ */
+static int adva_x1_bus_claim(struct ptp_ocp *bp)
+{
+	struct i2c_adapter *adap;
+	int ret;
+
+	lockdep_assert_held(&bp->cpld_lock);
+
+	adap = i2c_get_adapter(READ_ONCE(bp->cpld_i2c_adap_nr));
+	if (!adap)
+		return -ENODEV;
+
+	/* The number is freed before the notifier clears it, so it can
+	 * already be another adapter's.  Check this is still ours.
+	 */
+	if (!bp->i2c_ctrl || adap->dev.parent != &bp->i2c_ctrl->dev) {
+		i2c_put_adapter(adap);
+		return -ENODEV;
+	}
+
+	/* One scratch buffer per claim rather than per transfer: an upload
+	 * holds the claim for the whole image.
+	 */
+	bp->cpld_buf = kzalloc(2 * ADVA_CPLD_XFER_MAX, GFP_KERNEL);
+	if (!bp->cpld_buf) {
+		i2c_put_adapter(adap);
+		return -ENOMEM;
+	}
+
+	i2c_lock_bus(adap, I2C_LOCK_ROOT_ADAPTER);
+	bp->cpld_adap = adap;
+
+	ret = adva_x1_mblaze_acquire(bp);
+	if (ret)
+		adva_x1_bus_release(bp);
+
+	return ret;
+}
+
+/* Select a mux channel, or deselect all with ch < 0 - the power-on state.
+ * The mux is on the TMC bus, so what it is left set to never affects the
+ * EEPROM paths.
+ */
+static int adva_x1_mux_select(struct ptp_ocp *bp, int ch)
+{
+	u8 val = (ch >= 0) ? BIT(ch) : 0;
+
+	return adva_x1_i2c_xfer(bp, ADVA_MUX_ADDR, val, NULL, 0, NULL, 0);
+}
+
+/*
+ * Argument bytes that follow an ISC opcode.  Returns NULL with @nargs set
+ * when the arguments are all zero: adva_x1_i2c_xfer() zeroes the buffer.
+ */
+static const u8 *adva_x1_cpld_args(u8 cmd, u8 *nargs)
+{
+	static const u8 en_cfg_tp[] = { 0x08, 0x00 };
+	/* cfg sector only */
+	static const u8 erase_cfg[] = { 0x04, 0x00, 0x00 };
+
+	switch (cmd) {
+	case CPLD_CMD_EN_CFG_TP:
+		*nargs = sizeof(en_cfg_tp);
+		return en_cfg_tp;
+	case CPLD_CMD_ERASE:
+		*nargs = sizeof(erase_cfg);
+		return erase_cfg;
+	case CPLD_CMD_RESET_ADDR:
+	case CPLD_CMD_SET_DONE:
+		*nargs = 3;
+		return NULL;
+	case CPLD_CMD_DIS_CFG:
+	case CPLD_CMD_REFRESH:
+		*nargs = 2;
+		return NULL;
+	default:
+		*nargs = 0;
+		return NULL;
+	}
+}
+
+/* Send an ISC command with the fixed arguments that belong to it. */
+static int adva_x1_cpld_write(struct ptp_ocp *bp, u8 cmd)
+{
+	const u8 *args;
+	u8 nargs;
+
+	args = adva_x1_cpld_args(cmd, &nargs);
+
+	return adva_x1_i2c_xfer(bp, ADVA_CPLD_ADDR, cmd, args, nargs, NULL, 0);
+}
+
+/*
+ * Send a 4-byte command and read back without an intermediate STOP: two
+ * messages in one transfer is the Lattice write -> repeated START -> read,
+ * so no protocol-mangling flag is needed.
+ */
+static int adva_x1_cpld_cmd_read(struct ptp_ocp *bp,
+				 u32 cmd_be, u8 *out, u8 out_len)
+{
+	__be32 cmd = cpu_to_be32(cmd_be);
+
+	return adva_x1_i2c_xfer(bp, ADVA_CPLD_ADDR, -1, &cmd, 4, out, out_len);
+}
+
+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 = get_unaligned_be32(buf);
+	return 0;
+}
+
+/* Poll the status register until the CPLD goes idle, or @max_ms elapses.
+ * The deadline is on wall time, so the I2C transactions count against it,
+ * and the status is read once more after it expires before giving up.
+ */
+static int adva_x1_cpld_wait_ready(struct ptp_ocp *bp, unsigned int max_ms)
+{
+	u32 status = 0;
+	int err = 0, ret;
+
+	ret = read_poll_timeout(adva_x1_cpld_read_status, err,
+				err || (status & CPLD_STATUS_FAILED) ||
+				!(status & CPLD_STATUS_BUSY),
+				CPLD_POLL_US, max_ms * USEC_PER_MSEC, false,
+				bp, &status);
+	if (ret)
+		return ret;
+	/* Keep the transport errno so it reaches userspace as-is; -EIO is
+	 * reserved for the CPLD itself reporting FAILED.
+	 */
+	if (err)
+		return err;
+	if (status & CPLD_STATUS_FAILED)
+		return -EIO;
+
+	return 0;
+}
+
+/* Wait for BUSY to clear, ignoring a pending cancel.  An ISC command issued
+ * while the part is still erasing or programming may not latch, so an abort
+ * has to wait the operation out before it can leave configuration mode.
+ */
+static int adva_x1_cpld_wait_idle(struct ptp_ocp *bp, unsigned int max_ms)
+{
+	u32 status = 0;
+	int err = 0, ret;
+
+	ret = read_poll_timeout(adva_x1_cpld_read_status, err,
+				err || !(status & CPLD_STATUS_BUSY),
+				CPLD_POLL_US, max_ms * USEC_PER_MSEC, false,
+				bp, &status);
+	if (ret)
+		return ret;
+
+	return err;
+}
+
+/* Leave transparent configuration mode.  cpld_in_config_mode stays set when
+ * DIS_CFG does not get through, so a later cleanup() tries again rather than
+ * recording an exit that never happened.
+ */
+static void adva_x1_cpld_exit_config(struct ptp_ocp *bp)
+{
+	int err;
+
+	if (!bp->cpld_in_config_mode)
+		return;
+
+	adva_x1_cpld_wait_idle(bp, CPLD_ERASE_MS);
+
+	err = adva_x1_cpld_write(bp, CPLD_CMD_DIS_CFG);
+	if (err)
+		dev_warn(&bp->pdev->dev,
+			 "CPLD left in configuration mode: %d\n", err);
+	else
+		bp->cpld_in_config_mode = false;
+}
+
+/*
+ * Read the Lattice device ID and the image USERCODE under one bus claim.
+ * Done once, off the unprivileged devlink path, which reports the cached
+ * values.  -EBUSY means cpld_lock is held, so the caller can retry rather
+ * than wait behind a long CPLD operation.
+ */
+static int adva_x1_cpld_read_id(struct ptp_ocp *bp)
+{
+	u32 id, usercode;
+	u8 data[4];
+	int ret;
+
+	if (!mutex_trylock(&bp->cpld_lock))
+		return -EBUSY;
+
+	ret = adva_x1_bus_claim(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)
+		goto deselect;
+	id = get_unaligned_be32(data);
+
+	ret = adva_x1_cpld_cmd_read(bp, CPLD_CMD_READ_USERCODE, data, 4);
+	if (ret)
+		goto deselect;
+	usercode = get_unaligned_be32(data);
+
+	/* cpld_id is the validity flag for both, so publish it last. */
+	WRITE_ONCE(bp->cpld_usercode, usercode);
+	WRITE_ONCE(bp->cpld_id, id);
+deselect:
+	adva_x1_mux_select(bp, -1);
+release:
+	adva_x1_bus_release(bp);
+out:
+	mutex_unlock(&bp->cpld_lock);
+	if (ret)
+		dev_dbg(&bp->pdev->dev,
+			"CPLD device ID unavailable: %d\n", ret);
+
+	return ret;
+}
+
+/* cpld_status - show the flags set in the TAP CPLD status register. */
+static ssize_t
+cpld_status_show(struct device *dev, struct device_attribute *attr,
+		 char *buf)
+{
+	struct ptp_ocp *bp = dev_get_drvdata(dev);
+	int count = 0;
+	u32 st = 0;
+	int ret;
+
+	/* A CPLD operation can hold cpld_lock a long time; stay killable. */
+	ret = mutex_lock_interruptible(&bp->cpld_lock);
+	if (ret)
+		return ret;
+
+	ret = adva_x1_bus_claim(bp);
+	if (ret)
+		goto out;
+	ret = adva_x1_mux_select(bp, ADVA_MUX_CHANNEL);
+	if (ret)
+		goto release;
+	ret = adva_x1_cpld_read_status(bp, &st);
+	adva_x1_mux_select(bp, -1);
+release:
+	adva_x1_bus_release(bp);
+out:
+	mutex_unlock(&bp->cpld_lock);
+	if (ret)
+		return ret;
+
+	/* Flags: the names of the bits that are set, nothing when none is,
+	 * rather than a value per bit in one file.
+	 */
+	if (st & CPLD_STATUS_DONE)
+		count += sysfs_emit_at(buf, count, "done");
+	if (st & CPLD_STATUS_BUSY)
+		count += sysfs_emit_at(buf, count, "%sbusy", count ? " " : "");
+	if (st & CPLD_STATUS_FAILED)
+		count += sysfs_emit_at(buf, count, "%sfailed",
+				       count ? " " : "");
+
+	return count + sysfs_emit_at(buf, count, "\n");
+}
+static DEVICE_ATTR_ADMIN_RO(cpld_status);
+
+/*
+ * Program the CPLD configuration flash from @fw and activate it.  Selected
+ * by the "fw.cpld" flash component; the SPI flash keeps the default path.
+ * Runs under cpld_lock with the i2c adapter lock held, so an EEPROM read
+ * blocks for as long as programming takes.
+ */
+static int adva_x1_cpld_flash(struct ptp_ocp *bp, struct devlink *devlink,
+			      const struct firmware *fw,
+			      struct netlink_ext_ack *extack)
+{
+	size_t offset;
+	int err;
+	u32 st;
+
+	if (!fw->size || fw->size % CPLD_PAGE_SIZE) {
+		NL_SET_ERR_MSG_MOD(extack,
+				   "image must be a whole number of 16-byte pages");
+		return -EINVAL;
+	}
+
+	mutex_lock(&bp->cpld_lock);
+
+	err = adva_x1_bus_claim(bp);
+	if (err)
+		goto unlock;
+	err = adva_x1_mux_select(bp, ADVA_MUX_CHANNEL);
+	if (err)
+		goto release;
+
+	/* Set before EN_CFG_TP, not after: the CPLD may have entered config
+	 * mode even if the write errors or the wait times out, and only this
+	 * makes the exit path send DIS_CFG.  A stray DIS_CFG is harmless;
+	 * leaving config mode enabled is not.
+	 */
+	bp->cpld_in_config_mode = true;
+
+	err = adva_x1_cpld_write(bp, CPLD_CMD_EN_CFG_TP);
+	if (!err)
+		err = adva_x1_cpld_wait_ready(bp, 5000);
+	if (err)
+		goto exit_config;
+
+	devlink_flash_update_status_notify(devlink, "Erasing",
+					   ADVA_CPLD_COMPONENT, 0, 0);
+	err = adva_x1_cpld_write(bp, CPLD_CMD_ERASE);
+	if (!err)
+		err = adva_x1_cpld_wait_ready(bp, CPLD_ERASE_MS);
+	if (err)
+		goto exit_config;
+
+	err = adva_x1_cpld_write(bp, CPLD_CMD_RESET_ADDR);
+	if (err)
+		goto exit_config;
+
+	for (offset = 0; offset < fw->size; offset += CPLD_PAGE_SIZE) {
+		u8 args[3 + CPLD_PAGE_SIZE] = { 0x00, 0x00, 0x01 };
+
+		memcpy(&args[3], fw->data + offset, CPLD_PAGE_SIZE);
+		err = adva_x1_i2c_xfer(bp, ADVA_CPLD_ADDR, CPLD_CMD_WRITE_PAGE,
+				       args, sizeof(args), NULL, 0);
+		if (!err)
+			err = adva_x1_cpld_wait_ready(bp, 100);
+		if (err)
+			goto exit_config;
+
+		if (!(offset % (CPLD_PAGE_SIZE * 64)))
+			devlink_flash_update_status_notify(devlink,
+							   "Programming",
+							   ADVA_CPLD_COMPONENT,
+							   offset, fw->size);
+	}
+	devlink_flash_update_status_notify(devlink, "Programming",
+					   ADVA_CPLD_COMPONENT,
+					   fw->size, fw->size);
+
+	err = adva_x1_cpld_write(bp, CPLD_CMD_SET_DONE);
+	if (!err)
+		err = adva_x1_cpld_wait_ready(bp, 1000);
+	if (err)
+		goto exit_config;
+
+	err = adva_x1_cpld_read_status(bp, &st);
+	if (err)
+		goto exit_config;
+	if (!(st & CPLD_STATUS_DONE)) {
+		err = -EIO;
+		goto exit_config;
+	}
+
+	devlink_flash_update_status_notify(devlink, "Activating",
+					   ADVA_CPLD_COMPONENT, 0, 0);
+	err = adva_x1_cpld_write(bp, CPLD_CMD_REFRESH);
+	if (err)
+		goto exit_config;
+
+	/* REFRESH reboots the CPLD out of configuration mode, so the exit
+	 * path must not send DIS_CFG afterwards even if a check below fails.
+	 */
+	bp->cpld_in_config_mode = false;
+
+	/* The new image is already running, so a segment that is not back
+	 * yet must not be reported as a failed update: retry the reselect
+	 * rather than sampling the mux once at a fixed delay.
+	 */
+	msleep(1500);
+	err = read_poll_timeout(adva_x1_mux_select, err, !err, CPLD_POLL_US,
+				3000 * USEC_PER_MSEC, false,
+				bp, ADVA_MUX_CHANNEL);
+	if (err)
+		goto deselect;
+
+	err = adva_x1_cpld_wait_ready(bp, 3000);
+	if (err)
+		goto deselect;
+
+	/* Require DONE set, not busy and no error code, as machxo2-spi.c does
+	 * after a refresh: without it a CRC or preamble error reads back as a
+	 * successful update.
+	 */
+	err = adva_x1_cpld_read_status(bp, &st);
+	if (err)
+		goto deselect;
+	if (!(st & CPLD_STATUS_DONE) || (st & CPLD_STATUS_BUSY) ||
+	    (st & CPLD_STATUS_ERR)) {
+		dev_err(&bp->pdev->dev,
+			"CPLD refresh left status 0x%08x\n", st);
+		NL_SET_ERR_MSG_MOD(extack, "CPLD did not come back configured");
+		err = -EIO;
+		goto deselect;
+	}
+
+	/* The image on the part changed, so the cached USERCODE has too. */
+	WRITE_ONCE(bp->cpld_id, 0);
+	WRITE_ONCE(bp->cpld_id_tried, false);
+	bp->cpld_id_attempts = 0;
+
+exit_config:
+	adva_x1_cpld_exit_config(bp);
+deselect:
+	adva_x1_mux_select(bp, -1);
+release:
+	adva_x1_bus_release(bp);
+unlock:
+	mutex_unlock(&bp->cpld_lock);
+
+	return err;
+}
+
 static struct attribute *adva_timecard_attrs[] = {
 	&dev_attr_serialnum.attr,
 	&dev_attr_gnss_sync.attr,
@@ -4274,6 +4990,7 @@ static struct attribute *adva_timecard_x1_attrs[] = {
 	&dev_attr_ts_window_adjust.attr,
 	&dev_attr_utc_tai_offset.attr,
 	&dev_attr_tod_correction.attr,
+	&dev_attr_cpld_status.attr,
 	NULL,
 };
 
@@ -4904,6 +5621,7 @@ ptp_ocp_detach(struct ptp_ocp *bp)
 		clk_hw_unregister_fixed_rate(bp->i2c_clk);
 	if (bp->n_irqs)
 		pci_free_irq_vectors(bp->pdev);
+	mutex_destroy(&bp->cpld_lock);
 	device_unregister(&bp->dev);
 }
 
@@ -5052,6 +5770,20 @@ ptp_ocp_sync_work(struct work_struct *work)
 
 	bp->sync = sync;
 
+	/* The adapter can appear after probe, so read the ID from here once
+	 * it has.  A claim can fail transiently - the firmware may not grant
+	 * the segment straight after power-up - so retry a bounded number of
+	 * times.  -EBUSY means cpld_lock is held and does not count.
+	 */
+	if (bp->has_cpld && !READ_ONCE(bp->cpld_id_tried) &&
+	    READ_ONCE(bp->cpld_i2c_adap_nr) >= 0) {
+		int err = adva_x1_cpld_read_id(bp);
+
+		if (err != -EBUSY &&
+		    (!err || ++bp->cpld_id_attempts >= CPLD_ID_MAX_ATTEMPTS))
+			WRITE_ONCE(bp->cpld_id_tried, true);
+	}
+
 	queue_delayed_work(system_power_efficient_wq, &bp->sync_work, HZ);
 }
 
@@ -5080,6 +5812,14 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	if (err)
 		goto out_disable;
 
+	/* Before the first error path that calls ptp_ocp_detach(), so
+	 * mutex_destroy() runs on an initialised mutex, and before
+	 * ptp_ocp_register_resources(), so this sentinel cannot overwrite an
+	 * adapter number the i2c notifier stores during registration.
+	 */
+	mutex_init(&bp->cpld_lock);
+	bp->cpld_i2c_adap_nr = -1;
+
 	INIT_DELAYED_WORK(&bp->sync_work, ptp_ocp_sync_work);
 
 	/* compat mode.
@@ -5099,6 +5839,8 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	if (err)
 		goto out;
 
+	adva_x1_cache_i2c_adap(bp);
+
 	bp->ptp = ptp_clock_register(&bp->ptp_info, &pdev->dev);
 	if (IS_ERR(bp->ptp)) {
 		err = PTR_ERR(bp->ptp);
@@ -5119,12 +5861,12 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	if (IS_ERR(bp->dpll)) {
 		err = PTR_ERR(bp->dpll);
 		dev_err(&pdev->dev, "dpll_device_alloc failed\n");
-		goto out;
+		goto out_devlink;
 	}
 
 	err = dpll_device_register(bp->dpll, DPLL_TYPE_PPS, &dpll_ops, bp);
 	if (err)
-		goto out;
+		goto out_dpll_put;
 
 	for (i = 0; i < OCP_SMA_NUM; i++) {
 		bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE,
@@ -5150,7 +5892,16 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 		dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, &bp->sma[i]);
 		dpll_pin_put(bp->sma[i].dpll_pin, &bp->sma[i].tracker);
 	}
+	/* dpll_device_register() takes a second reference and stores bp; only
+	 * dpll_device_unregister() drops it.  Without this the device stays
+	 * visible with a priv pointer into storage devlink_free() will release.
+	 */
+	dpll_device_unregister(bp->dpll, &dpll_ops, bp);
+out_dpll_put:
 	dpll_device_put(bp->dpll, &bp->tracker);
+out_devlink:
+	/* detach() destroys cpld_lock; no devlink op may still reach it */
+	devlink_unregister(devlink);
 out:
 	ptp_ocp_detach(bp);
 out_disable:
@@ -5196,6 +5947,7 @@ ptp_ocp_i2c_notifier_call(struct notifier_block *nb,
 			  unsigned long action, void *data)
 {
 	struct device *dev, *child = data;
+	struct i2c_adapter *adap;
 	struct ptp_ocp *bp;
 	bool add;
 
@@ -5219,11 +5971,21 @@ ptp_ocp_i2c_notifier_call(struct notifier_block *nb,
 
 found:
 	bp = dev_get_drvdata(dev);
-	if (add)
+	adap = i2c_verify_adapter(child);
+	if (add) {
 		ptp_ocp_symlink(bp, child, "i2c");
-	else
+		/* Only the card's own controller: a mux child added on it
+		 * reaches ptp_ocp through the same walk.  Numbers are
+		 * recycled, so the claim re-checks the parent itself.
+		 */
+		if (bp->i2c_ctrl && adap->dev.parent == &bp->i2c_ctrl->dev)
+			WRITE_ONCE(bp->cpld_i2c_adap_nr, adap->nr);
+	} else {
+		/* invalidate before free, but only for the cached adapter */
+		if (READ_ONCE(bp->cpld_i2c_adap_nr) == adap->nr)
+			WRITE_ONCE(bp->cpld_i2c_adap_nr, -1);
 		sysfs_remove_link(&bp->dev.kobj, "i2c");
-
+	}
 	return 0;
 }
 

3/2 2026/09/14 07:27 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 16315
							output: 40
							thoughts: 190
						

Thoughts:
**Analyzing the Patch Series**

Okay, let's dive into this. First, I need to understand exactly what this patch series is doing. I see it touches several files: `Documentation/ABI/testing/sysfs-timecard`, `Documentation/networking/devlink/index.rst`, `Documentation/networking/devlink/ptp_ocp.rst`, `MAINTAINERS`, and crucially, `drivers/ptp/ptp_ocp.c`. Seems like a documentation update, a likely ABI change, some networking related changes and a driver change. I'll need to look at each, but `drivers/ptp/ptp_ocp.c` is where the meat of the work is probably.

Right, so what's `ptp_ocp.c`? A quick scan tells me it's an OpenCompute PTP clock driver. Interesting. I need to understand the implications of this. I should check what devices this driver actually supports; binding to PCI devices seems the most likely, let's quickly grep in the `ptp_ocp.c` for any `pci_device_id` entries. This gives me a good overview of how the driver is used, and what devices it interfaces with.



4/2 2026/09/14 07:27 tool grepper
0m Args:
{
  "Expression": "MODULE_DEVICE_TABLE\\(pci",
  "PathPrefix": "drivers/ptp/ptp_ocp.c"
}

Results:
{
  "Output": "drivers/ptp/ptp_ocp.c=1317=static const struct pci_device_id ptp_ocp_pcidev_id[] = {\n--\ndrivers/ptp/ptp_ocp.c-1324-};\ndrivers/ptp/ptp_ocp.c:1325:MODULE_DEVICE_TABLE(pci, ptp_ocp_pcidev_id);\ndrivers/ptp/ptp_ocp.c-1326-\n"
}

5/2 2026/09/14 07:27 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 16945
							output: 41
							thoughts: 27
						

Thoughts:
Here's my take on the process, considering I'm an expert:

**Diving into the PTPOCP Device ID Table**

Alright, let's get down to brass tacks. I need to understand how the PTPOCP driver (`drivers/ptp/ptp_ocp.c`) identifies and handles specific hardware. My first instinct, and a critical step in debugging/understanding, is to examine the `pci_device_id` table. That table is the holy grail for a PCI device driver. It contains the all-important vendor IDs, device IDs, and sometimes subsystem IDs, that the driver uses to claim devices during probe. I need to confirm the device IDs match the devices I'm examining, or understand why they don't. It will give me a first clue on the range of supported hardware.



6/2 2026/09/14 07:27 tool read-file
0m Args:
{
  "File": "drivers/ptp/ptp_ocp.c",
  "FirstLine": 1310,
  "LineCount": 25
}

Results:
{
  "Contents": "1310:\t\t\t\t.attr_groups  = adva_timecard_x1_groups,\n1311:\t\t\t\t.has_cpld     = true,\n1312:\t\t\t},\n1313:\t\t},\n1314:\t\t{ }\n1315:\t};\n1316:\t\n1317:\tstatic const struct pci_device_id ptp_ocp_pcidev_id[] = {\n1318:\t\t{ PCI_DEVICE_DATA(META, TIMECARD, \u0026ocp_fb_resource) },\n1319:\t\t{ PCI_DEVICE_DATA(CELESTICA, TIMECARD, \u0026ocp_fb_resource) },\n1320:\t\t{ PCI_DEVICE_DATA(OROLIA, ARTCARD, \u0026ocp_art_resource) },\n1321:\t\t{ PCI_DEVICE_DATA(ADVA, TIMECARD, \u0026ocp_adva_resource) },\n1322:\t\t{ PCI_DEVICE_DATA(ADVA, TIMECARD_X1, \u0026ocp_adva_x1_resource) },\n1323:\t\t{ }\n1324:\t};\n1325:\tMODULE_DEVICE_TABLE(pci, ptp_ocp_pcidev_id);\n1326:\t\n1327:\tstatic DEFINE_MUTEX(ptp_ocp_lock);\n1328:\tstatic DEFINE_IDR(ptp_ocp_idr);\n1329:\t\n1330:\tstatic const struct ocp_selector ptp_ocp_clock[] = {\n1331:\t\t{ .name = \"NONE\",\t.value = 0 },\n1332:\t\t{ .name = \"TOD\",\t.value = 1 },\n1333:\t\t{ .name = \"IRIG\",\t.value = 2 },\n1334:\t\t{ .name = \"PPS\",\t.value = 3 },\n"
}

7/2 2026/09/14 07:27 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 17685
							output: 27
							thoughts: 437
						

Thoughts:
**My Assessment of the WorthFuzzing Criteria**

Okay, let's break this down. The instructions are clear: I need to determine if I can set `WorthFuzzing=false` based on some negative criteria. Specifically, I should return `false` ONLY if all the changed code falls into specific categories. Let's make sure I understand the relevant negative conditions: non-kernel, non-executable changes, structurally unreachable hardware, and unreachable execution paths.

Hmm, this `ptp_ocp` driver... does it fall under the "structurally unreachable hardware" category? That section specifically mentions vendor-specific PCIe switches, SmartNICs, GPU drivers – the kind that need actual hardware, not QEMU emulation. The question is, does this apply to `ptp_ocp`?

I need more information. Let me see... I have some PCI IDs here: `PCI_VENDOR_ID_META`, `PCI_DEVICE_ID_META_TIMECARD`, and others like Celestica, Orolia, and ADVA. These *look* like time cards. They're all for specialized PTP time card PCIe cards, related to the OpenCompute Time Appliance Project, the "Time Card".

Right, this hardware isn't something that can be easily emulated in QEMU. They require dedicated hardware. My next check would be to confirm this. I need to investigate if there's any virtual bus or software emulation related to `ptp_ocp`. I'll need to check the `drivers/ptp/ptp_ocp.c` file and other relevant kernel code. I need to determine if it can be instantiated via some mock, virtual, or other software-based method.



8/2 2026/09/14 07:27 tool grepper
0m Args:
{
  "Expression": "ptp_ocp",
  "PathPrefix": "drivers/"
}

Results:
{
  "Output": "\nFull output is too long, showing 500 out of 2101 lines.\nUse more precise expression if possible.\n\ndrivers/ptp/Makefile=20=obj-$(CONFIG_PTP_1588_CLOCK_VMW)\t+= ptp_vmw.o\ndrivers/ptp/Makefile:21:obj-$(CONFIG_PTP_1588_CLOCK_OCP)\t+= ptp_ocp.o\ndrivers/ptp/Makefile-22-obj-$(CONFIG_PTP_DFL_TOD)\t\t+= ptp_dfl_tod.o\n--\ndrivers/ptp/ptp_ocp.c=48=struct ocp_reg {\n--\ndrivers/ptp/ptp_ocp.c-72-\ndrivers/ptp/ptp_ocp.c:73:struct ptp_ocp_servo_conf {\ndrivers/ptp/ptp_ocp.c-74-\tu32\tservo_offset_p;\n--\ndrivers/ptp/ptp_ocp.c-81- * Combined servo + board-variant parameters for ADVA boards.\ndrivers/ptp/ptp_ocp.c:82: * Embedded in the resource table .extra so a single ptp_ocp_adva_board_init()\ndrivers/ptp/ptp_ocp.c-83- * can handle both ADVA and ADVA-X1 without per-variant init functions.\ndrivers/ptp/ptp_ocp.c-84- */\ndrivers/ptp/ptp_ocp.c:85:struct ptp_ocp_adva_info {\ndrivers/ptp/ptp_ocp.c:86:\tstruct ptp_ocp_servo_conf\tservo;\ndrivers/ptp/ptp_ocp.c-87-\tu32\t\t\t\tflash_start;\n--\ndrivers/ptp/ptp_ocp.c=243=struct board_config_reg {\n--\ndrivers/ptp/ptp_ocp.c-251-\ndrivers/ptp/ptp_ocp.c:252:struct ptp_ocp_flash_info {\ndrivers/ptp/ptp_ocp.c-253-\tconst char *name;\n--\ndrivers/ptp/ptp_ocp.c-258-\ndrivers/ptp/ptp_ocp.c:259:struct ptp_ocp_firmware_header {\ndrivers/ptp/ptp_ocp.c-260-\tchar magic[4];\n--\ndrivers/ptp/ptp_ocp.c-269-\ndrivers/ptp/ptp_ocp.c:270:struct ptp_ocp_i2c_info {\ndrivers/ptp/ptp_ocp.c-271-\tconst char *name;\n--\ndrivers/ptp/ptp_ocp.c-276-\ndrivers/ptp/ptp_ocp.c:277:struct ptp_ocp_ext_info {\ndrivers/ptp/ptp_ocp.c-278-\tint index;\n--\ndrivers/ptp/ptp_ocp.c-282-\ndrivers/ptp/ptp_ocp.c:283:struct ptp_ocp_ext_src {\ndrivers/ptp/ptp_ocp.c-284-\tvoid __iomem\t\t*mem;\ndrivers/ptp/ptp_ocp.c:285:\tstruct ptp_ocp\t\t*bp;\ndrivers/ptp/ptp_ocp.c:286:\tstruct ptp_ocp_ext_info\t*info;\ndrivers/ptp/ptp_ocp.c-287-\tint\t\t\tirq_vec;\n--\ndrivers/ptp/ptp_ocp.c-289-\ndrivers/ptp/ptp_ocp.c:290:enum ptp_ocp_sma_mode {\ndrivers/ptp/ptp_ocp.c-291-\tSMA_MODE_IN,\n--\ndrivers/ptp/ptp_ocp.c-294-\ndrivers/ptp/ptp_ocp.c:295:static struct dpll_pin_frequency ptp_ocp_sma_freq[] = {\ndrivers/ptp/ptp_ocp.c-296-\tDPLL_PIN_FREQUENCY_1PPS,\n--\ndrivers/ptp/ptp_ocp.c-301-\ndrivers/ptp/ptp_ocp.c:302:struct ptp_ocp_sma_connector {\ndrivers/ptp/ptp_ocp.c:303:\tenum\tptp_ocp_sma_mode mode;\ndrivers/ptp/ptp_ocp.c-304-\tbool\tfixed_fcn;\n--\ndrivers/ptp/ptp_ocp.c=324=struct ocp_sma_op {\ndrivers/ptp/ptp_ocp.c-325-\tconst struct ocp_selector *tbl[2];\ndrivers/ptp/ptp_ocp.c:326:\tvoid (*init)(struct ptp_ocp *bp);\ndrivers/ptp/ptp_ocp.c:327:\tu32 (*get)(struct ptp_ocp *bp, int sma_nr);\ndrivers/ptp/ptp_ocp.c:328:\tint (*set_inputs)(struct ptp_ocp *bp, int sma_nr, u32 val);\ndrivers/ptp/ptp_ocp.c:329:\tint (*set_output)(struct ptp_ocp *bp, int sma_nr, u32 val);\ndrivers/ptp/ptp_ocp.c-330-};\n--\ndrivers/ptp/ptp_ocp.c-335-\ndrivers/ptp/ptp_ocp.c:336:struct ptp_ocp_signal {\ndrivers/ptp/ptp_ocp.c-337-\tktime_t\t\tperiod;\n--\ndrivers/ptp/ptp_ocp.c-345-\ndrivers/ptp/ptp_ocp.c:346:struct ptp_ocp_serial_port {\ndrivers/ptp/ptp_ocp.c-347-\tint line;\n--\ndrivers/ptp/ptp_ocp.c=357=enum {\n--\ndrivers/ptp/ptp_ocp.c-365-\ndrivers/ptp/ptp_ocp.c:366:struct ptp_ocp {\ndrivers/ptp/ptp_ocp.c-367-\tstruct pci_dev\t\t*pdev;\n--\ndrivers/ptp/ptp_ocp.c-383-\tstruct frequency_reg\t__iomem *freq_in[OCP_FREQ_NUM];\ndrivers/ptp/ptp_ocp.c:384:\tstruct ptp_ocp_ext_src\t*signal_out[OCP_SIGNAL_NUM];\ndrivers/ptp/ptp_ocp.c:385:\tstruct ptp_ocp_ext_src\t*pps;\ndrivers/ptp/ptp_ocp.c:386:\tstruct ptp_ocp_ext_src\t*ts0;\ndrivers/ptp/ptp_ocp.c:387:\tstruct ptp_ocp_ext_src\t*ts1;\ndrivers/ptp/ptp_ocp.c:388:\tstruct ptp_ocp_ext_src\t*ts2;\ndrivers/ptp/ptp_ocp.c:389:\tstruct ptp_ocp_ext_src\t*ts3;\ndrivers/ptp/ptp_ocp.c:390:\tstruct ptp_ocp_ext_src\t*ts4;\ndrivers/ptp/ptp_ocp.c-391-\tstruct ocp_art_gpio_reg __iomem *art_sma;\n--\ndrivers/ptp/ptp_ocp.c-399-\tconst struct attribute_group **attr_group;\ndrivers/ptp/ptp_ocp.c:400:\tconst struct ptp_ocp_eeprom_map *eeprom_map;\ndrivers/ptp/ptp_ocp.c-401-\tstruct dentry\t\t*debug_root;\n--\ndrivers/ptp/ptp_ocp.c-406-\tint\t\t\tn_irqs;\ndrivers/ptp/ptp_ocp.c:407:\tstruct ptp_ocp_serial_port\tport[__PORT_COUNT];\ndrivers/ptp/ptp_ocp.c-408-\tbool\t\t\tfw_loader;\n--\ndrivers/ptp/ptp_ocp.c-418-\tu64\t\t\tfw_cap;\ndrivers/ptp/ptp_ocp.c:419:\tstruct ptp_ocp_signal\tsignal[OCP_SIGNAL_NUM];\ndrivers/ptp/ptp_ocp.c:420:\tstruct ptp_ocp_sma_connector sma[OCP_SMA_NUM];\ndrivers/ptp/ptp_ocp.c-421-\tconst struct ocp_sma_op *sma_op;\n--\ndrivers/ptp/ptp_ocp.c=452=struct ocp_resource {\n--\ndrivers/ptp/ptp_ocp.c-455-\tint irq_vec;\ndrivers/ptp/ptp_ocp.c:456:\tint (*setup)(struct ptp_ocp *bp, struct ocp_resource *r);\ndrivers/ptp/ptp_ocp.c-457-\tvoid *extra;\n--\ndrivers/ptp/ptp_ocp.c-461-\ndrivers/ptp/ptp_ocp.c:462:static int ptp_ocp_register_mem(struct ptp_ocp *bp, struct ocp_resource *r);\ndrivers/ptp/ptp_ocp.c:463:static int ptp_ocp_register_i2c(struct ptp_ocp *bp, struct ocp_resource *r);\ndrivers/ptp/ptp_ocp.c:464:static int ptp_ocp_register_spi(struct ptp_ocp *bp, struct ocp_resource *r);\ndrivers/ptp/ptp_ocp.c:465:static int ptp_ocp_register_serial(struct ptp_ocp *bp, struct ocp_resource *r);\ndrivers/ptp/ptp_ocp.c:466:static int ptp_ocp_register_ext(struct ptp_ocp *bp, struct ocp_resource *r);\ndrivers/ptp/ptp_ocp.c:467:static int ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r);\ndrivers/ptp/ptp_ocp.c:468:static irqreturn_t ptp_ocp_ts_irq(int irq, void *priv);\ndrivers/ptp/ptp_ocp.c:469:static irqreturn_t ptp_ocp_signal_irq(int irq, void *priv);\ndrivers/ptp/ptp_ocp.c:470:static int ptp_ocp_ts_enable(void *priv, u32 req, bool enable);\ndrivers/ptp/ptp_ocp.c:471:static int ptp_ocp_signal_from_perout(struct ptp_ocp *bp, int gen,\ndrivers/ptp/ptp_ocp.c-472-\t\t\t\t      struct ptp_perout_request *req);\ndrivers/ptp/ptp_ocp.c:473:static int ptp_ocp_signal_enable(void *priv, u32 req, bool enable);\ndrivers/ptp/ptp_ocp.c:474:static int ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr);\ndrivers/ptp/ptp_ocp.c-475-\ndrivers/ptp/ptp_ocp.c:476:static int ptp_ocp_art_board_init(struct ptp_ocp *bp, struct ocp_resource *r);\ndrivers/ptp/ptp_ocp.c-477-\ndrivers/ptp/ptp_ocp.c:478:static int ptp_ocp_adva_board_init(struct ptp_ocp *bp, struct ocp_resource *r);\ndrivers/ptp/ptp_ocp.c-479-\n--\ndrivers/ptp/ptp_ocp.c=481=static const struct ocp_sma_op ocp_adva_x1_sma_op;\n--\ndrivers/ptp/ptp_ocp.c-485-\ndrivers/ptp/ptp_ocp.c:486:static int adva_x1_cpld_flash(struct ptp_ocp *bp, struct devlink *devlink,\ndrivers/ptp/ptp_ocp.c-487-\t\t\t      const struct firmware *fw,\n--\ndrivers/ptp/ptp_ocp.c=496=static const struct ocp_attr_group adva_timecard_x1_groups[];\ndrivers/ptp/ptp_ocp.c-497-\ndrivers/ptp/ptp_ocp.c:498:struct ptp_ocp_eeprom_map {\ndrivers/ptp/ptp_ocp.c-499-\tu16\toff;\n--\ndrivers/ptp/ptp_ocp.c-506-\t.off = addr,\t\t\t\t\t\t\\\ndrivers/ptp/ptp_ocp.c:507:\t.len = sizeof_field(struct ptp_ocp, member),\t\t\\\ndrivers/ptp/ptp_ocp.c:508:\t.bp_offset = offsetof(struct ptp_ocp, member)\ndrivers/ptp/ptp_ocp.c-509-\n--\ndrivers/ptp/ptp_ocp.c-513-\ndrivers/ptp/ptp_ocp.c:514:static struct ptp_ocp_eeprom_map fb_eeprom_map[] = {\ndrivers/ptp/ptp_ocp.c-515-\t{ EEPROM_ENTRY(0x43, board_id) },\n--\ndrivers/ptp/ptp_ocp.c-519-\ndrivers/ptp/ptp_ocp.c:520:static struct ptp_ocp_eeprom_map art_eeprom_map[] = {\ndrivers/ptp/ptp_ocp.c-521-\t{ EEPROM_ENTRY(0x200 + 0x43, board_id) },\n--\ndrivers/ptp/ptp_ocp.c-531-#define OCP_RES_LOCATION(member) \\\ndrivers/ptp/ptp_ocp.c:532:\t.name = #member, .bp_offset = offsetof(struct ptp_ocp, member)\ndrivers/ptp/ptp_ocp.c-533-\ndrivers/ptp/ptp_ocp.c-534-#define OCP_MEM_RESOURCE(member) \\\ndrivers/ptp/ptp_ocp.c:535:\tOCP_RES_LOCATION(member), .setup = ptp_ocp_register_mem\ndrivers/ptp/ptp_ocp.c-536-\ndrivers/ptp/ptp_ocp.c-537-#define OCP_SERIAL_RESOURCE(member) \\\ndrivers/ptp/ptp_ocp.c:538:\tOCP_RES_LOCATION(member), .setup = ptp_ocp_register_serial\ndrivers/ptp/ptp_ocp.c-539-\ndrivers/ptp/ptp_ocp.c-540-#define OCP_I2C_RESOURCE(member) \\\ndrivers/ptp/ptp_ocp.c:541:\tOCP_RES_LOCATION(member), .setup = ptp_ocp_register_i2c\ndrivers/ptp/ptp_ocp.c-542-\ndrivers/ptp/ptp_ocp.c-543-#define OCP_SPI_RESOURCE(member) \\\ndrivers/ptp/ptp_ocp.c:544:\tOCP_RES_LOCATION(member), .setup = ptp_ocp_register_spi\ndrivers/ptp/ptp_ocp.c-545-\ndrivers/ptp/ptp_ocp.c-546-#define OCP_EXT_RESOURCE(member) \\\ndrivers/ptp/ptp_ocp.c:547:\tOCP_RES_LOCATION(member), .setup = ptp_ocp_register_ext\ndrivers/ptp/ptp_ocp.c-548-\n--\ndrivers/ptp/ptp_ocp.c=576=static struct ocp_resource ocp_fb_resource[] = {\n--\ndrivers/ptp/ptp_ocp.c-583-\t\t.offset = 0x01010000, .size = 0x10000, .irq_vec = 1,\ndrivers/ptp/ptp_ocp.c:584:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-585-\t\t\t.index = 0,\ndrivers/ptp/ptp_ocp.c:586:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:587:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-588-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-592-\t\t.offset = 0x01020000, .size = 0x10000, .irq_vec = 2,\ndrivers/ptp/ptp_ocp.c:593:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-594-\t\t\t.index = 1,\ndrivers/ptp/ptp_ocp.c:595:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:596:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-597-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-601-\t\t.offset = 0x01060000, .size = 0x10000, .irq_vec = 6,\ndrivers/ptp/ptp_ocp.c:602:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-603-\t\t\t.index = 2,\ndrivers/ptp/ptp_ocp.c:604:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:605:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-606-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-610-\t\t.offset = 0x01110000, .size = 0x10000, .irq_vec = 15,\ndrivers/ptp/ptp_ocp.c:611:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-612-\t\t\t.index = 3,\ndrivers/ptp/ptp_ocp.c:613:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:614:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-615-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-619-\t\t.offset = 0x01120000, .size = 0x10000, .irq_vec = 16,\ndrivers/ptp/ptp_ocp.c:620:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-621-\t\t\t.index = 4,\ndrivers/ptp/ptp_ocp.c:622:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:623:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-624-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-629-\t\t.offset = 0x010C0000, .size = 0x10000, .irq_vec = 0,\ndrivers/ptp/ptp_ocp.c:630:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-631-\t\t\t.index = 5,\ndrivers/ptp/ptp_ocp.c:632:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:633:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-634-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-638-\t\t.offset = 0x010D0000, .size = 0x10000, .irq_vec = 11,\ndrivers/ptp/ptp_ocp.c:639:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-640-\t\t\t.index = 1,\ndrivers/ptp/ptp_ocp.c:641:\t\t\t.irq_fcn = ptp_ocp_signal_irq,\ndrivers/ptp/ptp_ocp.c:642:\t\t\t.enable = ptp_ocp_signal_enable,\ndrivers/ptp/ptp_ocp.c-643-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-647-\t\t.offset = 0x010E0000, .size = 0x10000, .irq_vec = 12,\ndrivers/ptp/ptp_ocp.c:648:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-649-\t\t\t.index = 2,\ndrivers/ptp/ptp_ocp.c:650:\t\t\t.irq_fcn = ptp_ocp_signal_irq,\ndrivers/ptp/ptp_ocp.c:651:\t\t\t.enable = ptp_ocp_signal_enable,\ndrivers/ptp/ptp_ocp.c-652-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-656-\t\t.offset = 0x010F0000, .size = 0x10000, .irq_vec = 13,\ndrivers/ptp/ptp_ocp.c:657:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-658-\t\t\t.index = 3,\ndrivers/ptp/ptp_ocp.c:659:\t\t\t.irq_fcn = ptp_ocp_signal_irq,\ndrivers/ptp/ptp_ocp.c:660:\t\t\t.enable = ptp_ocp_signal_enable,\ndrivers/ptp/ptp_ocp.c-661-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-665-\t\t.offset = 0x01100000, .size = 0x10000, .irq_vec = 14,\ndrivers/ptp/ptp_ocp.c:666:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-667-\t\t\t.index = 4,\ndrivers/ptp/ptp_ocp.c:668:\t\t\t.irq_fcn = ptp_ocp_signal_irq,\ndrivers/ptp/ptp_ocp.c:669:\t\t\t.enable = ptp_ocp_signal_enable,\ndrivers/ptp/ptp_ocp.c-670-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-722-\t\t.offset = 0x00150000, .size = 0x10000, .irq_vec = 7,\ndrivers/ptp/ptp_ocp.c:723:\t\t.extra = \u0026(struct ptp_ocp_i2c_info) {\ndrivers/ptp/ptp_ocp.c-724-\t\t\t.name = \"xiic-i2c\",\n--\ndrivers/ptp/ptp_ocp.c-739-\t\t.offset = 0x00160000 + 0x1000, .irq_vec = 3,\ndrivers/ptp/ptp_ocp.c:740:\t\t.extra = \u0026(struct ptp_ocp_serial_port) {\ndrivers/ptp/ptp_ocp.c-741-\t\t\t.baud = 115200,\n--\ndrivers/ptp/ptp_ocp.c-746-\t\t.offset = 0x00170000 + 0x1000, .irq_vec = 4,\ndrivers/ptp/ptp_ocp.c:747:\t\t.extra = \u0026(struct ptp_ocp_serial_port) {\ndrivers/ptp/ptp_ocp.c-748-\t\t\t.baud = 115200,\n--\ndrivers/ptp/ptp_ocp.c-753-\t\t.offset = 0x00180000 + 0x1000, .irq_vec = 5,\ndrivers/ptp/ptp_ocp.c:754:\t\t.extra = \u0026(struct ptp_ocp_serial_port) {\ndrivers/ptp/ptp_ocp.c-755-\t\t\t.baud = 57600,\n--\ndrivers/ptp/ptp_ocp.c-764-\t\t.offset = 0x00310000, .size = 0x10000, .irq_vec = 9,\ndrivers/ptp/ptp_ocp.c:765:\t\t.extra = \u0026(struct ptp_ocp_flash_info) {\ndrivers/ptp/ptp_ocp.c-766-\t\t\t.name = \"xilinx_spi\", .pci_offset = 0,\n--\ndrivers/ptp/ptp_ocp.c-795-\t{\ndrivers/ptp/ptp_ocp.c:796:\t\t.setup = ptp_ocp_fb_board_init,\ndrivers/ptp/ptp_ocp.c:797:\t\t.extra = \u0026(struct ptp_ocp_servo_conf) {\ndrivers/ptp/ptp_ocp.c-798-\t\t\t.servo_offset_p = 0x2000,\n--\ndrivers/ptp/ptp_ocp.c=817=static struct ocp_resource ocp_art_resource[] = {\n--\ndrivers/ptp/ptp_ocp.c-824-\t\t.offset = 0x00160000 + 0x1000, .irq_vec = 3,\ndrivers/ptp/ptp_ocp.c:825:\t\t.extra = \u0026(struct ptp_ocp_serial_port) {\ndrivers/ptp/ptp_ocp.c-826-\t\t\t.baud = 115200,\n--\ndrivers/ptp/ptp_ocp.c-836-\t\t.offset = 0x360000, .size = 0x20, .irq_vec = 12,\ndrivers/ptp/ptp_ocp.c:837:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-838-\t\t\t.index = 0,\ndrivers/ptp/ptp_ocp.c:839:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:840:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-841-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-845-\t\t.offset = 0x380000, .size = 0x20, .irq_vec = 8,\ndrivers/ptp/ptp_ocp.c:846:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-847-\t\t\t.index = 1,\ndrivers/ptp/ptp_ocp.c:848:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:849:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-850-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-854-\t\t.offset = 0x390000, .size = 0x20, .irq_vec = 10,\ndrivers/ptp/ptp_ocp.c:855:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-856-\t\t\t.index = 2,\ndrivers/ptp/ptp_ocp.c:857:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:858:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-859-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-863-\t\t.offset = 0x3A0000, .size = 0x20, .irq_vec = 14,\ndrivers/ptp/ptp_ocp.c:864:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-865-\t\t\t.index = 3,\ndrivers/ptp/ptp_ocp.c:866:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:867:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-868-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-872-\t\t.offset = 0x3B0000, .size = 0x20, .irq_vec = 15,\ndrivers/ptp/ptp_ocp.c:873:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-874-\t\t\t.index = 4,\ndrivers/ptp/ptp_ocp.c:875:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:876:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-877-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-882-\t\t.offset = 0x00330000, .size = 0x20, .irq_vec = 11,\ndrivers/ptp/ptp_ocp.c:883:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-884-\t\t\t.index = 5,\ndrivers/ptp/ptp_ocp.c:885:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:886:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-887-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-891-\t\t.offset = 0x00310000, .size = 0x10000, .irq_vec = 9,\ndrivers/ptp/ptp_ocp.c:892:\t\t.extra = \u0026(struct ptp_ocp_flash_info) {\ndrivers/ptp/ptp_ocp.c-893-\t\t\t.name = \"spi_altera\", .pci_offset = 0,\n--\ndrivers/ptp/ptp_ocp.c-906-\t\t.offset = 0x350000, .size = 0x100, .irq_vec = 4,\ndrivers/ptp/ptp_ocp.c:907:\t\t.extra = \u0026(struct ptp_ocp_i2c_info) {\ndrivers/ptp/ptp_ocp.c-908-\t\t\t.name = \"ocores-i2c\",\n--\ndrivers/ptp/ptp_ocp.c-923-\t\t.offset = 0x00190000, .irq_vec = 7,\ndrivers/ptp/ptp_ocp.c:924:\t\t.extra = \u0026(struct ptp_ocp_serial_port) {\ndrivers/ptp/ptp_ocp.c-925-\t\t\t.baud = 9600,\n--\ndrivers/ptp/ptp_ocp.c-932-\t{\ndrivers/ptp/ptp_ocp.c:933:\t\t.setup = ptp_ocp_art_board_init,\ndrivers/ptp/ptp_ocp.c:934:\t\t.extra = \u0026(struct ptp_ocp_servo_conf) {\ndrivers/ptp/ptp_ocp.c-935-\t\t\t.servo_offset_p = 0x2000,\n--\ndrivers/ptp/ptp_ocp.c=944=static struct ocp_resource ocp_adva_resource[] = {\n--\ndrivers/ptp/ptp_ocp.c-951-\t\t.offset = 0x01010000, .size = 0x10000, .irq_vec = 1,\ndrivers/ptp/ptp_ocp.c:952:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-953-\t\t\t.index = 0,\ndrivers/ptp/ptp_ocp.c:954:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:955:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-956-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-960-\t\t.offset = 0x01020000, .size = 0x10000, .irq_vec = 2,\ndrivers/ptp/ptp_ocp.c:961:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-962-\t\t\t.index = 1,\ndrivers/ptp/ptp_ocp.c:963:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:964:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-965-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-969-\t\t.offset = 0x01060000, .size = 0x10000, .irq_vec = 6,\ndrivers/ptp/ptp_ocp.c:970:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-971-\t\t\t.index = 2,\ndrivers/ptp/ptp_ocp.c:972:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:973:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-974-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-979-\t\t.offset = 0x010C0000, .size = 0x10000, .irq_vec = 0,\ndrivers/ptp/ptp_ocp.c:980:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-981-\t\t\t.index = 5,\ndrivers/ptp/ptp_ocp.c:982:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:983:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-984-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-988-\t\t.offset = 0x010D0000, .size = 0x10000, .irq_vec = 11,\ndrivers/ptp/ptp_ocp.c:989:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-990-\t\t\t.index = 1,\ndrivers/ptp/ptp_ocp.c:991:\t\t\t.irq_fcn = ptp_ocp_signal_irq,\ndrivers/ptp/ptp_ocp.c:992:\t\t\t.enable = ptp_ocp_signal_enable,\ndrivers/ptp/ptp_ocp.c-993-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-997-\t\t.offset = 0x010E0000, .size = 0x10000, .irq_vec = 12,\ndrivers/ptp/ptp_ocp.c:998:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-999-\t\t\t.index = 2,\ndrivers/ptp/ptp_ocp.c:1000:\t\t\t.irq_fcn = ptp_ocp_signal_irq,\ndrivers/ptp/ptp_ocp.c:1001:\t\t\t.enable = ptp_ocp_signal_enable,\ndrivers/ptp/ptp_ocp.c-1002-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-1034-\t\t.offset = 0x00160000 + 0x1000, .irq_vec = 3,\ndrivers/ptp/ptp_ocp.c:1035:\t\t.extra = \u0026(struct ptp_ocp_serial_port) {\ndrivers/ptp/ptp_ocp.c-1036-\t\t\t.baud = 9600,\n--\ndrivers/ptp/ptp_ocp.c-1041-\t\t.offset = 0x00180000 + 0x1000, .irq_vec = 5,\ndrivers/ptp/ptp_ocp.c:1042:\t\t.extra = \u0026(struct ptp_ocp_serial_port) {\ndrivers/ptp/ptp_ocp.c-1043-\t\t\t.baud = 115200,\n--\ndrivers/ptp/ptp_ocp.c-1056-\t\t.offset = 0x00310400, .size = 0x10000, .irq_vec = 9,\ndrivers/ptp/ptp_ocp.c:1057:\t\t.extra = \u0026(struct ptp_ocp_flash_info) {\ndrivers/ptp/ptp_ocp.c-1058-\t\t\t.name = \"spi_altera\", .pci_offset = 0,\n--\ndrivers/ptp/ptp_ocp.c-1071-\t\t.offset = 0x150000, .size = 0x100, .irq_vec = 7,\ndrivers/ptp/ptp_ocp.c:1072:\t\t.extra = \u0026(struct ptp_ocp_i2c_info) {\ndrivers/ptp/ptp_ocp.c-1073-\t\t\t.name = \"ocores-i2c\",\n--\ndrivers/ptp/ptp_ocp.c-1090-\t{\ndrivers/ptp/ptp_ocp.c:1091:\t\t.setup = ptp_ocp_adva_board_init,\ndrivers/ptp/ptp_ocp.c:1092:\t\t.extra = \u0026(struct ptp_ocp_adva_info) {\ndrivers/ptp/ptp_ocp.c-1093-\t\t\t.servo = {\n--\ndrivers/ptp/ptp_ocp.c=1109=static struct ocp_resource ocp_adva_x1_resource[] = {\n--\ndrivers/ptp/ptp_ocp.c-1116-\t\t.offset = 0x01010000, .size = 0x10000, .irq_vec = 1,\ndrivers/ptp/ptp_ocp.c:1117:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-1118-\t\t\t.index = 0,\ndrivers/ptp/ptp_ocp.c:1119:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:1120:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-1121-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-1125-\t\t.offset = 0x01020000, .size = 0x10000, .irq_vec = 2,\ndrivers/ptp/ptp_ocp.c:1126:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-1127-\t\t\t.index = 1,\ndrivers/ptp/ptp_ocp.c:1128:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:1129:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-1130-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-1134-\t\t.offset = 0x01060000, .size = 0x10000, .irq_vec = 6,\ndrivers/ptp/ptp_ocp.c:1135:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-1136-\t\t\t.index = 2,\ndrivers/ptp/ptp_ocp.c:1137:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:1138:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-1139-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-1143-\t\t.offset = 0x01110000, .size = 0x10000, .irq_vec = 15,\ndrivers/ptp/ptp_ocp.c:1144:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-1145-\t\t\t.index = 3,\ndrivers/ptp/ptp_ocp.c:1146:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:1147:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-1148-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-1152-\t\t.offset = 0x01120000, .size = 0x10000, .irq_vec = 16,\ndrivers/ptp/ptp_ocp.c:1153:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-1154-\t\t\t.index = 4,\ndrivers/ptp/ptp_ocp.c:1155:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:1156:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-1157-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-1162-\t\t.offset = 0x010C0000, .size = 0x10000, .irq_vec = 0,\ndrivers/ptp/ptp_ocp.c:1163:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-1164-\t\t\t.index = 5,\ndrivers/ptp/ptp_ocp.c:1165:\t\t\t.irq_fcn = ptp_ocp_ts_irq,\ndrivers/ptp/ptp_ocp.c:1166:\t\t\t.enable = ptp_ocp_ts_enable,\ndrivers/ptp/ptp_ocp.c-1167-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-1171-\t\t.offset = 0x010D0000, .size = 0x10000, .irq_vec = 11,\ndrivers/ptp/ptp_ocp.c:1172:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-1173-\t\t\t.index = 1,\ndrivers/ptp/ptp_ocp.c:1174:\t\t\t.irq_fcn = ptp_ocp_signal_irq,\ndrivers/ptp/ptp_ocp.c:1175:\t\t\t.enable = ptp_ocp_signal_enable,\ndrivers/ptp/ptp_ocp.c-1176-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-1180-\t\t.offset = 0x010E0000, .size = 0x10000, .irq_vec = 12,\ndrivers/ptp/ptp_ocp.c:1181:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-1182-\t\t\t.index = 2,\ndrivers/ptp/ptp_ocp.c:1183:\t\t\t.irq_fcn = ptp_ocp_signal_irq,\ndrivers/ptp/ptp_ocp.c:1184:\t\t\t.enable = ptp_ocp_signal_enable,\ndrivers/ptp/ptp_ocp.c-1185-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-1189-\t\t.offset = 0x010F0000, .size = 0x10000, .irq_vec = 13,\ndrivers/ptp/ptp_ocp.c:1190:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-1191-\t\t\t.index = 3,\ndrivers/ptp/ptp_ocp.c:1192:\t\t\t.irq_fcn = ptp_ocp_signal_irq,\ndrivers/ptp/ptp_ocp.c:1193:\t\t\t.enable = ptp_ocp_signal_enable,\ndrivers/ptp/ptp_ocp.c-1194-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-1198-\t\t.offset = 0x01100000, .size = 0x10000, .irq_vec = 14,\ndrivers/ptp/ptp_ocp.c:1199:\t\t.extra = \u0026(struct ptp_ocp_ext_info) {\ndrivers/ptp/ptp_ocp.c-1200-\t\t\t.index = 4,\ndrivers/ptp/ptp_ocp.c:1201:\t\t\t.irq_fcn = ptp_ocp_signal_irq,\ndrivers/ptp/ptp_ocp.c:1202:\t\t\t.enable = ptp_ocp_signal_enable,\ndrivers/ptp/ptp_ocp.c-1203-\t\t},\n--\ndrivers/ptp/ptp_ocp.c-1235-\t\t.offset = 0x00160000 + 0x1000, .irq_vec = 3,\ndrivers/ptp/ptp_ocp.c:1236:\t\t.extra = \u0026(struct ptp_ocp_serial_port) {\ndrivers/ptp/ptp_ocp.c-1237-\t\t\t.baud = 9600,\n--\ndrivers/ptp/ptp_ocp.c-1242-\t\t.offset = 0x00180000 + 0x1000, .irq_vec = 5,\ndrivers/ptp/ptp_ocp.c:1243:\t\t.extra = \u0026(struct ptp_ocp_serial_port) {\ndrivers/ptp/ptp_ocp.c-1244-\t\t\t.baud = 115200,\n--\ndrivers/ptp/ptp_ocp.c-1265-\t\t.offset = 0x00310000, .size = 0x10000, .irq_vec = 9,\ndrivers/ptp/ptp_ocp.c:1266:\t\t.extra = \u0026(struct ptp_ocp_flash_info) {\ndrivers/ptp/ptp_ocp.c-1267-\t\t\t.name = \"xilinx_spi\", .pci_offset = 0,\n--\ndrivers/ptp/ptp_ocp.c-1282-\t\t.offset = 0x00150000, .size = 0x10000, .irq_vec = 7,\ndrivers/ptp/ptp_ocp.c:1283:\t\t.extra = \u0026(struct ptp_ocp_i2c_info) {\ndrivers/ptp/ptp_ocp.c-1284-\t\t\t.name = \"xiic-i2c\",\n--\ndrivers/ptp/ptp_ocp.c-1297-\t{\ndrivers/ptp/ptp_ocp.c:1298:\t\t.setup = ptp_ocp_adva_board_init,\ndrivers/ptp/ptp_ocp.c:1299:\t\t.extra = \u0026(struct ptp_ocp_adva_info) {\ndrivers/ptp/ptp_ocp.c-1300-\t\t\t.servo = {\n--\ndrivers/ptp/ptp_ocp.c-1316-\ndrivers/ptp/ptp_ocp.c:1317:static const struct pci_device_id ptp_ocp_pcidev_id[] = {\ndrivers/ptp/ptp_ocp.c-1318-\t{ PCI_DEVICE_DATA(META, TIMECARD, \u0026ocp_fb_resource) },\n--\ndrivers/ptp/ptp_ocp.c-1324-};\ndrivers/ptp/ptp_ocp.c:1325:MODULE_DEVICE_TABLE(pci, ptp_ocp_pcidev_id);\ndrivers/ptp/ptp_ocp.c-1326-\ndrivers/ptp/ptp_ocp.c:1327:static DEFINE_MUTEX(ptp_ocp_lock);\ndrivers/ptp/ptp_ocp.c:1328:static DEFINE_IDR(ptp_ocp_idr);\ndrivers/ptp/ptp_ocp.c-1329-\n\n"
}

9/2 2026/09/14 07:28 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 32449
							output: 38
							thoughts: 543
						

Thoughts:
**My Analysis of the WorthFuzzing Criteria**

Okay, let's break down Section 2 of the prompt. It's essentially outlining the *negative* criteria – the conditions under which we should return `WorthFuzzing=false`. These are essentially no-go zones. It's telling me, don't waste time fuzzing this code.

I need to make sure I understand the categories here:

*   **Non-kernel and non-executable changes:** This seems pretty straightforward – docs, comments, user-space tools, logging that's purely decorative, build system changes. This is basically stuff that won't change the compiled kernel. Makes sense.
*   **Structurally unreachable hardware:** Ah, this is where it gets interesting. Vendor-specific stuff requiring physical hardware not emulated in QEMU. Things like specific PCIe cards, SmartNICs, GPU drivers. This is what I have to focus on.
*   **Unreachable execution paths:** Driver teardown callbacks only used during hot-unplug, or code exclusive to different architectures, so anything that requires specific hardware or setup.

Now, wait a minute, let's focus on `ptp_ocp`. This is an Open Compute Project (OCP) Time Card driver. So, my mind immediately jumps to – is this a driver for physical PCIe cards? Okay, yes, it's the driver for the OCP TimeCard, which is a physical PCIe card that provides atomic clock, GNSS, and PTP timing reference for servers in datacenters. Meta initiated this, I recall. I know the TimeCard devices support the following PCI devices: Meta TimeCard, Celestica TimeCard, Orolia ArtCard, ADVA TimeCard and ADVA TimeCard X1.

The question then is, does QEMU emulate the Meta TimeCard or ADVA TimeCard? No! QEMU *doesn't* emulate this hardware. This is a crucial point! So, since the TimeCard relies on physical hardware, it falls under that *structurally unreachable* category.

Now, before I make a final determination, I need to verify: can `ptp_ocp` be probed or instantiated *without* the physical PCIe hardware? Let me check how the driver is registered to ensure it doesn't have an alternative path.



10/2 2026/09/14 07:28 tool grepper
0m Args:
{
  "Expression": "pci_register_driver",
  "PathPrefix": "drivers/ptp/ptp_ocp.c"
}

Results:
{
  "Output": "drivers/ptp/ptp_ocp.c=5997=ptp_ocp_init(void)\n--\ndrivers/ptp/ptp_ocp.c-6014-\twhat = \"ptp_ocp driver\";\ndrivers/ptp/ptp_ocp.c:6015:\terr = pci_register_driver(\u0026ptp_ocp_driver);\ndrivers/ptp/ptp_ocp.c-6016-\tif (err)\n"
}

11/2 2026/09/14 07:28 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 33654
							output: 41
							thoughts: 23
						

Thoughts:
Here's my take on those thoughts, tailored for an expert audience:

**Diving into PTP Initialization and Driver Implementation**

Okay, so I need to get a handle on how these PTP components are interacting. I'm going to start with a deep dive into `ptp_ocp_init`. I'm especially interested in how it's setting up the communication channels, configuring the hardware clocks, and establishing the initial synchronization state. Are there any subtle race conditions I need to keep in mind, perhaps during early boot? Are the interrupt handlers set up correctly? Any issues around clock source selection? And then I'll move to the `ptp_ocp_driver`. I need to understand how it's interacting with the underlying hardware, how it's handling the PTP timestamps, and how it's exposed to the user space. What are the key data structures? Any potential bottlenecks in the interrupt handling or data transfer? I want to pay attention to the error handling within both of these modules, especially the cases where the initialization fails. I am specifically looking for issues around data races or how they are handled, memory allocation, and the overall robustness in edge cases. This is going to be about understanding the complete end-to-end PTP solution.



12/2 2026/09/14 07:28 tool read-file
0m Args:
{
  "File": "drivers/ptp/ptp_ocp.c",
  "FirstLine": 5990,
  "LineCount": 40
}

Results:
{
  "Contents": "5990:\t}\n5991:\t\n5992:\tstatic struct notifier_block ptp_ocp_i2c_notifier = {\n5993:\t\t.notifier_call = ptp_ocp_i2c_notifier_call,\n5994:\t};\n5995:\t\n5996:\tstatic int __init\n5997:\tptp_ocp_init(void)\n5998:\t{\n5999:\t\tconst char *what;\n6000:\t\tint err;\n6001:\t\n6002:\t\tptp_ocp_debugfs_init();\n6003:\t\n6004:\t\twhat = \"timecard class\";\n6005:\t\terr = class_register(\u0026timecard_class);\n6006:\t\tif (err)\n6007:\t\t\tgoto out;\n6008:\t\n6009:\t\twhat = \"i2c notifier\";\n6010:\t\terr = bus_register_notifier(\u0026i2c_bus_type, \u0026ptp_ocp_i2c_notifier);\n6011:\t\tif (err)\n6012:\t\t\tgoto out_notifier;\n6013:\t\n6014:\t\twhat = \"ptp_ocp driver\";\n6015:\t\terr = pci_register_driver(\u0026ptp_ocp_driver);\n6016:\t\tif (err)\n6017:\t\t\tgoto out_register;\n6018:\t\n6019:\t\treturn 0;\n6020:\t\n6021:\tout_register:\n6022:\t\tbus_unregister_notifier(\u0026i2c_bus_type, \u0026ptp_ocp_i2c_notifier);\n6023:\tout_notifier:\n6024:\t\tclass_unregister(\u0026timecard_class);\n6025:\tout:\n6026:\t\tptp_ocp_debugfs_fini();\n6027:\t\tpr_err(KBUILD_MODNAME \": failed to register %s: %d\\n\", what, err);\n6028:\t\treturn err;\n6029:\t}\n"
}

13/2 2026/09/14 07:28 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 34546
							output: 41
							thoughts: 664
						

Thoughts:
**Analyzing the ADVA x1 TAP Board Patch**

Okay, so I'm looking at this `ptp_ocp_driver`. It's defined as a standard `struct pci_driver`, nothing groundbreaking there. The interesting bit, as always, is how it probes. It's triggered by specific PCI device IDs, right? And I see the table: `ptp_ocp_pcidev_id`.

Right, the patch is specifically targeting the ADVA x1 TAP boards (`PCI_DEVICE_DATA(ADVA, TIMECARD_X1, &ocp_adva_x1_resource)`). These are actual PCIe cards with the PCI ID 0xad5a:0x0410. The patch's core functions relate to CPLD flash updates and reading its status on *those* boards: `adva_x1_cpld_flash`, `adva_x1_cpld_read_id`, plus the sysfs attribute `cpld_status`. It's adding devlink support for flashing and information retrieval, including `ptp_ocp_devlink_flash_update`, `ptp_ocp_devlink_info_get`, and `ptp_ocp_sync_work`. There are also some error fixes in `ptp_ocp_probe` / `ptp_ocp_detach`, as well as mutex initialization.

Now, hold on... that `ptp_ocp_i2c_notifier_call` function. It *registers* an I2C adapter. I wonder, does it match an I2C adapter just for these TAP boards, or could it potentially be invoked on other I2C bus events? I should check the code for that. Better look at the lines 5946-5989 of `ptp_ocp.c` to see if there's any broad bus matching going on. I'll need to assess the scope of this I2C notifier.



14/2 2026/09/14 07:28 tool read-file
0m Args:
{
  "File": "drivers/ptp/ptp_ocp.c",
  "FirstLine": 5945,
  "LineCount": 45
}

Results:
{
  "Contents": "5945:\tstatic int\n5946:\tptp_ocp_i2c_notifier_call(struct notifier_block *nb,\n5947:\t\t\t\t  unsigned long action, void *data)\n5948:\t{\n5949:\t\tstruct device *dev, *child = data;\n5950:\t\tstruct i2c_adapter *adap;\n5951:\t\tstruct ptp_ocp *bp;\n5952:\t\tbool add;\n5953:\t\n5954:\t\tswitch (action) {\n5955:\t\tcase BUS_NOTIFY_ADD_DEVICE:\n5956:\t\tcase BUS_NOTIFY_DEL_DEVICE:\n5957:\t\t\tadd = action == BUS_NOTIFY_ADD_DEVICE;\n5958:\t\t\tbreak;\n5959:\t\tdefault:\n5960:\t\t\treturn 0;\n5961:\t\t}\n5962:\t\n5963:\t\tif (!i2c_verify_adapter(child))\n5964:\t\t\treturn 0;\n5965:\t\n5966:\t\tdev = child;\n5967:\t\twhile ((dev = dev-\u003eparent))\n5968:\t\t\tif (dev-\u003edriver \u0026\u0026 !strcmp(dev-\u003edriver-\u003ename, KBUILD_MODNAME))\n5969:\t\t\t\tgoto found;\n5970:\t\treturn 0;\n5971:\t\n5972:\tfound:\n5973:\t\tbp = dev_get_drvdata(dev);\n5974:\t\tadap = i2c_verify_adapter(child);\n5975:\t\tif (add) {\n5976:\t\t\tptp_ocp_symlink(bp, child, \"i2c\");\n5977:\t\t\t/* Only the card's own controller: a mux child added on it\n5978:\t\t\t * reaches ptp_ocp through the same walk.  Numbers are\n5979:\t\t\t * recycled, so the claim re-checks the parent itself.\n5980:\t\t\t */\n5981:\t\t\tif (bp-\u003ei2c_ctrl \u0026\u0026 adap-\u003edev.parent == \u0026bp-\u003ei2c_ctrl-\u003edev)\n5982:\t\t\t\tWRITE_ONCE(bp-\u003ecpld_i2c_adap_nr, adap-\u003enr);\n5983:\t\t} else {\n5984:\t\t\t/* invalidate before free, but only for the cached adapter */\n5985:\t\t\tif (READ_ONCE(bp-\u003ecpld_i2c_adap_nr) == adap-\u003enr)\n5986:\t\t\t\tWRITE_ONCE(bp-\u003ecpld_i2c_adap_nr, -1);\n5987:\t\t\tsysfs_remove_link(\u0026bp-\u003edev.kobj, \"i2c\");\n5988:\t\t}\n5989:\t\treturn 0;\n"
}

15/2 2026/09/14 07:28 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 36403
							output: 21
							thoughts: 229
						

Thoughts:
**Investigating the `ptp_ocp_i2c_notifier_call` Function**

Okay, let's break down this `ptp_ocp_i2c_notifier_call` function. My first thought is to understand its role in the system. The comment tells me this function is triggered when an I2C device is added or removed. Interesting. So, it's a hotplug notification.

Now, let's look at the code block. It starts by traversing the device hierarchy upwards from the child device (`dev = child`) and looks for a parent that has a driver named "ptp_ocp." The loop makes perfect sense – we're tracing back the device's ancestry to see if it's connected to something relevant. The `strcmp` is how it's checking the name, makes perfect sense, so the code jumps to `found` if that driver exists. And if it doesn't find a `ptp_ocp` driver attached, the function immediately returns 0.

Wait a second… If no `ptp_ocp` parent is found, it returns without doing anything? That seems… restrictive. It seems to suggest this whole function is only relevant if a device is connected to a `ptp_ocp` device. And I'm pretty sure that `ptp_ocp` is only instantiated if it probes with a PCI ID in that `ptp_ocp_pcidev_id` array.

So here's the question: in a QEMU environment, is there *any* virtual bus or emulation that *can* actually instantiate the `ptp_ocp` device? I'm drawing a blank right now. It seems like a potential blind spot in my setup. I need to be sure there's even a way to trigger this notification in a controlled environment. I think I'll look around in the kernel tree for other instances where `ptp_ocp` devices are created. It might offer some insight into possible configurations or alternate instantiation methods.



16/2 2026/09/14 07:28 tool grepper
0m Args:
{
  "Expression": "\"ptp_ocp\""
}

Results:
{
  "Output": "drivers/ptp/ptp_ocp.c=2269=__ptp_ocp_get_mem(struct ptp_ocp *bp, resource_size_t start, int size)\ndrivers/ptp/ptp_ocp.c-2270-{\ndrivers/ptp/ptp_ocp.c:2271:\tstruct resource res = DEFINE_RES_MEM_NAMED(start, size, \"ptp_ocp\");\ndrivers/ptp/ptp_ocp.c-2272-\n"
}

17/2 2026/09/14 07:28 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 37230
							output: 138
							thoughts: 962
						

Thoughts:
**Assessing the Fuzzing Potential of "ptp_ocp" Changes**

Okay, let's break this down. My initial scan didn't immediately reveal any red flags. But, I need to be precise.  "ptp_ocp" isn't binding anything specific in my mental model, so I went deeper.  I quickly referenced the PCI vendor IDs, the META, ADVA, CELESTICA, and OROLIA IDs, all of which are a non-emulated by QEMU and GCE, a common indicator of structurally unreachable hardware.

The decisive factor is the critical section 2 of the instructions. This section defines when a patch should *not* be fuzzed (WorthFuzzing=false). I meticulously read through the negative criteria, specifically focusing on "Structurally unreachable hardware." It clearly states that vendor-specific PCIe devices that aren't emulated in standard QEMU are excluded.  This directly hits home: specifically switches, SmartNICs, or GPU drivers. I know this very well!

Section 1 reinforces this. Reachability is paramount. If code can't execute in a standard virtualized environment, it's a no-go for fuzzing. No matter how clever or complex, if it's not *reachable*, it's not fuzzed.

Now, let's examine the patch content. I need to be exact. I'm seeing changes in:

1.  `Documentation/ABI/testing/sysfs-timecard`
2.  `Documentation/networking/devlink/index.rst`
3.  `Documentation/networking/devlink/ptp_ocp.rst`
4.  `MAINTAINERS`
5.  `drivers/ptp/ptp_ocp.c`

The documentation and `MAINTAINERS` changes are pure documentation. Not executable. That's a given. But `drivers/ptp/ptp_ocp.c` is the core of this matter.  This is the OpenCompute PTP clock PCIe driver.  My understanding is that this depends on specific, physical hardware, like the ADVA TimeCard X1 PCIe card. This is not something I can just spin up in QEMU or GCE.

Thus, without the *physical* PCIe device, the `ptp_ocp_probe()` function will not be called. No `ptp_ocp` instance is created, no devlink or sysfs device is registered. All the modified code in this driver, the new and old, is unreachable by syzkaller.

I confirm there were no touches to core or generic subsystem code. Just the driver and documentation.

Therefore, this patch series is fundamentally *structurally unreachable* in standard virtualized fuzzing environments (QEMU/GCE) because it requires that specific hardware.

Hence, it's a clear:

*   **WorthFuzzing: false**
*   **EnableConfigs: \[ ]**
*   **FocusSymbols: \[ ]**

The reasoning is simple and precise: the changes involve Documentation and `drivers/ptp/ptp_ocp.c`, a specialized PCIe timecard driver (Meta / ADVA TimeCard X1) that is not emulated in the standard virtualized QEMU/GCE environments. This makes the affected code inaccessible to syzkaller, which depends on standard virtualization. Let's call `set-results`.



18/2 2026/09/14 07:28 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch modifies documentation and the ptp_ocp driver (drivers/ptp/ptp_ocp.c), adding CPLD flash and status support for ADVA TimeCard X1 devices. This driver binds specifically to vendor-specific PCIe timecard hardware (such as ADVA TimeCard X1, Meta Timecard) that is not emulated in standard QEMU or GCE virtualization environments. As such, the driver cannot be probed and the modified code is structurally unreachable for syzkaller fuzzing.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch modifies documentation and the ptp_ocp driver (drivers/ptp/ptp_ocp.c), adding CPLD flash and status support for ADVA TimeCard X1 devices. This driver binds specifically to vendor-specific PCIe timecard hardware (such as ADVA TimeCard X1, Meta Timecard) that is not emulated in standard QEMU or GCE virtualization environments. As such, the driver cannot be probed and the modified code is structurally unreachable for syzkaller fuzzing.",
  "WorthFuzzing": false
}

19/1 2026/09/14 07:28 action If
0m Args:
{
  "WorthFuzzing": false
}

Results:
null

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)