| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/01 02:52 | flow | patch-triage |
1mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch adds PTP clock support to the Microchip Azurite DPLL driver (zl3073x). This is a vendor-specific hardware driver for I2C/SPI connected DPLL devices. Since the physical hardware is not emulated in standard QEMU/GCE environments, the driver's probe function will fail to read the expected hardware chip IDs, making the modified code paths unreachable during fuzzing.",
"WorthFuzzing": false
} |
| 1/1 | 2026/08/01 02:52 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 6423e50ecd001cde2831841cc9cc1924768c91a9\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Sat Aug 1 02:52:34 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/drivers/dpll/zl3073x/Kconfig b/drivers/dpll/zl3073x/Kconfig\nindex 5bbca14005813..da1731ace15f0 100644\n--- a/drivers/dpll/zl3073x/Kconfig\n+++ b/drivers/dpll/zl3073x/Kconfig\n@@ -2,7 +2,7 @@\n \n config ZL3073X\n \ttristate \"Microchip Azurite DPLL/PTP/SyncE devices\" if COMPILE_TEST\n-\tdepends on NET\n+\tdepends on NET \u0026\u0026 PTP_1588_CLOCK\n \tselect DPLL\n \tselect NET_DEVLINK\n \tselect REGMAP\n@@ -16,7 +16,7 @@ config ZL3073X\n \n config ZL3073X_I2C\n \ttristate \"I2C bus implementation for Microchip Azurite devices\"\n-\tdepends on I2C \u0026\u0026 NET\n+\tdepends on I2C \u0026\u0026 NET \u0026\u0026 PTP_1588_CLOCK\n \tselect REGMAP_I2C\n \tselect ZL3073X\n \thelp\n@@ -28,7 +28,7 @@ config ZL3073X_I2C\n \n config ZL3073X_SPI\n \ttristate \"SPI bus implementation for Microchip Azurite devices\"\n-\tdepends on NET \u0026\u0026 SPI\n+\tdepends on NET \u0026\u0026 SPI \u0026\u0026 PTP_1588_CLOCK\n \tselect REGMAP_SPI\n \tselect ZL3073X\n \thelp\ndiff --git a/drivers/dpll/zl3073x/chan.c b/drivers/dpll/zl3073x/chan.c\nindex 4ec2cf53dad46..53032e782fd4a 100644\n--- a/drivers/dpll/zl3073x/chan.c\n+++ b/drivers/dpll/zl3073x/chan.c\n@@ -3,6 +3,7 @@\n #include \u003clinux/cleanup.h\u003e\n #include \u003clinux/delay.h\u003e\n #include \u003clinux/dev_printk.h\u003e\n+#include \u003clinux/ptp_clock_kernel.h\u003e\n #include \u003clinux/string.h\u003e\n #include \u003clinux/types.h\u003e\n \n@@ -202,6 +203,11 @@ int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index)\n \t\tzl3073x_chan_refsel_state_get(chan),\n \t\tzl3073x_chan_refsel_ref_get(chan));\n \n+\trc = zl3073x_read_u16(zldev, ZL_REG_OUTPUT_STEP_TIME_MASK,\n+\t\t\t \u0026chan-\u003eout_step_time_mask);\n+\tif (rc)\n+\t\treturn rc;\n+\n \tguard(mutex)(\u0026zldev-\u003emultiop_lock);\n \n \t/* Read DPLL configuration from mailbox */\n@@ -234,6 +240,307 @@ const struct zl3073x_chan *zl3073x_chan_state_get(struct zl3073x_dev *zldev,\n \treturn \u0026zldev-\u003echan[index];\n }\n \n+/**\n+ * zl3073x_chan_tod_ready_wait - wait for ToD semaphore to clear\n+ * @zldev: pointer to zl3073x device\n+ * @ch: DPLL channel index\n+ *\n+ * Polls the ToD control register until the semaphore bit is cleared,\n+ * indicating the device has completed the previous ToD operation.\n+ *\n+ * Return: 0 on success, -EBUSY if semaphore not cleared, \u003c0 on error\n+ */\n+static int zl3073x_chan_tod_ready_wait(struct zl3073x_dev *zldev, u8 ch)\n+{\n+\tunsigned int timeout;\n+\tu8 tod_ctrl;\n+\tint rc;\n+\n+\trc = zl3073x_read_u8(zldev, ZL_REG_DPLL_TOD_CTRL(ch), \u0026tod_ctrl);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tswitch (FIELD_GET(ZL_DPLL_TOD_CTRL_CMD, tod_ctrl)) {\n+\tcase ZL_DPLL_TOD_CTRL_CMD_WR_NEXT_1HZ:\n+\t\ttimeout = ZL_POLL_TOD_WR_TIMEOUT_US;\n+\t\tbreak;\n+\tdefault:\n+\t\ttimeout = ZL_POLL_TOD_RD_TIMEOUT_US;\n+\t\tbreak;\n+\t}\n+\n+\trc = zl3073x_poll_zero_u8(zldev, ZL_REG_DPLL_TOD_CTRL(ch),\n+\t\t\t\t ZL_DPLL_TOD_CTRL_SEM, timeout);\n+\n+\treturn rc == -ETIMEDOUT ? -EBUSY : rc;\n+}\n+\n+/**\n+ * zl3073x_chan_tod_ctrl - issue ToD command\n+ * @zldev: pointer to zl3073x device\n+ * @ch: DPLL channel index\n+ * @cmd: ToD command to execute\n+ *\n+ * Writes the semaphore and command to dpll_tod_ctrl. The caller must\n+ * ensure the device is ready (semaphore clear) before calling and\n+ * must wait for completion if needed.\n+ *\n+ * Return: 0 on success, \u003c0 on error\n+ */\n+static int zl3073x_chan_tod_ctrl(struct zl3073x_dev *zldev, u8 ch, u8 cmd)\n+{\n+\treturn zl3073x_write_u8(zldev, ZL_REG_DPLL_TOD_CTRL(ch),\n+\t\t\t\tZL_DPLL_TOD_CTRL_SEM | cmd);\n+}\n+\n+/**\n+ * zl3073x_chan_tod_read - read ToD registers after issuing a command\n+ * @zldev: pointer to zl3073x device\n+ * @ch: DPLL channel index\n+ * @next_hz: if true, read predicted ToD at next 1 Hz; otherwise read current\n+ * @ts: timespec to store the result\n+ * @sts: optional system timestamp pair for cross-timestamping\n+ *\n+ * Context: Caller must serialize all zl3073x_chan_tod_* calls externally.\n+ * Return: 0 on success, \u003c0 on error\n+ */\n+int zl3073x_chan_tod_read(struct zl3073x_dev *zldev, u8 ch,\n+\t\t\t bool next_hz, struct timespec64 *ts,\n+\t\t\t struct ptp_system_timestamp *sts)\n+{\n+\tu32 nsec;\n+\tu64 sec;\n+\tu8 cmd;\n+\tint rc;\n+\n+\tif (next_hz)\n+\t\tcmd = ZL_DPLL_TOD_CTRL_CMD_RD_NEXT_1HZ;\n+\telse\n+\t\tcmd = ZL_DPLL_TOD_CTRL_CMD_RD_CURRENT;\n+\n+\t/* Wait for any previous ToD operation to complete */\n+\trc = zl3073x_chan_tod_ready_wait(zldev, ch);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tptp_read_system_prets(sts);\n+\trc = zl3073x_chan_tod_ctrl(zldev, ch, cmd);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = zl3073x_chan_tod_ready_wait(zldev, ch);\n+\tif (rc)\n+\t\treturn rc;\n+\tptp_read_system_postts(sts);\n+\n+\trc = zl3073x_read_u48(zldev, ZL_REG_DPLL_TOD_SEC(ch), \u0026sec);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\t/* HW nanoseconds are always in [0, NSEC_PER_SEC) range */\n+\trc = zl3073x_read_u32(zldev, ZL_REG_DPLL_TOD_NS(ch), \u0026nsec);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tts-\u003etv_sec = sec;\n+\tts-\u003etv_nsec = nsec;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * zl3073x_chan_tod_write - write ToD registers and trigger 1 Hz update\n+ * @zldev: pointer to zl3073x device\n+ * @ch: DPLL channel index\n+ * @ts: time to set\n+ *\n+ * Context: Caller must serialize all zl3073x_chan_tod_* calls externally.\n+ * Return: 0 on success, \u003c0 on error\n+ */\n+int zl3073x_chan_tod_write(struct zl3073x_dev *zldev, u8 ch,\n+\t\t\t struct timespec64 ts)\n+{\n+\tint rc;\n+\n+\t/* Wait for any previous ToD operation to complete */\n+\trc = zl3073x_chan_tod_ready_wait(zldev, ch);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = zl3073x_write_u48(zldev, ZL_REG_DPLL_TOD_SEC(ch), ts.tv_sec);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = zl3073x_write_u32(zldev, ZL_REG_DPLL_TOD_NS(ch), ts.tv_nsec);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\treturn zl3073x_chan_tod_ctrl(zldev, ch,\n+\t\t\t\t ZL_DPLL_TOD_CTRL_CMD_WR_NEXT_1HZ);\n+}\n+\n+/**\n+ * zl3073x_chan_tod_adjust - atomic ToD read-modify-write with rollover guard\n+ * @zldev: pointer to zl3073x device\n+ * @ch: DPLL channel index\n+ * @delta: time adjustment to apply\n+ *\n+ * Reads the next-Hz ToD and current ToD, then checks whether enough time\n+ * remains before the next 1 Hz rollover to safely complete the write.\n+ * If less than 20 ms remains, waits for the rollover and increments the\n+ * next-Hz seconds by one. Applies @delta and writes the result back.\n+ *\n+ * Context: Caller must serialize all zl3073x_chan_tod_* calls externally.\n+ * Return: 0 on success, \u003c0 on error\n+ */\n+int zl3073x_chan_tod_adjust(struct zl3073x_dev *zldev, u8 ch,\n+\t\t\t struct timespec64 delta)\n+{\n+\tstatic const long threshold_ns = 20 * NSEC_PER_MSEC;\n+\tstruct timespec64 ts_next, ts_cur, diff;\n+\tint rc;\n+\n+\t/* Read predicted ToD at next 1 Hz tick */\n+\trc = zl3073x_chan_tod_read(zldev, ch, true, \u0026ts_next, NULL);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\t/* Read current ToD to determine remaining margin */\n+\trc = zl3073x_chan_tod_read(zldev, ch, false, \u0026ts_cur, NULL);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\t/* If too close to (or past) the next rollover, wait it out */\n+\tdiff = timespec64_sub(ts_next, ts_cur);\n+\tif (diff.tv_sec \u003c 0 || (!diff.tv_sec \u0026\u0026 diff.tv_nsec \u003c threshold_ns)) {\n+\t\tif (!diff.tv_sec \u0026\u0026 diff.tv_nsec)\n+\t\t\tfsleep((unsigned long)diff.tv_nsec / NSEC_PER_USEC + 1);\n+\t\tts_next.tv_sec++;\n+\t}\n+\n+\t/* Apply delta to the next-Hz ToD */\n+\tts_next = timespec64_add(ts_next, delta);\n+\tif (!timespec64_valid(\u0026ts_next))\n+\t\treturn -EINVAL;\n+\n+\t/* Write adjusted ToD back and wait for completion */\n+\trc = zl3073x_chan_tod_write(zldev, ch, ts_next);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\treturn zl3073x_chan_tod_ready_wait(zldev, ch);\n+}\n+\n+/**\n+ * zl3073x_chan_df_offset_set - write delta frequency offset to hardware\n+ * @zldev: pointer to zl3073x device\n+ * @ch: DPLL channel index\n+ * @offset: frequency offset in 2^-48 steps\n+ *\n+ * Return: 0 on success, \u003c0 on error\n+ */\n+int zl3073x_chan_df_offset_set(struct zl3073x_dev *zldev, u8 ch, s64 offset)\n+{\n+\tint rc;\n+\n+\trc = zl3073x_write_u48(zldev, ZL_REG_DPLL_DF_OFFSET(ch), offset);\n+\tif (!rc)\n+\t\tzldev-\u003echan[ch].df_offset = offset;\n+\n+\treturn rc;\n+}\n+\n+/**\n+ * zl3073x_chan_tie_write - adjust DPLL phase using TIE write\n+ * @zldev: pointer to zl3073x device\n+ * @ch: DPLL channel index\n+ * @delta_ns: phase adjustment in nanoseconds (must be within +-1s)\n+ *\n+ * Converts nanoseconds to TIE units (0.01 ps) and writes TIE data\n+ * to the specified channel.\n+ *\n+ * Return: 0 on success, \u003c0 on error\n+ */\n+int zl3073x_chan_tie_write(struct zl3073x_dev *zldev, u8 ch, s64 delta_ns)\n+{\n+\ts64 tie_data;\n+\tint rc;\n+\n+\tguard(mutex)(\u0026zldev-\u003etie_lock);\n+\n+\t/* Wait for any previous TIE operation to complete */\n+\trc = zl3073x_poll_zero_u8(zldev, ZL_REG_DPLL_TIE_CTRL,\n+\t\t\t\t ZL_DPLL_TIE_CTRL_OP,\n+\t\t\t\t ZL_POLL_TIE_WR_TIMEOUT_US);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\t/* Convert ns to TIE units (0.01 ps = 10^-14 s) */\n+\ttie_data = delta_ns * 100000LL;\n+\n+\trc = zl3073x_write_u48(zldev, ZL_REG_DPLL_TIE_DATA(ch), tie_data);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = zl3073x_write_u8(zldev, ZL_REG_DPLL_TIE_CTRL_MASK, BIT(ch));\n+\tif (rc)\n+\t\treturn rc;\n+\n+\treturn zl3073x_write_u8(zldev, ZL_REG_DPLL_TIE_CTRL,\n+\t\t\t\tZL_DPLL_TIE_CTRL_OP_WR);\n+}\n+\n+/**\n+ * zl3073x_chan_phase_step - execute one output phase step operation\n+ * @zldev: pointer to zl3073x device\n+ * @ch: DPLL channel index\n+ * @out_mask: bitmask of outputs to step\n+ * @step_cycles: phase step in synthesizer clock cycles\n+ * @tod_step: also step the ToD counter\n+ *\n+ * All masked outputs must use synthesizers of the same frequency since\n+ * the step value is in synthesizer clock cycles.\n+ *\n+ * Return: 0 on success, \u003c0 on error\n+ */\n+int zl3073x_chan_phase_step(struct zl3073x_dev *zldev, u8 ch,\n+\t\t\t u16 out_mask, s32 step_cycles,\n+\t\t\t bool tod_step)\n+{\n+\tu8 ctrl;\n+\tint rc;\n+\n+\tguard(mutex)(\u0026zldev-\u003ephase_step_lock);\n+\n+\t/* Wait for any previous phase step operation to complete */\n+\trc = zl3073x_poll_zero_u8(zldev, ZL_REG_OUTPUT_PHASE_STEP_CTRL,\n+\t\t\t\t ZL_OUTPUT_PHASE_STEP_CTRL_OP,\n+\t\t\t\t ZL_POLL_PHASE_STEP_TIMEOUT_US);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_PHASE_STEP_DATA,\n+\t\t\t step_cycles);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = zl3073x_write_u16(zldev, ZL_REG_OUTPUT_PHASE_STEP_MASK, out_mask);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = zl3073x_write_u8(zldev, ZL_REG_OUTPUT_PHASE_STEP_NUMBER, 1);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tctrl = FIELD_PREP(ZL_OUTPUT_PHASE_STEP_CTRL_DPLL, ch) |\n+\t FIELD_PREP(ZL_OUTPUT_PHASE_STEP_CTRL_OP,\n+\t\t\t ZL_OUTPUT_PHASE_STEP_CTRL_OP_WRITE);\n+\tif (tod_step)\n+\t\tctrl |= ZL_OUTPUT_PHASE_STEP_CTRL_TOD_STEP;\n+\n+\treturn zl3073x_write_u8(zldev, ZL_REG_OUTPUT_PHASE_STEP_CTRL, ctrl);\n+}\n+\n /**\n * zl3073x_chan_state_set - commit DPLL channel state changes to hardware\n * @zldev: pointer to zl3073x_dev structure\ndiff --git a/drivers/dpll/zl3073x/chan.h b/drivers/dpll/zl3073x/chan.h\nindex dc9c6d95bdee7..9dc5a3f1991f1 100644\n--- a/drivers/dpll/zl3073x/chan.h\n+++ b/drivers/dpll/zl3073x/chan.h\n@@ -5,10 +5,12 @@\n \n #include \u003clinux/bitfield.h\u003e\n #include \u003clinux/stddef.h\u003e\n+#include \u003clinux/time64.h\u003e\n #include \u003clinux/types.h\u003e\n \n #include \"regs.h\"\n \n+struct ptp_system_timestamp;\n struct zl3073x_dev;\n \n /**\n@@ -16,6 +18,7 @@ struct zl3073x_dev;\n * @ctrl: DPLL control register value\n * @mode_refsel: mode and reference selection register value\n * @ref_prio: reference priority registers (4 bits per ref, P/N packed)\n+ * @out_step_time_mask: output step-time mask\n * @mon_status: monitor status register value\n * @refsel_status: reference selection status register value\n * @df_offset: frequency offset vs tracked reference in 2^-48 steps\n@@ -26,6 +29,9 @@ struct zl3073x_chan {\n \t\tu8\tmode_refsel;\n \t\tu8\tref_prio[ZL3073X_NUM_REFS / 2];\n \t);\n+\tstruct_group(inv, /* Invariants */\n+\t\tu16\tout_step_time_mask;\n+\t);\n \tstruct_group(stat,\n \t\tu8\tmon_status;\n \t\tu8\trefsel_status;\n@@ -42,6 +48,20 @@ int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index,\n int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index);\n int zl3073x_chan_nco_mode_set(struct zl3073x_dev *zldev, u8 index);\n \n+int zl3073x_chan_tod_read(struct zl3073x_dev *zldev, u8 ch,\n+\t\t\t bool next_hz, struct timespec64 *ts,\n+\t\t\t struct ptp_system_timestamp *sts);\n+int zl3073x_chan_tod_write(struct zl3073x_dev *zldev, u8 ch,\n+\t\t\t struct timespec64 ts);\n+int zl3073x_chan_tod_adjust(struct zl3073x_dev *zldev, u8 ch,\n+\t\t\t struct timespec64 delta);\n+int zl3073x_chan_phase_step(struct zl3073x_dev *zldev, u8 ch,\n+\t\t\t u16 out_mask, s32 step_cycles, bool tod_step);\n+\n+int zl3073x_chan_df_offset_set(struct zl3073x_dev *zldev, u8 ch, s64 offset);\n+\n+int zl3073x_chan_tie_write(struct zl3073x_dev *zldev, u8 ch, s64 delta_ns);\n+\n /**\n * zl3073x_chan_df_offset_get - get cached df_offset vs tracked reference\n * @chan: pointer to channel state\n@@ -200,6 +220,21 @@ static inline bool zl3073x_chan_mode_is_reflock(const struct zl3073x_chan *chan)\n \treturn zl3073x_chan_mode_get(chan) == ZL_DPLL_MODE_REFSEL_MODE_REFLOCK;\n }\n \n+/**\n+ * zl3073x_chan_mode_supports_tie - check if channel mode supports TIE write\n+ * @chan: pointer to channel state\n+ *\n+ * TIE write is supported in AUTO and REFLOCK modes regardless of lock state.\n+ *\n+ * Return: true if TIE write is supported, false otherwise\n+ */\n+static inline bool\n+zl3073x_chan_mode_supports_tie(const struct zl3073x_chan *chan)\n+{\n+\treturn zl3073x_chan_mode_is_auto(chan) ||\n+\t\tzl3073x_chan_mode_is_reflock(chan);\n+}\n+\n /**\n * zl3073x_chan_is_ho_ready - check if holdover is ready\n * @chan: pointer to channel state\n@@ -233,4 +268,17 @@ static inline u8 zl3073x_chan_refsel_ref_get(const struct zl3073x_chan *chan)\n \treturn FIELD_GET(ZL_DPLL_REFSEL_STATUS_REFSEL, chan-\u003erefsel_status);\n }\n \n+/**\n+ * zl3073x_chan_is_out_stepped - check if output is in step-time mask\n+ * @chan: pointer to channel state\n+ * @out: output index\n+ *\n+ * Return: true if output is affected by step-time operations\n+ */\n+static inline bool\n+zl3073x_chan_is_out_stepped(const struct zl3073x_chan *chan, u8 out)\n+{\n+\treturn !!(chan-\u003eout_step_time_mask \u0026 BIT(out));\n+}\n+\n #endif /* _ZL3073X_CHAN_H */\ndiff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c\nindex 7f5afaaae6342..b2f9fc97fd1b6 100644\n--- a/drivers/dpll/zl3073x/core.c\n+++ b/drivers/dpll/zl3073x/core.c\n@@ -1033,6 +1033,14 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev)\n \t * and/or polls are required to be done atomically.\n \t */\n \trc = devm_mutex_init(zldev-\u003edev, \u0026zldev-\u003emultiop_lock);\n+\tif (rc)\n+\t\treturn dev_err_probe(zldev-\u003edev, rc,\n+\t\t\t\t \"Failed to initialize mutex\\n\");\n+\trc = devm_mutex_init(zldev-\u003edev, \u0026zldev-\u003ephase_step_lock);\n+\tif (rc)\n+\t\treturn dev_err_probe(zldev-\u003edev, rc,\n+\t\t\t\t \"Failed to initialize mutex\\n\");\n+\trc = devm_mutex_init(zldev-\u003edev, \u0026zldev-\u003etie_lock);\n \tif (rc)\n \t\treturn dev_err_probe(zldev-\u003edev, rc,\n \t\t\t\t \"Failed to initialize mutex\\n\");\ndiff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h\nindex 78dc208f3eea2..2dc836d0c8f6b 100644\n--- a/drivers/dpll/zl3073x/core.h\n+++ b/drivers/dpll/zl3073x/core.h\n@@ -26,6 +26,10 @@ struct zl3073x_dpll;\n #define ZL_POLL_HWREG_TIMEOUT_US\t(50 * USEC_PER_MSEC)\n #define ZL_POLL_MB_TIMEOUT_US\t\t(30 * USEC_PER_MSEC)\n #define ZL_POLL_PHASE_ERR_TIMEOUT_US\t(50 * USEC_PER_MSEC)\n+#define ZL_POLL_PHASE_STEP_TIMEOUT_US\t(3000 * USEC_PER_MSEC)\n+#define ZL_POLL_TIE_WR_TIMEOUT_US\t(1000 * USEC_PER_MSEC)\n+#define ZL_POLL_TOD_RD_TIMEOUT_US\t(30 * USEC_PER_MSEC)\n+#define ZL_POLL_TOD_WR_TIMEOUT_US\t(1000 * USEC_PER_MSEC)\n \n enum zl3073x_flags {\n \tZL3073X_FLAG_REF_PHASE_COMP_32_BIT,\n@@ -55,6 +59,8 @@ struct zl3073x_chip_info {\n * @regmap: regmap to access device registers\n * @info: detected chip info\n * @multiop_lock: to serialize multiple register operations\n+ * @tie_lock: to serialize TIE write operations\n+ * @phase_step_lock: to serialize output phase step operations\n * @ref: array of input references' invariants\n * @out: array of outs' invariants\n * @synth: array of synths' invariants\n@@ -71,6 +77,8 @@ struct zl3073x_dev {\n \tstruct regmap\t\t\t*regmap;\n \tconst struct zl3073x_chip_info\t*info;\n \tstruct mutex\t\t\tmultiop_lock;\n+\tstruct mutex\t\t\ttie_lock;\n+\tstruct mutex\t\t\tphase_step_lock;\n \n \t/* Invariants */\n \tstruct zl3073x_ref\tref[ZL3073X_NUM_REFS];\ndiff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c\nindex 0488ae6ac486c..320852dfc58cb 100644\n--- a/drivers/dpll/zl3073x/dpll.c\n+++ b/drivers/dpll/zl3073x/dpll.c\n@@ -2,6 +2,7 @@\n \n #include \u003clinux/bits.h\u003e\n #include \u003clinux/bitfield.h\u003e\n+#include \u003clinux/cleanup.h\u003e\n #include \u003clinux/bug.h\u003e\n #include \u003clinux/container_of.h\u003e\n #include \u003clinux/dev_printk.h\u003e\n@@ -13,6 +14,7 @@\n #include \u003clinux/netlink.h\u003e\n #include \u003clinux/platform_device.h\u003e\n #include \u003clinux/property.h\u003e\n+#include \u003clinux/ptp_clock_kernel.h\u003e\n #include \u003clinux/slab.h\u003e\n #include \u003clinux/sprintf.h\u003e\n \n@@ -41,6 +43,7 @@\n * @phase_offset: last saved pin phase offset\n * @freq_offset: last saved fractional frequency offset\n * @measured_freq: last saved measured frequency\n+ * @perout_idx: PTP perout channel index, or -1 if not perout-eligible\n */\n struct zl3073x_dpll_pin {\n \tstruct list_head\tlist;\n@@ -58,6 +61,7 @@ struct zl3073x_dpll_pin {\n \ts64\t\t\tphase_offset;\n \ts64\t\t\tfreq_offset;\n \tu32\t\t\tmeasured_freq;\n+\ts8\t\t\tperout_idx;\n };\n \n /*\n@@ -935,21 +939,17 @@ zl3073x_dpll_output_pin_frequency_get(const struct dpll_pin *dpll_pin,\n }\n \n static int\n-zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin,\n-\t\t\t\t void *pin_priv,\n-\t\t\t\t const struct dpll_device *dpll,\n-\t\t\t\t void *dpll_priv, u64 frequency,\n-\t\t\t\t struct netlink_ext_ack *extack)\n+__zl3073x_dpll_output_pin_frequency_set(struct zl3073x_dpll *zldpll,\n+\t\t\t\t\tstruct zl3073x_dpll_pin *pin,\n+\t\t\t\t\tu64 frequency)\n {\n-\tstruct zl3073x_dpll *zldpll = dpll_priv;\n \tstruct zl3073x_dev *zldev = zldpll-\u003edev;\n-\tstruct zl3073x_dpll_pin *pin = pin_priv;\n \tconst struct zl3073x_synth *synth;\n \tu32 new_div, synth_freq;\n \tstruct zl3073x_out out;\n \tu8 out_id;\n \n-\tguard(mutex)(\u0026zldpll-\u003elock);\n+\tlockdep_assert_held(\u0026zldpll-\u003elock);\n \n \tout_id = zl3073x_output_pin_out_get(pin-\u003eid);\n \tout = *zl3073x_out_state_get(zldev, out_id);\n@@ -1010,6 +1010,21 @@ zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin,\n \treturn zl3073x_out_state_set(zldev, out_id, \u0026out);\n }\n \n+static int\n+zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin,\n+\t\t\t\t void *pin_priv,\n+\t\t\t\t const struct dpll_device *dpll,\n+\t\t\t\t void *dpll_priv, u64 frequency,\n+\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct zl3073x_dpll *zldpll = dpll_priv;\n+\n+\tguard(mutex)(\u0026zldpll-\u003elock);\n+\n+\treturn __zl3073x_dpll_output_pin_frequency_set(zldpll, pin_priv,\n+\t\t\t\t\t\t frequency);\n+}\n+\n static int\n zl3073x_dpll_output_pin_phase_adjust_get(const struct dpll_pin *dpll_pin,\n \t\t\t\t\t void *pin_priv,\n@@ -1607,6 +1622,7 @@ zl3073x_dpll_pin_alloc(struct zl3073x_dpll *zldpll, enum dpll_pin_direction dir,\n \tpin-\u003edpll = zldpll;\n \tpin-\u003edir = dir;\n \tpin-\u003eid = id;\n+\tpin-\u003eperout_idx = -1;\n \n \treturn pin;\n }\n@@ -1625,6 +1641,44 @@ zl3073x_dpll_pin_free(struct zl3073x_dpll_pin *pin)\n \tkfree(pin);\n }\n \n+/**\n+ * zl3073x_dpll_pin_is_perout - check if output pin is perout-eligible\n+ * @pin: DPLL pin to check\n+ * @props: pin properties (already fetched by caller)\n+ *\n+ * An output pin is eligible for PTP periodic output if it is single-ended\n+ * (not differential) and supports 1 Hz in its frequency list.\n+ *\n+ * Return: true if eligible, false otherwise\n+ */\n+static bool\n+zl3073x_dpll_pin_is_perout(struct zl3073x_dpll_pin *pin,\n+\t\t\t const struct zl3073x_pin_props *props)\n+{\n+\tstruct zl3073x_dpll *zldpll = pin-\u003edpll;\n+\tconst struct zl3073x_chan *chan;\n+\tu8 out_id;\n+\tint i;\n+\n+\tif (zl3073x_dpll_is_input_pin(pin))\n+\t\treturn false;\n+\n+\tout_id = zl3073x_output_pin_out_get(pin-\u003eid);\n+\tif (zl3073x_dev_out_is_diff(zldpll-\u003edev, out_id))\n+\t\treturn false;\n+\n+\tchan = zl3073x_chan_state_get(zldpll-\u003edev, zldpll-\u003eid);\n+\tif (!zl3073x_chan_is_out_stepped(chan, out_id))\n+\t\treturn false;\n+\n+\tfor (i = 0; i \u003c props-\u003edpll_props.freq_supported_num; i++) {\n+\t\tif (props-\u003edpll_props.freq_supported[i].min == 1)\n+\t\t\treturn true;\n+\t}\n+\n+\treturn false;\n+}\n+\n /**\n * zl3073x_dpll_pin_register - register DPLL pin\n * @pin: pointer to DPLL pin\n@@ -1666,6 +1720,8 @@ zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index)\n \t\tif (pin-\u003eprio == ZL_DPLL_REF_PRIO_NONE)\n \t\t\t/* Clamp prio to max value */\n \t\t\tpin-\u003eprio = ZL_DPLL_REF_PRIO_MAX;\n+\t} else if (zl3073x_dpll_pin_is_perout(pin, props)) {\n+\t\tpin-\u003eperout_idx = zldpll-\u003eptp_info.n_per_out++;\n \t}\n \n \t/* Create or get existing DPLL pin */\n@@ -1896,6 +1952,8 @@ zl3073x_dpll_pins_register(struct zl3073x_dpll *zldpll)\n \tu8 id, index;\n \tint rc;\n \n+\tzldpll-\u003eptp_info.n_per_out = 0;\n+\n \t/* Process input pins */\n \tfor (index = 0; index \u003c ZL3073X_NUM_PINS; index++) {\n \t\t/* First input pins and then output pins */\n@@ -2275,45 +2333,447 @@ zl3073x_dpll_init_fine_phase_adjust(struct zl3073x_dev *zldev)\n \treturn zl3073x_write_u8(zldev, ZL_REG_SYNTH_PHASE_SHIFT_CTRL, 0x01);\n }\n \n+/* Maximum frequency adjustment: +-1% of nominal in ppb */\n+#define ZL3073X_DPLL_PTP_MAX_ADJ\t10000000\n+\n /**\n- * zl3073x_dpll_alloc - allocate DPLL device\n- * @zldev: pointer to zl3073x device\n- * @ch: DPLL channel number\n+ * zl3073x_dpll_ptp_gettimex64 - read current time from ToD counters\n+ * @info: PTP clock info\n+ * @ts: timespec to store current time\n+ * @sts: optional system timestamp pair for cross-timestamping\n *\n- * Allocates DPLL device structure for given DPLL channel.\n+ * Return: 0 on success, \u003c0 on error\n+ */\n+static int zl3073x_dpll_ptp_gettimex64(struct ptp_clock_info *info,\n+\t\t\t\t struct timespec64 *ts,\n+\t\t\t\t struct ptp_system_timestamp *sts)\n+{\n+\tstruct zl3073x_dpll *zldpll = container_of(info, struct zl3073x_dpll,\n+\t\t\t\t\t\t ptp_info);\n+\n+\tguard(mutex)(\u0026zldpll-\u003elock);\n+\n+\treturn zl3073x_chan_tod_read(zldpll-\u003edev, zldpll-\u003eid, false, ts, sts);\n+}\n+\n+/**\n+ * zl3073x_dpll_ptp_settime64 - set ToD counters to given time\n+ * @info: PTP clock info\n+ * @ts: timespec with time to set\n *\n- * Return: pointer to DPLL device on success, error pointer on error\n+ * Return: 0 on success, \u003c0 on error\n */\n-struct zl3073x_dpll *\n-zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch)\n+static int zl3073x_dpll_ptp_settime64(struct ptp_clock_info *info,\n+\t\t\t\t const struct timespec64 *ts)\n {\n-\tstruct zl3073x_dpll *zldpll;\n+\tstruct zl3073x_dpll *zldpll = container_of(info, struct zl3073x_dpll,\n+\t\t\t\t\t\t ptp_info);\n \n-\tzldpll = kzalloc_obj(*zldpll);\n-\tif (!zldpll)\n-\t\treturn ERR_PTR(-ENOMEM);\n+\tguard(mutex)(\u0026zldpll-\u003elock);\n \n-\tzldpll-\u003edev = zldev;\n-\tzldpll-\u003eid = ch;\n-\tmutex_init(\u0026zldpll-\u003elock);\n-\tINIT_LIST_HEAD(\u0026zldpll-\u003epins);\n+\treturn zl3073x_chan_tod_write(zldpll-\u003edev, zldpll-\u003eid, *ts);\n+}\n \n-\treturn zldpll;\n+/**\n+ * zl3073x_dpll_ptp_adjtime_phase_step - adjust sub-second time via phase step\n+ * @zldpll: DPLL channel\n+ * @delta: time adjustment in nanoseconds (must be within (-NSEC_PER_SEC,\n+ * NSEC_PER_SEC))\n+ *\n+ * Uses the output phase step mechanism with tod_step=1 to adjust both\n+ * the output clock phase and the ToD counter simultaneously. This keeps\n+ * outputs and ToD coherent. Only valid for NCO.\n+ *\n+ * Outputs are grouped by synthesizer since the phase step value is in\n+ * synthesizer clock cycles. The first synth group with enabled outputs\n+ * uses tod_step to adjust both outputs and the ToD counter. Remaining\n+ * groups step outputs only. If no synth has enabled outputs, the ToD\n+ * counter is stepped alone using an empty output mask (the FW uses the\n+ * lowest-ID synth's period for the conversion).\n+ *\n+ * Return: 0 on success, -EOPNOTSUPP if no synths available, \u003c0 on error\n+ */\n+static int zl3073x_dpll_ptp_adjtime_phase_step(struct zl3073x_dpll *zldpll,\n+\t\t\t\t\t s64 delta)\n+{\n+\tu16 synth_mask[ZL3073X_NUM_SYNTHS] = {};\n+\tstruct zl3073x_dev *zldev = zldpll-\u003edev;\n+\tconst struct zl3073x_synth *synth;\n+\tconst struct zl3073x_chan *chan;\n+\tstruct zl3073x_dpll_pin *pin;\n+\tu32 first_synth_freq = 0;\n+\tbool tod_stepped = false;\n+\ts32 step_cycles;\n+\tu32 synth_freq;\n+\tint rc;\n+\tu8 i;\n+\n+\tchan = zl3073x_chan_state_get(zldev, zldpll-\u003eid);\n+\n+\t/* Build per-synth output masks from registered output pins */\n+\tlist_for_each_entry(pin, \u0026zldpll-\u003epins, list) {\n+\t\tu8 out_id, synth_id;\n+\n+\t\tif (zl3073x_dpll_is_input_pin(pin))\n+\t\t\tcontinue;\n+\n+\t\tout_id = zl3073x_output_pin_out_get(pin-\u003eid);\n+\n+\t\tif (!zl3073x_chan_is_out_stepped(chan, out_id))\n+\t\t\tcontinue;\n+\n+\t\tsynth_id = zl3073x_dev_out_synth_get(zldev, out_id);\n+\t\tif (synth_id \u003e= ZL3073X_NUM_SYNTHS) {\n+\t\t\tdev_warn(zldev-\u003edev, \"Unexpected synth id for OUT%u\\n\",\n+\t\t\t\t out_id);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tsynth_mask[synth_id] |= BIT(out_id);\n+\t}\n+\n+\t/* Process each synth group */\n+\tfor (i = 0; i \u003c ZL3073X_NUM_SYNTHS; i++) {\n+\t\tsynth = zl3073x_synth_state_get(zldev, i);\n+\t\tif (!zl3073x_synth_is_enabled(synth) ||\n+\t\t zl3073x_synth_dpll_get(synth) != zldpll-\u003eid)\n+\t\t\tcontinue;\n+\n+\t\tsynth_freq = zl3073x_synth_freq_get(synth);\n+\n+\t\t/* Remember lowest-ID synth freq for ToD-only fallback */\n+\t\tif (!first_synth_freq)\n+\t\t\tfirst_synth_freq = synth_freq;\n+\n+\t\tif (!synth_mask[i])\n+\t\t\tcontinue;\n+\n+\t\t/* Safe for s32: max synth freq is 750 MHz */\n+\t\tstep_cycles = div_s64(delta * synth_freq, NSEC_PER_SEC);\n+\n+\t\trc = zl3073x_chan_phase_step(zldev, zldpll-\u003eid,\n+\t\t\t\t\t synth_mask[i], step_cycles,\n+\t\t\t\t\t !tod_stepped);\n+\t\tif (rc) {\n+\t\t\tif (tod_stepped) {\n+\t\t\t\tdev_warn(zldev-\u003edev,\n+\t\t\t\t\t \"Partial phase step failure\\n\");\n+\t\t\t\treturn 0;\n+\t\t\t}\n+\t\t\treturn rc;\n+\t\t}\n+\t\ttod_stepped = true;\n+\t}\n+\n+\tif (!first_synth_freq)\n+\t\treturn -EOPNOTSUPP;\n+\n+\t/* No enabled outputs found; step ToD counter only using the\n+\t * lowest-ID synth's period (empty output mask).\n+\t */\n+\tif (!tod_stepped) {\n+\t\tstep_cycles = div_s64(delta * first_synth_freq, NSEC_PER_SEC);\n+\t\treturn zl3073x_chan_phase_step(zldev, zldpll-\u003eid, 0,\n+\t\t\t\t\t step_cycles, true);\n+\t}\n+\n+\treturn 0;\n }\n \n /**\n- * zl3073x_dpll_free - free DPLL device\n- * @zldpll: pointer to zl3073x_dpll structure\n+ * zl3073x_dpll_ptp_adjtime - adjust PTP clock time\n+ * @info: PTP clock info\n+ * @delta: time adjustment in nanoseconds\n *\n- * Deallocates given DPLL device previously allocated by @zl3073x_dpll_alloc.\n+ * For NCO, large deltas (\u003e= 1 second) are split into a ToD\n+ * read-modify-write for the seconds part and an output phase step for\n+ * the sub-second remainder. Sub-second deltas use phase step directly,\n+ * falling back to ToD read-modify-write if phase step is unavailable.\n+ * In AUTO/REFLOCK modes, large deltas are split into ToD\n+ * read-modify-write for seconds and TIE write for the sub-second\n+ * remainder. Sub-second deltas use TIE write directly.\n+ *\n+ * Return: 0 on success, \u003c0 on error\n */\n-void\n-zl3073x_dpll_free(struct zl3073x_dpll *zldpll)\n+static int zl3073x_dpll_ptp_adjtime(struct ptp_clock_info *info, s64 delta)\n {\n-\tWARN(zldpll-\u003edpll_dev, \"DPLL device is still registered\\n\");\n+\tstruct zl3073x_dpll *zldpll = container_of(info, struct zl3073x_dpll,\n+\t\t\t\t\t\t ptp_info);\n+\tstruct zl3073x_dev *zldev = zldpll-\u003edev;\n+\tconst struct zl3073x_chan *chan;\n+\tbool sec_adjusted = false;\n+\tstruct timespec64 ts;\n+\tint rc;\n \n-\tmutex_destroy(\u0026zldpll-\u003elock);\n-\tkfree(zldpll);\n+\tif (!delta)\n+\t\treturn 0;\n+\n+\tguard(mutex)(\u0026zldpll-\u003elock);\n+\n+\t/* Modes without phase step or TIE use plain ToD adjust */\n+\tchan = zl3073x_chan_state_get(zldev, zldpll-\u003eid);\n+\tif (!zl3073x_chan_mode_is_nco(chan) \u0026\u0026\n+\t !zl3073x_chan_mode_supports_tie(chan))\n+\t\treturn zl3073x_chan_tod_adjust(zldev, zldpll-\u003eid,\n+\t\t\t\t\t ns_to_timespec64(delta));\n+\n+\t/* Split off seconds via ToD read-modify-write so the sub-second\n+\t * remainder can be applied through the output-coherent mechanism\n+\t * (phase step or TIE write).\n+\t */\n+\tif (abs(delta) \u003e= NSEC_PER_SEC) {\n+\t\ts32 remainder;\n+\n+\t\tts.tv_sec = div_s64_rem(delta, NSEC_PER_SEC, \u0026remainder);\n+\t\tts.tv_nsec = 0;\n+\t\tdelta = remainder;\n+\n+\t\trc = zl3073x_chan_tod_adjust(zldev, zldpll-\u003eid, ts);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\n+\t\tsec_adjusted = true;\n+\n+\t\t/* No sub-second remainder, done */\n+\t\tif (!delta)\n+\t\t\treturn 0;\n+\t}\n+\n+\t/* Apply sub-second delta via phase step (NCO) or TIE write */\n+\tif (zl3073x_chan_mode_is_nco(chan)) {\n+\t\trc = zl3073x_dpll_ptp_adjtime_phase_step(zldpll, delta);\n+\t\tif (!rc)\n+\t\t\treturn 0;\n+\t} else {\n+\t\trc = zl3073x_chan_tie_write(zldev, zldpll-\u003eid, delta);\n+\t\tif (!rc)\n+\t\t\treturn 0;\n+\t}\n+\n+\t/* Phase step or TIE write failed, fall back to ToD adjust */\n+\trc = zl3073x_chan_tod_adjust(zldev, zldpll-\u003eid,\n+\t\t\t\t ns_to_timespec64(delta));\n+\n+\t/* In the unlikely event that both phase step/TIE write and fallback\n+\t * ToD adjust fail after seconds were already committed, return\n+\t * success to prevent the PTP servo from retrying the full delta and\n+\t * applying seconds again. The sub-second residual will self-correct\n+\t * in the next servo cycle.\n+\t */\n+\tif (rc \u0026\u0026 sec_adjusted) {\n+\t\tdev_warn(zldev-\u003edev,\n+\t\t\t \"Sub-second adjustment failed after seconds applied\\n\");\n+\t\treturn 0;\n+\t}\n+\n+\treturn rc;\n+}\n+\n+/**\n+ * zl3073x_dpll_ptp_adjfine - adjust PTP clock frequency\n+ * @info: PTP clock info\n+ * @scaled_ppm: frequency adjustment in scaled ppm (ppm * 2^16)\n+ *\n+ * Only supported for NCO. Writes the delta frequency offset register.\n+ *\n+ * Return: 0 on success, -EOPNOTSUPP if NCO pin is not connected, \u003c0 on error\n+ */\n+static int\n+zl3073x_dpll_ptp_adjfine(struct ptp_clock_info *info, long scaled_ppm)\n+{\n+\tstruct zl3073x_dpll *zldpll = container_of(info, struct zl3073x_dpll,\n+\t\t\t\t\t\t ptp_info);\n+\tconst struct zl3073x_chan *chan;\n+\ts64 offset;\n+\n+\t/* Convert scaled_ppm to df_offset in 2^-48 steps:\n+\t * df_offset = -(scaled_ppm * 2^32) / 10^6\n+\t *\n+\t * Simplify to avoid overflow:\n+\t * df_offset = -(scaled_ppm * 2^26) / 5^6\n+\t * df_offset = -(scaled_ppm * 67108864) / 15625\n+\t */\n+\toffset = -div_s64((s64)scaled_ppm * 67108864LL, 15625);\n+\n+\tguard(mutex)(\u0026zldpll-\u003elock);\n+\n+\tchan = zl3073x_chan_state_get(zldpll-\u003edev, zldpll-\u003eid);\n+\tif (!zl3073x_chan_mode_is_nco(chan))\n+\t\treturn scaled_ppm ? -EOPNOTSUPP : 0;\n+\tif (offset == chan-\u003edf_offset)\n+\t\treturn 0;\n+\n+\treturn zl3073x_chan_df_offset_set(zldpll-\u003edev, zldpll-\u003eid, offset);\n+}\n+\n+/**\n+ * zl3073x_dpll_ptp_adjphase - adjust PTP clock phase\n+ * @info: PTP clock info\n+ * @delta: phase adjustment in nanoseconds\n+ *\n+ * Only supported in AUTO and REFLOCK modes. Uses TIE write for\n+ * nanosecond resolution phase adjustment.\n+ *\n+ * Return: 0 on success, -EOPNOTSUPP if mode doesn't support TIE, \u003c0 on error\n+ */\n+static int zl3073x_dpll_ptp_adjphase(struct ptp_clock_info *info, s32 delta)\n+{\n+\tstruct zl3073x_dpll *zldpll = container_of(info, struct zl3073x_dpll,\n+\t\t\t\t\t\t ptp_info);\n+\tstruct zl3073x_dev *zldev = zldpll-\u003edev;\n+\tconst struct zl3073x_chan *chan;\n+\n+\tif (!delta)\n+\t\treturn 0;\n+\n+\tguard(mutex)(\u0026zldpll-\u003elock);\n+\n+\tchan = zl3073x_chan_state_get(zldev, zldpll-\u003eid);\n+\n+\tif (!zl3073x_chan_mode_supports_tie(chan))\n+\t\treturn -EOPNOTSUPP;\n+\n+\treturn zl3073x_chan_tie_write(zldev, zldpll-\u003eid, delta);\n+}\n+\n+static s32\n+zl3073x_dpll_ptp_getmaxphase(struct ptp_clock_info *info __always_unused)\n+{\n+\t/* HW limits TIE write to +-1 second. Return the constant HW\n+\t * limit and let adjphase handle mode-specific checks.\n+\t */\n+\treturn NSEC_PER_SEC - 1;\n+}\n+\n+/**\n+ * zl3073x_dpll_ptp_perout_find_pin - find pin by perout channel index\n+ * @zldpll: DPLL channel\n+ * @idx: perout channel index to find\n+ *\n+ * Return: pointer to the pin, or NULL if not found\n+ */\n+static struct zl3073x_dpll_pin *\n+zl3073x_dpll_ptp_perout_find_pin(struct zl3073x_dpll *zldpll, int idx)\n+{\n+\tstruct zl3073x_dpll_pin *pin;\n+\n+\tlist_for_each_entry(pin, \u0026zldpll-\u003epins, list) {\n+\t\tif (pin-\u003eperout_idx == idx)\n+\t\t\treturn pin;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+/**\n+ * zl3073x_dpll_ptp_enable - enable/disable PTP clock functions\n+ * @info: PTP clock info\n+ * @rq: the requested clock function and parameters\n+ * @on: true to enable, false to disable\n+ *\n+ * Handles PTP_CLK_REQ_PEROUT requests. Only 1PPS (period = 1s) is supported.\n+ * On enable, configures the output divider for 1 Hz. On disable, does nothing\n+ * as the signal is not disabled.\n+ *\n+ * Return: 0 on success, \u003c0 on error\n+ */\n+static int zl3073x_dpll_ptp_enable(struct ptp_clock_info *info,\n+\t\t\t\t struct ptp_clock_request *rq, int on)\n+{\n+\tstruct zl3073x_dpll *zldpll = container_of(info, struct zl3073x_dpll,\n+\t\t\t\t\t\t ptp_info);\n+\tstruct zl3073x_dpll_pin *pin;\n+\tunsigned int idx;\n+\tint rc;\n+\n+\tif (rq-\u003etype != PTP_CLK_REQ_PEROUT)\n+\t\treturn -EOPNOTSUPP;\n+\n+\tidx = rq-\u003eperout.index;\n+\tif (idx \u003e= info-\u003en_per_out)\n+\t\treturn -EINVAL;\n+\n+\t/* Toggling output pins can produce runt pulses, so disable\n+\t * is a no-op.\n+\t */\n+\tif (!on)\n+\t\treturn 0;\n+\n+\t/* Only accept exactly 1PPS (period.sec == 1, nsec == 0) */\n+\tif (rq-\u003eperout.period.sec != 1 || rq-\u003eperout.period.nsec != 0)\n+\t\treturn -EINVAL;\n+\n+\tmutex_lock(\u0026zldpll-\u003elock);\n+\n+\tpin = zl3073x_dpll_ptp_perout_find_pin(zldpll, idx);\n+\tif (!pin) {\n+\t\tmutex_unlock(\u0026zldpll-\u003elock);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/* Already at 1 Hz, nothing to do */\n+\tif (zl3073x_dev_output_pin_freq_get(zldpll-\u003edev, pin-\u003eid) == 1) {\n+\t\tmutex_unlock(\u0026zldpll-\u003elock);\n+\t\treturn 0;\n+\t}\n+\n+\trc = __zl3073x_dpll_output_pin_frequency_set(zldpll, pin, 1);\n+\n+\tmutex_unlock(\u0026zldpll-\u003elock);\n+\n+\tif (!rc)\n+\t\tdpll_pin_change_ntf(pin-\u003edpll_pin);\n+\n+\treturn rc;\n+}\n+\n+static const struct ptp_clock_info zl3073x_dpll_ptp_clock_info = {\n+\t.owner\t\t= THIS_MODULE,\n+\t.max_adj\t= ZL3073X_DPLL_PTP_MAX_ADJ,\n+\t.gettimex64\t= zl3073x_dpll_ptp_gettimex64,\n+\t.settime64\t= zl3073x_dpll_ptp_settime64,\n+\t.adjtime\t= zl3073x_dpll_ptp_adjtime,\n+\t.adjfine\t= zl3073x_dpll_ptp_adjfine,\n+\t.adjphase\t= zl3073x_dpll_ptp_adjphase,\n+\t.getmaxphase\t= zl3073x_dpll_ptp_getmaxphase,\n+\t.enable\t\t= zl3073x_dpll_ptp_enable,\n+};\n+\n+/**\n+ * zl3073x_dpll_ptp_register - register PTP clock for a DPLL channel\n+ * @zldpll: DPLL channel to register PTP clock for\n+ *\n+ * Return: 0 on success, \u003c0 on error\n+ */\n+static int zl3073x_dpll_ptp_register(struct zl3073x_dpll *zldpll)\n+{\n+\tstruct zl3073x_dev *zldev = zldpll-\u003edev;\n+\tstruct ptp_clock *ptp_clock;\n+\n+\tsnprintf(zldpll-\u003eptp_info.name, sizeof(zldpll-\u003eptp_info.name),\n+\t\t \"zl3073x-dpll%u\", zldpll-\u003eid);\n+\n+\tptp_clock = ptp_clock_register(\u0026zldpll-\u003eptp_info, zldev-\u003edev);\n+\tif (IS_ERR(ptp_clock)) {\n+\t\tdev_err(zldev-\u003edev, \"Failed to register PTP clock for DPLL%u\\n\",\n+\t\t\tzldpll-\u003eid);\n+\t\treturn PTR_ERR(ptp_clock);\n+\t}\n+\n+\tzldpll-\u003eptp_clock = ptp_clock;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * zl3073x_dpll_ptp_unregister - unregister PTP clock for a DPLL channel\n+ * @zldpll: DPLL channel to unregister PTP clock for\n+ */\n+static void zl3073x_dpll_ptp_unregister(struct zl3073x_dpll *zldpll)\n+{\n+\tif (!IS_ERR_OR_NULL(zldpll-\u003eptp_clock)) {\n+\t\tptp_clock_unregister(zldpll-\u003eptp_clock);\n+\t\tzldpll-\u003eptp_clock = NULL;\n+\t}\n }\n \n /**\n@@ -2398,6 +2858,48 @@ zl3073x_dpll_ref_sync_pairs_register(struct zl3073x_dpll *zldpll)\n \treturn 0;\n }\n \n+/**\n+ * zl3073x_dpll_alloc - allocate DPLL device\n+ * @zldev: pointer to zl3073x device\n+ * @ch: DPLL channel number\n+ *\n+ * Allocates DPLL device structure for given DPLL channel.\n+ *\n+ * Return: pointer to DPLL device on success, error pointer on error\n+ */\n+struct zl3073x_dpll *\n+zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch)\n+{\n+\tstruct zl3073x_dpll *zldpll;\n+\n+\tzldpll = kzalloc_obj(*zldpll);\n+\tif (!zldpll)\n+\t\treturn ERR_PTR(-ENOMEM);\n+\n+\tzldpll-\u003edev = zldev;\n+\tzldpll-\u003eid = ch;\n+\tzldpll-\u003eptp_info = zl3073x_dpll_ptp_clock_info;\n+\tmutex_init(\u0026zldpll-\u003elock);\n+\tINIT_LIST_HEAD(\u0026zldpll-\u003epins);\n+\n+\treturn zldpll;\n+}\n+\n+/**\n+ * zl3073x_dpll_free - free DPLL device\n+ * @zldpll: pointer to zl3073x_dpll structure\n+ *\n+ * Deallocates given DPLL device previously allocated by @zl3073x_dpll_alloc.\n+ */\n+void\n+zl3073x_dpll_free(struct zl3073x_dpll *zldpll)\n+{\n+\tWARN(zldpll-\u003edpll_dev, \"DPLL device is still registered\\n\");\n+\n+\tmutex_destroy(\u0026zldpll-\u003elock);\n+\tkfree(zldpll);\n+}\n+\n /**\n * zl3073x_dpll_register - register DPLL device and all its pins\n * @zldpll: pointer to zl3073x_dpll structure\n@@ -2428,6 +2930,13 @@ zl3073x_dpll_register(struct zl3073x_dpll *zldpll)\n \t\treturn rc;\n \t}\n \n+\trc = zl3073x_dpll_ptp_register(zldpll);\n+\tif (rc) {\n+\t\tzl3073x_dpll_pins_unregister(zldpll);\n+\t\tzl3073x_dpll_device_unregister(zldpll);\n+\t\treturn rc;\n+\t}\n+\n \treturn 0;\n }\n \n@@ -2441,7 +2950,7 @@ zl3073x_dpll_register(struct zl3073x_dpll *zldpll)\n void\n zl3073x_dpll_unregister(struct zl3073x_dpll *zldpll)\n {\n-\t/* Unregister all pins and dpll */\n+\tzl3073x_dpll_ptp_unregister(zldpll);\n \tzl3073x_dpll_pins_unregister(zldpll);\n \tzl3073x_dpll_device_unregister(zldpll);\n }\ndiff --git a/drivers/dpll/zl3073x/dpll.h b/drivers/dpll/zl3073x/dpll.h\nindex faebc402ba1b7..993221dc63249 100644\n--- a/drivers/dpll/zl3073x/dpll.h\n+++ b/drivers/dpll/zl3073x/dpll.h\n@@ -5,6 +5,7 @@\n \n #include \u003clinux/dpll.h\u003e\n #include \u003clinux/list.h\u003e\n+#include \u003clinux/ptp_clock_kernel.h\u003e\n \n #include \"core.h\"\n \n@@ -22,6 +23,8 @@\n * @type: DPLL type (PPS or EEC)\n * @lock_status: last saved DPLL lock status\n * @pins: list of pins\n+ * @ptp_info: PTP clock info\n+ * @ptp_clock: registered PTP clock (or NULL)\n */\n struct zl3073x_dpll {\n \tstruct list_head\t\tlist;\n@@ -36,6 +39,8 @@ struct zl3073x_dpll {\n \tenum dpll_type\t\t\ttype;\n \tenum dpll_lock_status\t\tlock_status;\n \tstruct list_head\t\tpins;\n+\tstruct ptp_clock_info\t\tptp_info;\n+\tstruct ptp_clock\t\t*ptp_clock;\n };\n \n struct zl3073x_dpll *zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch);\ndiff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h\nindex b70ead7d4495b..447757e99845d 100644\n--- a/drivers/dpll/zl3073x/regs.h\n+++ b/drivers/dpll/zl3073x/regs.h\n@@ -179,6 +179,20 @@\n #define ZL_DPLL_DF_READ_CMD\t\t\tGENMASK(2, 0)\n #define ZL_DPLL_DF_READ_CMD_ACC_I\t\t4\n \n+#define ZL_REG_DPLL_TIE_CTRL\t\t\tZL_REG(5, 0x30, 1)\n+#define ZL_DPLL_TIE_CTRL_OP\t\t\tGENMASK(2, 0)\n+#define ZL_DPLL_TIE_CTRL_OP_WR\t\t\t4\n+\n+#define ZL_REG_DPLL_TIE_CTRL_MASK\t\tZL_REG(5, 0x31, 1)\n+\n+#define ZL_REG_DPLL_TOD_CTRL(_idx)\t\t\t\t\t\\\n+\tZL_REG_IDX(_idx, 5, 0x38, 1, 8, 1)\n+#define ZL_DPLL_TOD_CTRL_SEM\t\t\tBIT(4)\n+#define ZL_DPLL_TOD_CTRL_CMD\t\t\tGENMASK(3, 0)\n+#define ZL_DPLL_TOD_CTRL_CMD_WR_NEXT_1HZ\t1\n+#define ZL_DPLL_TOD_CTRL_CMD_RD_CURRENT\t8\n+#define ZL_DPLL_TOD_CTRL_CMD_RD_NEXT_1HZ\t9\n+\n #define ZL_REG_DPLL_MEAS_CTRL\t\t\tZL_REG(5, 0x50, 1)\n #define ZL_DPLL_MEAS_CTRL_EN\t\t\tBIT(0)\n #define ZL_DPLL_MEAS_CTRL_AVG_FACTOR\t\tGENMASK(7, 4)\n@@ -193,6 +207,9 @@\n \n /*******************************\n * Register Pages 6-7, DPLL Data\n+ *\n+ * Per-channel registers with stride 0x20. Channels 0-3 reside on page 6,\n+ * channel 4 on page 7.\n *******************************/\n \n #define ZL_REG_DPLL_DF_OFFSET_03(_idx)\t\t\t\t\t\\\n@@ -202,6 +219,24 @@\n \t((_idx) \u003c 4 ? ZL_REG_DPLL_DF_OFFSET_03(_idx) : ZL_REG_DPLL_DF_OFFSET_4)\n #define ZL_DPLL_DF_OFFSET_UNKNOWN\tS64_MIN\n \n+#define ZL_REG_DPLL_TIE_DATA_03(_idx)\t\t\t\t\t\\\n+\tZL_REG_IDX(_idx, 6, 0x0C, 6, 4, 0x20)\n+#define ZL_REG_DPLL_TIE_DATA_4\t\t\tZL_REG(7, 0x0C, 6)\n+#define ZL_REG_DPLL_TIE_DATA(_idx)\t\t\t\t\t\\\n+\t((_idx) \u003c 4 ? ZL_REG_DPLL_TIE_DATA_03(_idx) : ZL_REG_DPLL_TIE_DATA_4)\n+\n+#define ZL_REG_DPLL_TOD_SEC_03(_idx)\t\t\t\t\t\\\n+\tZL_REG_IDX(_idx, 6, 0x12, 6, 4, 0x20)\n+#define ZL_REG_DPLL_TOD_SEC_4\t\t\tZL_REG(7, 0x12, 6)\n+#define ZL_REG_DPLL_TOD_SEC(_idx)\t\t\t\t\t\\\n+\t((_idx) \u003c 4 ? ZL_REG_DPLL_TOD_SEC_03(_idx) : ZL_REG_DPLL_TOD_SEC_4)\n+\n+#define ZL_REG_DPLL_TOD_NS_03(_idx)\t\t\t\t\t\\\n+\tZL_REG_IDX(_idx, 6, 0x18, 4, 4, 0x20)\n+#define ZL_REG_DPLL_TOD_NS_4\t\t\tZL_REG(7, 0x18, 4)\n+#define ZL_REG_DPLL_TOD_NS(_idx)\t\t\t\t\t\\\n+\t((_idx) \u003c 4 ? ZL_REG_DPLL_TOD_NS_03(_idx) : ZL_REG_DPLL_TOD_NS_4)\n+\n /***********************************\n * Register Page 9, Synth and Output\n ***********************************/\n@@ -221,6 +256,8 @@\n #define ZL_OUTPUT_CTRL_EN\t\t\tBIT(0)\n #define ZL_OUTPUT_CTRL_SYNTH_SEL\t\tGENMASK(6, 4)\n \n+#define ZL_REG_OUTPUT_STEP_TIME_MASK\t\tZL_REG(9, 0x36, 2)\n+\n /*******************************\n * Register Page 10, Ref Mailbox\n *******************************/\n@@ -316,6 +353,25 @@\n #define ZL_REG_OUTPUT_ESYNC_WIDTH\t\tZL_REG(14, 0x18, 4)\n #define ZL_REG_OUTPUT_PHASE_COMP\t\tZL_REG(14, 0x20, 4)\n \n+/***********************************\n+ * Register Page 9, Output Phase Step\n+ ***********************************/\n+\n+#define ZL_REG_OUTPUT_PHASE_STEP_CTRL\t\tZL_REG(9, 0x38, 1)\n+#define ZL_OUTPUT_PHASE_STEP_CTRL_DPLL\t\tGENMASK(6, 4)\n+#define ZL_OUTPUT_PHASE_STEP_CTRL_TOD_STEP\tBIT(3)\n+#define ZL_OUTPUT_PHASE_STEP_CTRL_OP\t\tGENMASK(1, 0)\n+#define ZL_OUTPUT_PHASE_STEP_CTRL_OP_NONE\t0\n+#define ZL_OUTPUT_PHASE_STEP_CTRL_OP_RESET\t1\n+#define ZL_OUTPUT_PHASE_STEP_CTRL_OP_READ\t2\n+#define ZL_OUTPUT_PHASE_STEP_CTRL_OP_WRITE\t3\n+\n+#define ZL_REG_OUTPUT_PHASE_STEP_NUMBER\t\tZL_REG(9, 0x39, 1)\n+\n+#define ZL_REG_OUTPUT_PHASE_STEP_MASK\t\tZL_REG(9, 0x3a, 2)\n+\n+#define ZL_REG_OUTPUT_PHASE_STEP_DATA\t\tZL_REG(9, 0x3c, 4)\n+\n /*\n * Register Page 255 - HW registers access\n */\n"
} |
| 2/1 | 2026/08/01 02:52 | agent | patch-evaluator |
1mModel:best-expensive Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch adds PTP clock support to the Microchip Azurite DPLL driver (zl3073x). This is a vendor-specific hardware driver for I2C/SPI connected DPLL devices. Since the physical hardware is not emulated in standard QEMU/GCE environments, the driver's probe function will fail to read the expected hardware chip IDs, making the modified code paths unreachable during fuzzing.",
"WorthFuzzing": false
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a '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: For your convenience, here is the diff of the changes:
commit 6423e50ecd001cde2831841cc9cc1924768c91a9
Author: syz-cluster <triage@syzkaller.com>
Date: Sat Aug 1 02:52:34 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/dpll/zl3073x/Kconfig b/drivers/dpll/zl3073x/Kconfig
index 5bbca14005813..da1731ace15f0 100644
--- a/drivers/dpll/zl3073x/Kconfig
+++ b/drivers/dpll/zl3073x/Kconfig
@@ -2,7 +2,7 @@
config ZL3073X
tristate "Microchip Azurite DPLL/PTP/SyncE devices" if COMPILE_TEST
- depends on NET
+ depends on NET && PTP_1588_CLOCK
select DPLL
select NET_DEVLINK
select REGMAP
@@ -16,7 +16,7 @@ config ZL3073X
config ZL3073X_I2C
tristate "I2C bus implementation for Microchip Azurite devices"
- depends on I2C && NET
+ depends on I2C && NET && PTP_1588_CLOCK
select REGMAP_I2C
select ZL3073X
help
@@ -28,7 +28,7 @@ config ZL3073X_I2C
config ZL3073X_SPI
tristate "SPI bus implementation for Microchip Azurite devices"
- depends on NET && SPI
+ depends on NET && SPI && PTP_1588_CLOCK
select REGMAP_SPI
select ZL3073X
help
diff --git a/drivers/dpll/zl3073x/chan.c b/drivers/dpll/zl3073x/chan.c
index 4ec2cf53dad46..53032e782fd4a 100644
--- a/drivers/dpll/zl3073x/chan.c
+++ b/drivers/dpll/zl3073x/chan.c
@@ -3,6 +3,7 @@
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/dev_printk.h>
+#include <linux/ptp_clock_kernel.h>
#include <linux/string.h>
#include <linux/types.h>
@@ -202,6 +203,11 @@ int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index)
zl3073x_chan_refsel_state_get(chan),
zl3073x_chan_refsel_ref_get(chan));
+ rc = zl3073x_read_u16(zldev, ZL_REG_OUTPUT_STEP_TIME_MASK,
+ &chan->out_step_time_mask);
+ if (rc)
+ return rc;
+
guard(mutex)(&zldev->multiop_lock);
/* Read DPLL configuration from mailbox */
@@ -234,6 +240,307 @@ const struct zl3073x_chan *zl3073x_chan_state_get(struct zl3073x_dev *zldev,
return &zldev->chan[index];
}
+/**
+ * zl3073x_chan_tod_ready_wait - wait for ToD semaphore to clear
+ * @zldev: pointer to zl3073x device
+ * @ch: DPLL channel index
+ *
+ * Polls the ToD control register until the semaphore bit is cleared,
+ * indicating the device has completed the previous ToD operation.
+ *
+ * Return: 0 on success, -EBUSY if semaphore not cleared, <0 on error
+ */
+static int zl3073x_chan_tod_ready_wait(struct zl3073x_dev *zldev, u8 ch)
+{
+ unsigned int timeout;
+ u8 tod_ctrl;
+ int rc;
+
+ rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_TOD_CTRL(ch), &tod_ctrl);
+ if (rc)
+ return rc;
+
+ switch (FIELD_GET(ZL_DPLL_TOD_CTRL_CMD, tod_ctrl)) {
+ case ZL_DPLL_TOD_CTRL_CMD_WR_NEXT_1HZ:
+ timeout = ZL_POLL_TOD_WR_TIMEOUT_US;
+ break;
+ default:
+ timeout = ZL_POLL_TOD_RD_TIMEOUT_US;
+ break;
+ }
+
+ rc = zl3073x_poll_zero_u8(zldev, ZL_REG_DPLL_TOD_CTRL(ch),
+ ZL_DPLL_TOD_CTRL_SEM, timeout);
+
+ return rc == -ETIMEDOUT ? -EBUSY : rc;
+}
+
+/**
+ * zl3073x_chan_tod_ctrl - issue ToD command
+ * @zldev: pointer to zl3073x device
+ * @ch: DPLL channel index
+ * @cmd: ToD command to execute
+ *
+ * Writes the semaphore and command to dpll_tod_ctrl. The caller must
+ * ensure the device is ready (semaphore clear) before calling and
+ * must wait for completion if needed.
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int zl3073x_chan_tod_ctrl(struct zl3073x_dev *zldev, u8 ch, u8 cmd)
+{
+ return zl3073x_write_u8(zldev, ZL_REG_DPLL_TOD_CTRL(ch),
+ ZL_DPLL_TOD_CTRL_SEM | cmd);
+}
+
+/**
+ * zl3073x_chan_tod_read - read ToD registers after issuing a command
+ * @zldev: pointer to zl3073x device
+ * @ch: DPLL channel index
+ * @next_hz: if true, read predicted ToD at next 1 Hz; otherwise read current
+ * @ts: timespec to store the result
+ * @sts: optional system timestamp pair for cross-timestamping
+ *
+ * Context: Caller must serialize all zl3073x_chan_tod_* calls externally.
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_chan_tod_read(struct zl3073x_dev *zldev, u8 ch,
+ bool next_hz, struct timespec64 *ts,
+ struct ptp_system_timestamp *sts)
+{
+ u32 nsec;
+ u64 sec;
+ u8 cmd;
+ int rc;
+
+ if (next_hz)
+ cmd = ZL_DPLL_TOD_CTRL_CMD_RD_NEXT_1HZ;
+ else
+ cmd = ZL_DPLL_TOD_CTRL_CMD_RD_CURRENT;
+
+ /* Wait for any previous ToD operation to complete */
+ rc = zl3073x_chan_tod_ready_wait(zldev, ch);
+ if (rc)
+ return rc;
+
+ ptp_read_system_prets(sts);
+ rc = zl3073x_chan_tod_ctrl(zldev, ch, cmd);
+ if (rc)
+ return rc;
+
+ rc = zl3073x_chan_tod_ready_wait(zldev, ch);
+ if (rc)
+ return rc;
+ ptp_read_system_postts(sts);
+
+ rc = zl3073x_read_u48(zldev, ZL_REG_DPLL_TOD_SEC(ch), &sec);
+ if (rc)
+ return rc;
+
+ /* HW nanoseconds are always in [0, NSEC_PER_SEC) range */
+ rc = zl3073x_read_u32(zldev, ZL_REG_DPLL_TOD_NS(ch), &nsec);
+ if (rc)
+ return rc;
+
+ ts->tv_sec = sec;
+ ts->tv_nsec = nsec;
+
+ return 0;
+}
+
+/**
+ * zl3073x_chan_tod_write - write ToD registers and trigger 1 Hz update
+ * @zldev: pointer to zl3073x device
+ * @ch: DPLL channel index
+ * @ts: time to set
+ *
+ * Context: Caller must serialize all zl3073x_chan_tod_* calls externally.
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_chan_tod_write(struct zl3073x_dev *zldev, u8 ch,
+ struct timespec64 ts)
+{
+ int rc;
+
+ /* Wait for any previous ToD operation to complete */
+ rc = zl3073x_chan_tod_ready_wait(zldev, ch);
+ if (rc)
+ return rc;
+
+ rc = zl3073x_write_u48(zldev, ZL_REG_DPLL_TOD_SEC(ch), ts.tv_sec);
+ if (rc)
+ return rc;
+
+ rc = zl3073x_write_u32(zldev, ZL_REG_DPLL_TOD_NS(ch), ts.tv_nsec);
+ if (rc)
+ return rc;
+
+ return zl3073x_chan_tod_ctrl(zldev, ch,
+ ZL_DPLL_TOD_CTRL_CMD_WR_NEXT_1HZ);
+}
+
+/**
+ * zl3073x_chan_tod_adjust - atomic ToD read-modify-write with rollover guard
+ * @zldev: pointer to zl3073x device
+ * @ch: DPLL channel index
+ * @delta: time adjustment to apply
+ *
+ * Reads the next-Hz ToD and current ToD, then checks whether enough time
+ * remains before the next 1 Hz rollover to safely complete the write.
+ * If less than 20 ms remains, waits for the rollover and increments the
+ * next-Hz seconds by one. Applies @delta and writes the result back.
+ *
+ * Context: Caller must serialize all zl3073x_chan_tod_* calls externally.
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_chan_tod_adjust(struct zl3073x_dev *zldev, u8 ch,
+ struct timespec64 delta)
+{
+ static const long threshold_ns = 20 * NSEC_PER_MSEC;
+ struct timespec64 ts_next, ts_cur, diff;
+ int rc;
+
+ /* Read predicted ToD at next 1 Hz tick */
+ rc = zl3073x_chan_tod_read(zldev, ch, true, &ts_next, NULL);
+ if (rc)
+ return rc;
+
+ /* Read current ToD to determine remaining margin */
+ rc = zl3073x_chan_tod_read(zldev, ch, false, &ts_cur, NULL);
+ if (rc)
+ return rc;
+
+ /* If too close to (or past) the next rollover, wait it out */
+ diff = timespec64_sub(ts_next, ts_cur);
+ if (diff.tv_sec < 0 || (!diff.tv_sec && diff.tv_nsec < threshold_ns)) {
+ if (!diff.tv_sec && diff.tv_nsec)
+ fsleep((unsigned long)diff.tv_nsec / NSEC_PER_USEC + 1);
+ ts_next.tv_sec++;
+ }
+
+ /* Apply delta to the next-Hz ToD */
+ ts_next = timespec64_add(ts_next, delta);
+ if (!timespec64_valid(&ts_next))
+ return -EINVAL;
+
+ /* Write adjusted ToD back and wait for completion */
+ rc = zl3073x_chan_tod_write(zldev, ch, ts_next);
+ if (rc)
+ return rc;
+
+ return zl3073x_chan_tod_ready_wait(zldev, ch);
+}
+
+/**
+ * zl3073x_chan_df_offset_set - write delta frequency offset to hardware
+ * @zldev: pointer to zl3073x device
+ * @ch: DPLL channel index
+ * @offset: frequency offset in 2^-48 steps
+ *
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_chan_df_offset_set(struct zl3073x_dev *zldev, u8 ch, s64 offset)
+{
+ int rc;
+
+ rc = zl3073x_write_u48(zldev, ZL_REG_DPLL_DF_OFFSET(ch), offset);
+ if (!rc)
+ zldev->chan[ch].df_offset = offset;
+
+ return rc;
+}
+
+/**
+ * zl3073x_chan_tie_write - adjust DPLL phase using TIE write
+ * @zldev: pointer to zl3073x device
+ * @ch: DPLL channel index
+ * @delta_ns: phase adjustment in nanoseconds (must be within +-1s)
+ *
+ * Converts nanoseconds to TIE units (0.01 ps) and writes TIE data
+ * to the specified channel.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_chan_tie_write(struct zl3073x_dev *zldev, u8 ch, s64 delta_ns)
+{
+ s64 tie_data;
+ int rc;
+
+ guard(mutex)(&zldev->tie_lock);
+
+ /* Wait for any previous TIE operation to complete */
+ rc = zl3073x_poll_zero_u8(zldev, ZL_REG_DPLL_TIE_CTRL,
+ ZL_DPLL_TIE_CTRL_OP,
+ ZL_POLL_TIE_WR_TIMEOUT_US);
+ if (rc)
+ return rc;
+
+ /* Convert ns to TIE units (0.01 ps = 10^-14 s) */
+ tie_data = delta_ns * 100000LL;
+
+ rc = zl3073x_write_u48(zldev, ZL_REG_DPLL_TIE_DATA(ch), tie_data);
+ if (rc)
+ return rc;
+
+ rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_TIE_CTRL_MASK, BIT(ch));
+ if (rc)
+ return rc;
+
+ return zl3073x_write_u8(zldev, ZL_REG_DPLL_TIE_CTRL,
+ ZL_DPLL_TIE_CTRL_OP_WR);
+}
+
+/**
+ * zl3073x_chan_phase_step - execute one output phase step operation
+ * @zldev: pointer to zl3073x device
+ * @ch: DPLL channel index
+ * @out_mask: bitmask of outputs to step
+ * @step_cycles: phase step in synthesizer clock cycles
+ * @tod_step: also step the ToD counter
+ *
+ * All masked outputs must use synthesizers of the same frequency since
+ * the step value is in synthesizer clock cycles.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_chan_phase_step(struct zl3073x_dev *zldev, u8 ch,
+ u16 out_mask, s32 step_cycles,
+ bool tod_step)
+{
+ u8 ctrl;
+ int rc;
+
+ guard(mutex)(&zldev->phase_step_lock);
+
+ /* Wait for any previous phase step operation to complete */
+ rc = zl3073x_poll_zero_u8(zldev, ZL_REG_OUTPUT_PHASE_STEP_CTRL,
+ ZL_OUTPUT_PHASE_STEP_CTRL_OP,
+ ZL_POLL_PHASE_STEP_TIMEOUT_US);
+ if (rc)
+ return rc;
+
+ rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_PHASE_STEP_DATA,
+ step_cycles);
+ if (rc)
+ return rc;
+
+ rc = zl3073x_write_u16(zldev, ZL_REG_OUTPUT_PHASE_STEP_MASK, out_mask);
+ if (rc)
+ return rc;
+
+ rc = zl3073x_write_u8(zldev, ZL_REG_OUTPUT_PHASE_STEP_NUMBER, 1);
+ if (rc)
+ return rc;
+
+ ctrl = FIELD_PREP(ZL_OUTPUT_PHASE_STEP_CTRL_DPLL, ch) |
+ FIELD_PREP(ZL_OUTPUT_PHASE_STEP_CTRL_OP,
+ ZL_OUTPUT_PHASE_STEP_CTRL_OP_WRITE);
+ if (tod_step)
+ ctrl |= ZL_OUTPUT_PHASE_STEP_CTRL_TOD_STEP;
+
+ return zl3073x_write_u8(zldev, ZL_REG_OUTPUT_PHASE_STEP_CTRL, ctrl);
+}
+
/**
* zl3073x_chan_state_set - commit DPLL channel state changes to hardware
* @zldev: pointer to zl3073x_dev structure
diff --git a/drivers/dpll/zl3073x/chan.h b/drivers/dpll/zl3073x/chan.h
index dc9c6d95bdee7..9dc5a3f1991f1 100644
--- a/drivers/dpll/zl3073x/chan.h
+++ b/drivers/dpll/zl3073x/chan.h
@@ -5,10 +5,12 @@
#include <linux/bitfield.h>
#include <linux/stddef.h>
+#include <linux/time64.h>
#include <linux/types.h>
#include "regs.h"
+struct ptp_system_timestamp;
struct zl3073x_dev;
/**
@@ -16,6 +18,7 @@ struct zl3073x_dev;
* @ctrl: DPLL control register value
* @mode_refsel: mode and reference selection register value
* @ref_prio: reference priority registers (4 bits per ref, P/N packed)
+ * @out_step_time_mask: output step-time mask
* @mon_status: monitor status register value
* @refsel_status: reference selection status register value
* @df_offset: frequency offset vs tracked reference in 2^-48 steps
@@ -26,6 +29,9 @@ struct zl3073x_chan {
u8 mode_refsel;
u8 ref_prio[ZL3073X_NUM_REFS / 2];
);
+ struct_group(inv, /* Invariants */
+ u16 out_step_time_mask;
+ );
struct_group(stat,
u8 mon_status;
u8 refsel_status;
@@ -42,6 +48,20 @@ int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index,
int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index);
int zl3073x_chan_nco_mode_set(struct zl3073x_dev *zldev, u8 index);
+int zl3073x_chan_tod_read(struct zl3073x_dev *zldev, u8 ch,
+ bool next_hz, struct timespec64 *ts,
+ struct ptp_system_timestamp *sts);
+int zl3073x_chan_tod_write(struct zl3073x_dev *zldev, u8 ch,
+ struct timespec64 ts);
+int zl3073x_chan_tod_adjust(struct zl3073x_dev *zldev, u8 ch,
+ struct timespec64 delta);
+int zl3073x_chan_phase_step(struct zl3073x_dev *zldev, u8 ch,
+ u16 out_mask, s32 step_cycles, bool tod_step);
+
+int zl3073x_chan_df_offset_set(struct zl3073x_dev *zldev, u8 ch, s64 offset);
+
+int zl3073x_chan_tie_write(struct zl3073x_dev *zldev, u8 ch, s64 delta_ns);
+
/**
* zl3073x_chan_df_offset_get - get cached df_offset vs tracked reference
* @chan: pointer to channel state
@@ -200,6 +220,21 @@ static inline bool zl3073x_chan_mode_is_reflock(const struct zl3073x_chan *chan)
return zl3073x_chan_mode_get(chan) == ZL_DPLL_MODE_REFSEL_MODE_REFLOCK;
}
+/**
+ * zl3073x_chan_mode_supports_tie - check if channel mode supports TIE write
+ * @chan: pointer to channel state
+ *
+ * TIE write is supported in AUTO and REFLOCK modes regardless of lock state.
+ *
+ * Return: true if TIE write is supported, false otherwise
+ */
+static inline bool
+zl3073x_chan_mode_supports_tie(const struct zl3073x_chan *chan)
+{
+ return zl3073x_chan_mode_is_auto(chan) ||
+ zl3073x_chan_mode_is_reflock(chan);
+}
+
/**
* zl3073x_chan_is_ho_ready - check if holdover is ready
* @chan: pointer to channel state
@@ -233,4 +268,17 @@ static inline u8 zl3073x_chan_refsel_ref_get(const struct zl3073x_chan *chan)
return FIELD_GET(ZL_DPLL_REFSEL_STATUS_REFSEL, chan->refsel_status);
}
+/**
+ * zl3073x_chan_is_out_stepped - check if output is in step-time mask
+ * @chan: pointer to channel state
+ * @out: output index
+ *
+ * Return: true if output is affected by step-time operations
+ */
+static inline bool
+zl3073x_chan_is_out_stepped(const struct zl3073x_chan *chan, u8 out)
+{
+ return !!(chan->out_step_time_mask & BIT(out));
+}
+
#endif /* _ZL3073X_CHAN_H */
diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c
index 7f5afaaae6342..b2f9fc97fd1b6 100644
--- a/drivers/dpll/zl3073x/core.c
+++ b/drivers/dpll/zl3073x/core.c
@@ -1033,6 +1033,14 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev)
* and/or polls are required to be done atomically.
*/
rc = devm_mutex_init(zldev->dev, &zldev->multiop_lock);
+ if (rc)
+ return dev_err_probe(zldev->dev, rc,
+ "Failed to initialize mutex\n");
+ rc = devm_mutex_init(zldev->dev, &zldev->phase_step_lock);
+ if (rc)
+ return dev_err_probe(zldev->dev, rc,
+ "Failed to initialize mutex\n");
+ rc = devm_mutex_init(zldev->dev, &zldev->tie_lock);
if (rc)
return dev_err_probe(zldev->dev, rc,
"Failed to initialize mutex\n");
diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h
index 78dc208f3eea2..2dc836d0c8f6b 100644
--- a/drivers/dpll/zl3073x/core.h
+++ b/drivers/dpll/zl3073x/core.h
@@ -26,6 +26,10 @@ struct zl3073x_dpll;
#define ZL_POLL_HWREG_TIMEOUT_US (50 * USEC_PER_MSEC)
#define ZL_POLL_MB_TIMEOUT_US (30 * USEC_PER_MSEC)
#define ZL_POLL_PHASE_ERR_TIMEOUT_US (50 * USEC_PER_MSEC)
+#define ZL_POLL_PHASE_STEP_TIMEOUT_US (3000 * USEC_PER_MSEC)
+#define ZL_POLL_TIE_WR_TIMEOUT_US (1000 * USEC_PER_MSEC)
+#define ZL_POLL_TOD_RD_TIMEOUT_US (30 * USEC_PER_MSEC)
+#define ZL_POLL_TOD_WR_TIMEOUT_US (1000 * USEC_PER_MSEC)
enum zl3073x_flags {
ZL3073X_FLAG_REF_PHASE_COMP_32_BIT,
@@ -55,6 +59,8 @@ struct zl3073x_chip_info {
* @regmap: regmap to access device registers
* @info: detected chip info
* @multiop_lock: to serialize multiple register operations
+ * @tie_lock: to serialize TIE write operations
+ * @phase_step_lock: to serialize output phase step operations
* @ref: array of input references' invariants
* @out: array of outs' invariants
* @synth: array of synths' invariants
@@ -71,6 +77,8 @@ struct zl3073x_dev {
struct regmap *regmap;
const struct zl3073x_chip_info *info;
struct mutex multiop_lock;
+ struct mutex tie_lock;
+ struct mutex phase_step_lock;
/* Invariants */
struct zl3073x_ref ref[ZL3073X_NUM_REFS];
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
index 0488ae6ac486c..320852dfc58cb 100644
--- a/drivers/dpll/zl3073x/dpll.c
+++ b/drivers/dpll/zl3073x/dpll.c
@@ -2,6 +2,7 @@
#include <linux/bits.h>
#include <linux/bitfield.h>
+#include <linux/cleanup.h>
#include <linux/bug.h>
#include <linux/container_of.h>
#include <linux/dev_printk.h>
@@ -13,6 +14,7 @@
#include <linux/netlink.h>
#include <linux/platform_device.h>
#include <linux/property.h>
+#include <linux/ptp_clock_kernel.h>
#include <linux/slab.h>
#include <linux/sprintf.h>
@@ -41,6 +43,7 @@
* @phase_offset: last saved pin phase offset
* @freq_offset: last saved fractional frequency offset
* @measured_freq: last saved measured frequency
+ * @perout_idx: PTP perout channel index, or -1 if not perout-eligible
*/
struct zl3073x_dpll_pin {
struct list_head list;
@@ -58,6 +61,7 @@ struct zl3073x_dpll_pin {
s64 phase_offset;
s64 freq_offset;
u32 measured_freq;
+ s8 perout_idx;
};
/*
@@ -935,21 +939,17 @@ zl3073x_dpll_output_pin_frequency_get(const struct dpll_pin *dpll_pin,
}
static int
-zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin,
- void *pin_priv,
- const struct dpll_device *dpll,
- void *dpll_priv, u64 frequency,
- struct netlink_ext_ack *extack)
+__zl3073x_dpll_output_pin_frequency_set(struct zl3073x_dpll *zldpll,
+ struct zl3073x_dpll_pin *pin,
+ u64 frequency)
{
- struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
- struct zl3073x_dpll_pin *pin = pin_priv;
const struct zl3073x_synth *synth;
u32 new_div, synth_freq;
struct zl3073x_out out;
u8 out_id;
- guard(mutex)(&zldpll->lock);
+ lockdep_assert_held(&zldpll->lock);
out_id = zl3073x_output_pin_out_get(pin->id);
out = *zl3073x_out_state_get(zldev, out_id);
@@ -1010,6 +1010,21 @@ zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin,
return zl3073x_out_state_set(zldev, out_id, &out);
}
+static int
+zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv, u64 frequency,
+ struct netlink_ext_ack *extack)
+{
+ struct zl3073x_dpll *zldpll = dpll_priv;
+
+ guard(mutex)(&zldpll->lock);
+
+ return __zl3073x_dpll_output_pin_frequency_set(zldpll, pin_priv,
+ frequency);
+}
+
static int
zl3073x_dpll_output_pin_phase_adjust_get(const struct dpll_pin *dpll_pin,
void *pin_priv,
@@ -1607,6 +1622,7 @@ zl3073x_dpll_pin_alloc(struct zl3073x_dpll *zldpll, enum dpll_pin_direction dir,
pin->dpll = zldpll;
pin->dir = dir;
pin->id = id;
+ pin->perout_idx = -1;
return pin;
}
@@ -1625,6 +1641,44 @@ zl3073x_dpll_pin_free(struct zl3073x_dpll_pin *pin)
kfree(pin);
}
+/**
+ * zl3073x_dpll_pin_is_perout - check if output pin is perout-eligible
+ * @pin: DPLL pin to check
+ * @props: pin properties (already fetched by caller)
+ *
+ * An output pin is eligible for PTP periodic output if it is single-ended
+ * (not differential) and supports 1 Hz in its frequency list.
+ *
+ * Return: true if eligible, false otherwise
+ */
+static bool
+zl3073x_dpll_pin_is_perout(struct zl3073x_dpll_pin *pin,
+ const struct zl3073x_pin_props *props)
+{
+ struct zl3073x_dpll *zldpll = pin->dpll;
+ const struct zl3073x_chan *chan;
+ u8 out_id;
+ int i;
+
+ if (zl3073x_dpll_is_input_pin(pin))
+ return false;
+
+ out_id = zl3073x_output_pin_out_get(pin->id);
+ if (zl3073x_dev_out_is_diff(zldpll->dev, out_id))
+ return false;
+
+ chan = zl3073x_chan_state_get(zldpll->dev, zldpll->id);
+ if (!zl3073x_chan_is_out_stepped(chan, out_id))
+ return false;
+
+ for (i = 0; i < props->dpll_props.freq_supported_num; i++) {
+ if (props->dpll_props.freq_supported[i].min == 1)
+ return true;
+ }
+
+ return false;
+}
+
/**
* zl3073x_dpll_pin_register - register DPLL pin
* @pin: pointer to DPLL pin
@@ -1666,6 +1720,8 @@ zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index)
if (pin->prio == ZL_DPLL_REF_PRIO_NONE)
/* Clamp prio to max value */
pin->prio = ZL_DPLL_REF_PRIO_MAX;
+ } else if (zl3073x_dpll_pin_is_perout(pin, props)) {
+ pin->perout_idx = zldpll->ptp_info.n_per_out++;
}
/* Create or get existing DPLL pin */
@@ -1896,6 +1952,8 @@ zl3073x_dpll_pins_register(struct zl3073x_dpll *zldpll)
u8 id, index;
int rc;
+ zldpll->ptp_info.n_per_out = 0;
+
/* Process input pins */
for (index = 0; index < ZL3073X_NUM_PINS; index++) {
/* First input pins and then output pins */
@@ -2275,45 +2333,447 @@ zl3073x_dpll_init_fine_phase_adjust(struct zl3073x_dev *zldev)
return zl3073x_write_u8(zldev, ZL_REG_SYNTH_PHASE_SHIFT_CTRL, 0x01);
}
+/* Maximum frequency adjustment: +-1% of nominal in ppb */
+#define ZL3073X_DPLL_PTP_MAX_ADJ 10000000
+
/**
- * zl3073x_dpll_alloc - allocate DPLL device
- * @zldev: pointer to zl3073x device
- * @ch: DPLL channel number
+ * zl3073x_dpll_ptp_gettimex64 - read current time from ToD counters
+ * @info: PTP clock info
+ * @ts: timespec to store current time
+ * @sts: optional system timestamp pair for cross-timestamping
*
- * Allocates DPLL device structure for given DPLL channel.
+ * Return: 0 on success, <0 on error
+ */
+static int zl3073x_dpll_ptp_gettimex64(struct ptp_clock_info *info,
+ struct timespec64 *ts,
+ struct ptp_system_timestamp *sts)
+{
+ struct zl3073x_dpll *zldpll = container_of(info, struct zl3073x_dpll,
+ ptp_info);
+
+ guard(mutex)(&zldpll->lock);
+
+ return zl3073x_chan_tod_read(zldpll->dev, zldpll->id, false, ts, sts);
+}
+
+/**
+ * zl3073x_dpll_ptp_settime64 - set ToD counters to given time
+ * @info: PTP clock info
+ * @ts: timespec with time to set
*
- * Return: pointer to DPLL device on success, error pointer on error
+ * Return: 0 on success, <0 on error
*/
-struct zl3073x_dpll *
-zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch)
+static int zl3073x_dpll_ptp_settime64(struct ptp_clock_info *info,
+ const struct timespec64 *ts)
{
- struct zl3073x_dpll *zldpll;
+ struct zl3073x_dpll *zldpll = container_of(info, struct zl3073x_dpll,
+ ptp_info);
- zldpll = kzalloc_obj(*zldpll);
- if (!zldpll)
- return ERR_PTR(-ENOMEM);
+ guard(mutex)(&zldpll->lock);
- zldpll->dev = zldev;
- zldpll->id = ch;
- mutex_init(&zldpll->lock);
- INIT_LIST_HEAD(&zldpll->pins);
+ return zl3073x_chan_tod_write(zldpll->dev, zldpll->id, *ts);
+}
- return zldpll;
+/**
+ * zl3073x_dpll_ptp_adjtime_phase_step - adjust sub-second time via phase step
+ * @zldpll: DPLL channel
+ * @delta: time adjustment in nanoseconds (must be within (-NSEC_PER_SEC,
+ * NSEC_PER_SEC))
+ *
+ * Uses the output phase step mechanism with tod_step=1 to adjust both
+ * the output clock phase and the ToD counter simultaneously. This keeps
+ * outputs and ToD coherent. Only valid for NCO.
+ *
+ * Outputs are grouped by synthesizer since the phase step value is in
+ * synthesizer clock cycles. The first synth group with enabled outputs
+ * uses tod_step to adjust both outputs and the ToD counter. Remaining
+ * groups step outputs only. If no synth has enabled outputs, the ToD
+ * counter is stepped alone using an empty output mask (the FW uses the
+ * lowest-ID synth's period for the conversion).
+ *
+ * Return: 0 on success, -EOPNOTSUPP if no synths available, <0 on error
+ */
+static int zl3073x_dpll_ptp_adjtime_phase_step(struct zl3073x_dpll *zldpll,
+ s64 delta)
+{
+ u16 synth_mask[ZL3073X_NUM_SYNTHS] = {};
+ struct zl3073x_dev *zldev = zldpll->dev;
+ const struct zl3073x_synth *synth;
+ const struct zl3073x_chan *chan;
+ struct zl3073x_dpll_pin *pin;
+ u32 first_synth_freq = 0;
+ bool tod_stepped = false;
+ s32 step_cycles;
+ u32 synth_freq;
+ int rc;
+ u8 i;
+
+ chan = zl3073x_chan_state_get(zldev, zldpll->id);
+
+ /* Build per-synth output masks from registered output pins */
+ list_for_each_entry(pin, &zldpll->pins, list) {
+ u8 out_id, synth_id;
+
+ if (zl3073x_dpll_is_input_pin(pin))
+ continue;
+
+ out_id = zl3073x_output_pin_out_get(pin->id);
+
+ if (!zl3073x_chan_is_out_stepped(chan, out_id))
+ continue;
+
+ synth_id = zl3073x_dev_out_synth_get(zldev, out_id);
+ if (synth_id >= ZL3073X_NUM_SYNTHS) {
+ dev_warn(zldev->dev, "Unexpected synth id for OUT%u\n",
+ out_id);
+ continue;
+ }
+ synth_mask[synth_id] |= BIT(out_id);
+ }
+
+ /* Process each synth group */
+ for (i = 0; i < ZL3073X_NUM_SYNTHS; i++) {
+ synth = zl3073x_synth_state_get(zldev, i);
+ if (!zl3073x_synth_is_enabled(synth) ||
+ zl3073x_synth_dpll_get(synth) != zldpll->id)
+ continue;
+
+ synth_freq = zl3073x_synth_freq_get(synth);
+
+ /* Remember lowest-ID synth freq for ToD-only fallback */
+ if (!first_synth_freq)
+ first_synth_freq = synth_freq;
+
+ if (!synth_mask[i])
+ continue;
+
+ /* Safe for s32: max synth freq is 750 MHz */
+ step_cycles = div_s64(delta * synth_freq, NSEC_PER_SEC);
+
+ rc = zl3073x_chan_phase_step(zldev, zldpll->id,
+ synth_mask[i], step_cycles,
+ !tod_stepped);
+ if (rc) {
+ if (tod_stepped) {
+ dev_warn(zldev->dev,
+ "Partial phase step failure\n");
+ return 0;
+ }
+ return rc;
+ }
+ tod_stepped = true;
+ }
+
+ if (!first_synth_freq)
+ return -EOPNOTSUPP;
+
+ /* No enabled outputs found; step ToD counter only using the
+ * lowest-ID synth's period (empty output mask).
+ */
+ if (!tod_stepped) {
+ step_cycles = div_s64(delta * first_synth_freq, NSEC_PER_SEC);
+ return zl3073x_chan_phase_step(zldev, zldpll->id, 0,
+ step_cycles, true);
+ }
+
+ return 0;
}
/**
- * zl3073x_dpll_free - free DPLL device
- * @zldpll: pointer to zl3073x_dpll structure
+ * zl3073x_dpll_ptp_adjtime - adjust PTP clock time
+ * @info: PTP clock info
+ * @delta: time adjustment in nanoseconds
*
- * Deallocates given DPLL device previously allocated by @zl3073x_dpll_alloc.
+ * For NCO, large deltas (>= 1 second) are split into a ToD
+ * read-modify-write for the seconds part and an output phase step for
+ * the sub-second remainder. Sub-second deltas use phase step directly,
+ * falling back to ToD read-modify-write if phase step is unavailable.
+ * In AUTO/REFLOCK modes, large deltas are split into ToD
+ * read-modify-write for seconds and TIE write for the sub-second
+ * remainder. Sub-second deltas use TIE write directly.
+ *
+ * Return: 0 on success, <0 on error
*/
-void
-zl3073x_dpll_free(struct zl3073x_dpll *zldpll)
+static int zl3073x_dpll_ptp_adjtime(struct ptp_clock_info *info, s64 delta)
{
- WARN(zldpll->dpll_dev, "DPLL device is still registered\n");
+ struct zl3073x_dpll *zldpll = container_of(info, struct zl3073x_dpll,
+ ptp_info);
+ struct zl3073x_dev *zldev = zldpll->dev;
+ const struct zl3073x_chan *chan;
+ bool sec_adjusted = false;
+ struct timespec64 ts;
+ int rc;
- mutex_destroy(&zldpll->lock);
- kfree(zldpll);
+ if (!delta)
+ return 0;
+
+ guard(mutex)(&zldpll->lock);
+
+ /* Modes without phase step or TIE use plain ToD adjust */
+ chan = zl3073x_chan_state_get(zldev, zldpll->id);
+ if (!zl3073x_chan_mode_is_nco(chan) &&
+ !zl3073x_chan_mode_supports_tie(chan))
+ return zl3073x_chan_tod_adjust(zldev, zldpll->id,
+ ns_to_timespec64(delta));
+
+ /* Split off seconds via ToD read-modify-write so the sub-second
+ * remainder can be applied through the output-coherent mechanism
+ * (phase step or TIE write).
+ */
+ if (abs(delta) >= NSEC_PER_SEC) {
+ s32 remainder;
+
+ ts.tv_sec = div_s64_rem(delta, NSEC_PER_SEC, &remainder);
+ ts.tv_nsec = 0;
+ delta = remainder;
+
+ rc = zl3073x_chan_tod_adjust(zldev, zldpll->id, ts);
+ if (rc)
+ return rc;
+
+ sec_adjusted = true;
+
+ /* No sub-second remainder, done */
+ if (!delta)
+ return 0;
+ }
+
+ /* Apply sub-second delta via phase step (NCO) or TIE write */
+ if (zl3073x_chan_mode_is_nco(chan)) {
+ rc = zl3073x_dpll_ptp_adjtime_phase_step(zldpll, delta);
+ if (!rc)
+ return 0;
+ } else {
+ rc = zl3073x_chan_tie_write(zldev, zldpll->id, delta);
+ if (!rc)
+ return 0;
+ }
+
+ /* Phase step or TIE write failed, fall back to ToD adjust */
+ rc = zl3073x_chan_tod_adjust(zldev, zldpll->id,
+ ns_to_timespec64(delta));
+
+ /* In the unlikely event that both phase step/TIE write and fallback
+ * ToD adjust fail after seconds were already committed, return
+ * success to prevent the PTP servo from retrying the full delta and
+ * applying seconds again. The sub-second residual will self-correct
+ * in the next servo cycle.
+ */
+ if (rc && sec_adjusted) {
+ dev_warn(zldev->dev,
+ "Sub-second adjustment failed after seconds applied\n");
+ return 0;
+ }
+
+ return rc;
+}
+
+/**
+ * zl3073x_dpll_ptp_adjfine - adjust PTP clock frequency
+ * @info: PTP clock info
+ * @scaled_ppm: frequency adjustment in scaled ppm (ppm * 2^16)
+ *
+ * Only supported for NCO. Writes the delta frequency offset register.
+ *
+ * Return: 0 on success, -EOPNOTSUPP if NCO pin is not connected, <0 on error
+ */
+static int
+zl3073x_dpll_ptp_adjfine(struct ptp_clock_info *info, long scaled_ppm)
+{
+ struct zl3073x_dpll *zldpll = container_of(info, struct zl3073x_dpll,
+ ptp_info);
+ const struct zl3073x_chan *chan;
+ s64 offset;
+
+ /* Convert scaled_ppm to df_offset in 2^-48 steps:
+ * df_offset = -(scaled_ppm * 2^32) / 10^6
+ *
+ * Simplify to avoid overflow:
+ * df_offset = -(scaled_ppm * 2^26) / 5^6
+ * df_offset = -(scaled_ppm * 67108864) / 15625
+ */
+ offset = -div_s64((s64)scaled_ppm * 67108864LL, 15625);
+
+ guard(mutex)(&zldpll->lock);
+
+ chan = zl3073x_chan_state_get(zldpll->dev, zldpll->id);
+ if (!zl3073x_chan_mode_is_nco(chan))
+ return scaled_ppm ? -EOPNOTSUPP : 0;
+ if (offset == chan->df_offset)
+ return 0;
+
+ return zl3073x_chan_df_offset_set(zldpll->dev, zldpll->id, offset);
+}
+
+/**
+ * zl3073x_dpll_ptp_adjphase - adjust PTP clock phase
+ * @info: PTP clock info
+ * @delta: phase adjustment in nanoseconds
+ *
+ * Only supported in AUTO and REFLOCK modes. Uses TIE write for
+ * nanosecond resolution phase adjustment.
+ *
+ * Return: 0 on success, -EOPNOTSUPP if mode doesn't support TIE, <0 on error
+ */
+static int zl3073x_dpll_ptp_adjphase(struct ptp_clock_info *info, s32 delta)
+{
+ struct zl3073x_dpll *zldpll = container_of(info, struct zl3073x_dpll,
+ ptp_info);
+ struct zl3073x_dev *zldev = zldpll->dev;
+ const struct zl3073x_chan *chan;
+
+ if (!delta)
+ return 0;
+
+ guard(mutex)(&zldpll->lock);
+
+ chan = zl3073x_chan_state_get(zldev, zldpll->id);
+
+ if (!zl3073x_chan_mode_supports_tie(chan))
+ return -EOPNOTSUPP;
+
+ return zl3073x_chan_tie_write(zldev, zldpll->id, delta);
+}
+
+static s32
+zl3073x_dpll_ptp_getmaxphase(struct ptp_clock_info *info __always_unused)
+{
+ /* HW limits TIE write to +-1 second. Return the constant HW
+ * limit and let adjphase handle mode-specific checks.
+ */
+ return NSEC_PER_SEC - 1;
+}
+
+/**
+ * zl3073x_dpll_ptp_perout_find_pin - find pin by perout channel index
+ * @zldpll: DPLL channel
+ * @idx: perout channel index to find
+ *
+ * Return: pointer to the pin, or NULL if not found
+ */
+static struct zl3073x_dpll_pin *
+zl3073x_dpll_ptp_perout_find_pin(struct zl3073x_dpll *zldpll, int idx)
+{
+ struct zl3073x_dpll_pin *pin;
+
+ list_for_each_entry(pin, &zldpll->pins, list) {
+ if (pin->perout_idx == idx)
+ return pin;
+ }
+
+ return NULL;
+}
+
+/**
+ * zl3073x_dpll_ptp_enable - enable/disable PTP clock functions
+ * @info: PTP clock info
+ * @rq: the requested clock function and parameters
+ * @on: true to enable, false to disable
+ *
+ * Handles PTP_CLK_REQ_PEROUT requests. Only 1PPS (period = 1s) is supported.
+ * On enable, configures the output divider for 1 Hz. On disable, does nothing
+ * as the signal is not disabled.
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int zl3073x_dpll_ptp_enable(struct ptp_clock_info *info,
+ struct ptp_clock_request *rq, int on)
+{
+ struct zl3073x_dpll *zldpll = container_of(info, struct zl3073x_dpll,
+ ptp_info);
+ struct zl3073x_dpll_pin *pin;
+ unsigned int idx;
+ int rc;
+
+ if (rq->type != PTP_CLK_REQ_PEROUT)
+ return -EOPNOTSUPP;
+
+ idx = rq->perout.index;
+ if (idx >= info->n_per_out)
+ return -EINVAL;
+
+ /* Toggling output pins can produce runt pulses, so disable
+ * is a no-op.
+ */
+ if (!on)
+ return 0;
+
+ /* Only accept exactly 1PPS (period.sec == 1, nsec == 0) */
+ if (rq->perout.period.sec != 1 || rq->perout.period.nsec != 0)
+ return -EINVAL;
+
+ mutex_lock(&zldpll->lock);
+
+ pin = zl3073x_dpll_ptp_perout_find_pin(zldpll, idx);
+ if (!pin) {
+ mutex_unlock(&zldpll->lock);
+ return -EINVAL;
+ }
+
+ /* Already at 1 Hz, nothing to do */
+ if (zl3073x_dev_output_pin_freq_get(zldpll->dev, pin->id) == 1) {
+ mutex_unlock(&zldpll->lock);
+ return 0;
+ }
+
+ rc = __zl3073x_dpll_output_pin_frequency_set(zldpll, pin, 1);
+
+ mutex_unlock(&zldpll->lock);
+
+ if (!rc)
+ dpll_pin_change_ntf(pin->dpll_pin);
+
+ return rc;
+}
+
+static const struct ptp_clock_info zl3073x_dpll_ptp_clock_info = {
+ .owner = THIS_MODULE,
+ .max_adj = ZL3073X_DPLL_PTP_MAX_ADJ,
+ .gettimex64 = zl3073x_dpll_ptp_gettimex64,
+ .settime64 = zl3073x_dpll_ptp_settime64,
+ .adjtime = zl3073x_dpll_ptp_adjtime,
+ .adjfine = zl3073x_dpll_ptp_adjfine,
+ .adjphase = zl3073x_dpll_ptp_adjphase,
+ .getmaxphase = zl3073x_dpll_ptp_getmaxphase,
+ .enable = zl3073x_dpll_ptp_enable,
+};
+
+/**
+ * zl3073x_dpll_ptp_register - register PTP clock for a DPLL channel
+ * @zldpll: DPLL channel to register PTP clock for
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int zl3073x_dpll_ptp_register(struct zl3073x_dpll *zldpll)
+{
+ struct zl3073x_dev *zldev = zldpll->dev;
+ struct ptp_clock *ptp_clock;
+
+ snprintf(zldpll->ptp_info.name, sizeof(zldpll->ptp_info.name),
+ "zl3073x-dpll%u", zldpll->id);
+
+ ptp_clock = ptp_clock_register(&zldpll->ptp_info, zldev->dev);
+ if (IS_ERR(ptp_clock)) {
+ dev_err(zldev->dev, "Failed to register PTP clock for DPLL%u\n",
+ zldpll->id);
+ return PTR_ERR(ptp_clock);
+ }
+
+ zldpll->ptp_clock = ptp_clock;
+
+ return 0;
+}
+
+/**
+ * zl3073x_dpll_ptp_unregister - unregister PTP clock for a DPLL channel
+ * @zldpll: DPLL channel to unregister PTP clock for
+ */
+static void zl3073x_dpll_ptp_unregister(struct zl3073x_dpll *zldpll)
+{
+ if (!IS_ERR_OR_NULL(zldpll->ptp_clock)) {
+ ptp_clock_unregister(zldpll->ptp_clock);
+ zldpll->ptp_clock = NULL;
+ }
}
/**
@@ -2398,6 +2858,48 @@ zl3073x_dpll_ref_sync_pairs_register(struct zl3073x_dpll *zldpll)
return 0;
}
+/**
+ * zl3073x_dpll_alloc - allocate DPLL device
+ * @zldev: pointer to zl3073x device
+ * @ch: DPLL channel number
+ *
+ * Allocates DPLL device structure for given DPLL channel.
+ *
+ * Return: pointer to DPLL device on success, error pointer on error
+ */
+struct zl3073x_dpll *
+zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch)
+{
+ struct zl3073x_dpll *zldpll;
+
+ zldpll = kzalloc_obj(*zldpll);
+ if (!zldpll)
+ return ERR_PTR(-ENOMEM);
+
+ zldpll->dev = zldev;
+ zldpll->id = ch;
+ zldpll->ptp_info = zl3073x_dpll_ptp_clock_info;
+ mutex_init(&zldpll->lock);
+ INIT_LIST_HEAD(&zldpll->pins);
+
+ return zldpll;
+}
+
+/**
+ * zl3073x_dpll_free - free DPLL device
+ * @zldpll: pointer to zl3073x_dpll structure
+ *
+ * Deallocates given DPLL device previously allocated by @zl3073x_dpll_alloc.
+ */
+void
+zl3073x_dpll_free(struct zl3073x_dpll *zldpll)
+{
+ WARN(zldpll->dpll_dev, "DPLL device is still registered\n");
+
+ mutex_destroy(&zldpll->lock);
+ kfree(zldpll);
+}
+
/**
* zl3073x_dpll_register - register DPLL device and all its pins
* @zldpll: pointer to zl3073x_dpll structure
@@ -2428,6 +2930,13 @@ zl3073x_dpll_register(struct zl3073x_dpll *zldpll)
return rc;
}
+ rc = zl3073x_dpll_ptp_register(zldpll);
+ if (rc) {
+ zl3073x_dpll_pins_unregister(zldpll);
+ zl3073x_dpll_device_unregister(zldpll);
+ return rc;
+ }
+
return 0;
}
@@ -2441,7 +2950,7 @@ zl3073x_dpll_register(struct zl3073x_dpll *zldpll)
void
zl3073x_dpll_unregister(struct zl3073x_dpll *zldpll)
{
- /* Unregister all pins and dpll */
+ zl3073x_dpll_ptp_unregister(zldpll);
zl3073x_dpll_pins_unregister(zldpll);
zl3073x_dpll_device_unregister(zldpll);
}
diff --git a/drivers/dpll/zl3073x/dpll.h b/drivers/dpll/zl3073x/dpll.h
index faebc402ba1b7..993221dc63249 100644
--- a/drivers/dpll/zl3073x/dpll.h
+++ b/drivers/dpll/zl3073x/dpll.h
@@ -5,6 +5,7 @@
#include <linux/dpll.h>
#include <linux/list.h>
+#include <linux/ptp_clock_kernel.h>
#include "core.h"
@@ -22,6 +23,8 @@
* @type: DPLL type (PPS or EEC)
* @lock_status: last saved DPLL lock status
* @pins: list of pins
+ * @ptp_info: PTP clock info
+ * @ptp_clock: registered PTP clock (or NULL)
*/
struct zl3073x_dpll {
struct list_head list;
@@ -36,6 +39,8 @@ struct zl3073x_dpll {
enum dpll_type type;
enum dpll_lock_status lock_status;
struct list_head pins;
+ struct ptp_clock_info ptp_info;
+ struct ptp_clock *ptp_clock;
};
struct zl3073x_dpll *zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch);
diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h
index b70ead7d4495b..447757e99845d 100644
--- a/drivers/dpll/zl3073x/regs.h
+++ b/drivers/dpll/zl3073x/regs.h
@@ -179,6 +179,20 @@
#define ZL_DPLL_DF_READ_CMD GENMASK(2, 0)
#define ZL_DPLL_DF_READ_CMD_ACC_I 4
+#define ZL_REG_DPLL_TIE_CTRL ZL_REG(5, 0x30, 1)
+#define ZL_DPLL_TIE_CTRL_OP GENMASK(2, 0)
+#define ZL_DPLL_TIE_CTRL_OP_WR 4
+
+#define ZL_REG_DPLL_TIE_CTRL_MASK ZL_REG(5, 0x31, 1)
+
+#define ZL_REG_DPLL_TOD_CTRL(_idx) \
+ ZL_REG_IDX(_idx, 5, 0x38, 1, 8, 1)
+#define ZL_DPLL_TOD_CTRL_SEM BIT(4)
+#define ZL_DPLL_TOD_CTRL_CMD GENMASK(3, 0)
+#define ZL_DPLL_TOD_CTRL_CMD_WR_NEXT_1HZ 1
+#define ZL_DPLL_TOD_CTRL_CMD_RD_CURRENT 8
+#define ZL_DPLL_TOD_CTRL_CMD_RD_NEXT_1HZ 9
+
#define ZL_REG_DPLL_MEAS_CTRL ZL_REG(5, 0x50, 1)
#define ZL_DPLL_MEAS_CTRL_EN BIT(0)
#define ZL_DPLL_MEAS_CTRL_AVG_FACTOR GENMASK(7, 4)
@@ -193,6 +207,9 @@
/*******************************
* Register Pages 6-7, DPLL Data
+ *
+ * Per-channel registers with stride 0x20. Channels 0-3 reside on page 6,
+ * channel 4 on page 7.
*******************************/
#define ZL_REG_DPLL_DF_OFFSET_03(_idx) \
@@ -202,6 +219,24 @@
((_idx) < 4 ? ZL_REG_DPLL_DF_OFFSET_03(_idx) : ZL_REG_DPLL_DF_OFFSET_4)
#define ZL_DPLL_DF_OFFSET_UNKNOWN S64_MIN
+#define ZL_REG_DPLL_TIE_DATA_03(_idx) \
+ ZL_REG_IDX(_idx, 6, 0x0C, 6, 4, 0x20)
+#define ZL_REG_DPLL_TIE_DATA_4 ZL_REG(7, 0x0C, 6)
+#define ZL_REG_DPLL_TIE_DATA(_idx) \
+ ((_idx) < 4 ? ZL_REG_DPLL_TIE_DATA_03(_idx) : ZL_REG_DPLL_TIE_DATA_4)
+
+#define ZL_REG_DPLL_TOD_SEC_03(_idx) \
+ ZL_REG_IDX(_idx, 6, 0x12, 6, 4, 0x20)
+#define ZL_REG_DPLL_TOD_SEC_4 ZL_REG(7, 0x12, 6)
+#define ZL_REG_DPLL_TOD_SEC(_idx) \
+ ((_idx) < 4 ? ZL_REG_DPLL_TOD_SEC_03(_idx) : ZL_REG_DPLL_TOD_SEC_4)
+
+#define ZL_REG_DPLL_TOD_NS_03(_idx) \
+ ZL_REG_IDX(_idx, 6, 0x18, 4, 4, 0x20)
+#define ZL_REG_DPLL_TOD_NS_4 ZL_REG(7, 0x18, 4)
+#define ZL_REG_DPLL_TOD_NS(_idx) \
+ ((_idx) < 4 ? ZL_REG_DPLL_TOD_NS_03(_idx) : ZL_REG_DPLL_TOD_NS_4)
+
/***********************************
* Register Page 9, Synth and Output
***********************************/
@@ -221,6 +256,8 @@
#define ZL_OUTPUT_CTRL_EN BIT(0)
#define ZL_OUTPUT_CTRL_SYNTH_SEL GENMASK(6, 4)
+#define ZL_REG_OUTPUT_STEP_TIME_MASK ZL_REG(9, 0x36, 2)
+
/*******************************
* Register Page 10, Ref Mailbox
*******************************/
@@ -316,6 +353,25 @@
#define ZL_REG_OUTPUT_ESYNC_WIDTH ZL_REG(14, 0x18, 4)
#define ZL_REG_OUTPUT_PHASE_COMP ZL_REG(14, 0x20, 4)
+/***********************************
+ * Register Page 9, Output Phase Step
+ ***********************************/
+
+#define ZL_REG_OUTPUT_PHASE_STEP_CTRL ZL_REG(9, 0x38, 1)
+#define ZL_OUTPUT_PHASE_STEP_CTRL_DPLL GENMASK(6, 4)
+#define ZL_OUTPUT_PHASE_STEP_CTRL_TOD_STEP BIT(3)
+#define ZL_OUTPUT_PHASE_STEP_CTRL_OP GENMASK(1, 0)
+#define ZL_OUTPUT_PHASE_STEP_CTRL_OP_NONE 0
+#define ZL_OUTPUT_PHASE_STEP_CTRL_OP_RESET 1
+#define ZL_OUTPUT_PHASE_STEP_CTRL_OP_READ 2
+#define ZL_OUTPUT_PHASE_STEP_CTRL_OP_WRITE 3
+
+#define ZL_REG_OUTPUT_PHASE_STEP_NUMBER ZL_REG(9, 0x39, 1)
+
+#define ZL_REG_OUTPUT_PHASE_STEP_MASK ZL_REG(9, 0x3a, 2)
+
+#define ZL_REG_OUTPUT_PHASE_STEP_DATA ZL_REG(9, 0x3c, 4)
+
/*
* Register Page 255 - HW registers access
*/
|
| 3/2 | 2026/08/01 02:52 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 20361 output: 20 thoughts: 496 Thoughts: **Reaching the PTP Clock Patch in a Virtualized Environment** Okay, so I've got this patch for PTP clock support for the Microchip Azurite DPLL driver (`drivers/dpll/zl3073x/`). It looks solid: adds the `PTP_1588_CLOCK` dependency in `Kconfig`, the necessary ToD functions, phase/frequency adjustment, and handles the 1PPS. The driver registers the PTP clock via `ptp_clock_register` so now the question is can I reach it in a virtualized environment. My initial thought is to figure out if syzkaller can reach it. The driver targets Microchip Azurite DPLL/PTP/SyncE devices, which are typically connected via I2C or SPI. Looking at the `Kconfig` for the driver, it confirms `ZL3073X_I2C` and `ZL3073X_SPI` as options. Now, I know I2C and SPI devices are a frequent target for syzkaller. I remember syzkaller can often handle these via pseudo-syscalls or by injecting I2C/SPI traffic. However, I'm thinking about the generic device instantiation. Syzkaller usually isn't great at instantiating *arbitrary* I2C/SPI devices unless they're explicitly described in a syz description, or if they're handled via device tree or sysfs. Wait, didn't I come across a way to do that in the past... Ah yes, I can instantiate I2C devices via `/sys/bus/i2c/devices/i2c-0/new_device` or similar, or potentially via netlink if it's a net device. This is a DPLL device though. So it will need to be probed via I2C. Thinking more about this, I can interact with the DPLL subsystem via netlink (Generic Netlink `dpll`). The tricky part is the device itself – can syzkaller actually probe this I2C/SPI device? Does syzkaller have something like `syz_i2c_setup`? No, that looks like it's used for setup purposes, I have to find a way to make the driver probe the device. It has `i2c-dev`, which provides a user-space interface to an I2C bus, but it doesn't probe the kernel driver. Okay, wait a second. I remember now: syzkaller can instantiate I2C devices by writing to `/sys/bus/i2c/devices/i2c-%d/new_device`. That could be the key! I should check the syzkaller descriptions to see if there's anything already available for DPLL or I2C instantiation. That's the first thing to check. |
| 4/2 | 2026/08/01 02:52 | tool | grepper |
0mArgs:{
"Expression": "zl3073x"
}Results: {
"Output": "\nFull output is too long, showing 500 out of 4601 lines.\nUse more precise expression if possible.\n\nDocumentation/networking/devlink/index.rst=77=parameters, info versions, and other features it supports.\n--\nDocumentation/networking/devlink/index.rst-104- ti-cpsw-switch\nDocumentation/networking/devlink/index.rst:105: zl3073x\n--\nDocumentation/networking/devlink/zl3073x.rst-3-=======================\nDocumentation/networking/devlink/zl3073x.rst:4:zl3073x devlink support\nDocumentation/networking/devlink/zl3073x.rst-5-=======================\nDocumentation/networking/devlink/zl3073x.rst-6-\nDocumentation/networking/devlink/zl3073x.rst:7:This document describes the devlink features implemented by the ``zl3073x``\nDocumentation/networking/devlink/zl3073x.rst-8-device driver.\n--\nDocumentation/networking/devlink/zl3073x.rst=24=Info versions\n--\nDocumentation/networking/devlink/zl3073x.rst-26-\nDocumentation/networking/devlink/zl3073x.rst:27:The ``zl3073x`` driver reports the following versions\nDocumentation/networking/devlink/zl3073x.rst-28-\n--\nDocumentation/networking/devlink/zl3073x.rst=53=Flash Update\n--\nDocumentation/networking/devlink/zl3073x.rst-55-\nDocumentation/networking/devlink/zl3073x.rst:56:The ``zl3073x`` driver implements support for flash update using the\nDocumentation/networking/devlink/zl3073x.rst-57-``devlink-flash`` interface. It supports updating the device flash using a\n--\nMAINTAINERS=17860=F:\tDocumentation/devicetree/bindings/dpll/microchip,zl30731.yaml\nMAINTAINERS:17861:F:\tdrivers/dpll/zl3073x/\nMAINTAINERS-17862-\n--\ndrivers/dpll/Kconfig=11=config DPLL_REFCNT_TRACKER\n--\ndrivers/dpll/Kconfig-25-\ndrivers/dpll/Kconfig:26:source \"drivers/dpll/zl3073x/Kconfig\"\ndrivers/dpll/Kconfig-27-\n--\ndrivers/dpll/Makefile=9=dpll-y += dpll_nl.o\ndrivers/dpll/Makefile-10-\ndrivers/dpll/Makefile:11:obj-$(CONFIG_ZL3073X)\t+= zl3073x/\n--\ndrivers/dpll/zl3073x/Kconfig=3=config ZL3073X\n--\ndrivers/dpll/zl3073x/Kconfig-14-\t To compile this driver as a module, choose M here. The module\ndrivers/dpll/zl3073x/Kconfig:15:\t will be called zl3073x.\ndrivers/dpll/zl3073x/Kconfig-16-\ndrivers/dpll/zl3073x/Kconfig=17=config ZL3073X_I2C\n--\ndrivers/dpll/zl3073x/Kconfig-26-\t To compile this driver as a module, choose M here: the module will\ndrivers/dpll/zl3073x/Kconfig:27:\t be called zl3073x_i2c.\ndrivers/dpll/zl3073x/Kconfig-28-\ndrivers/dpll/zl3073x/Kconfig=29=config ZL3073X_SPI\n--\ndrivers/dpll/zl3073x/Kconfig-38-\t To compile this driver as a module, choose M here: the module will\ndrivers/dpll/zl3073x/Kconfig:39:\t be called zl3073x_spi.\n--\ndrivers/dpll/zl3073x/Makefile-2-\ndrivers/dpll/zl3073x/Makefile:3:obj-$(CONFIG_ZL3073X)\t\t+= zl3073x.o\ndrivers/dpll/zl3073x/Makefile:4:zl3073x-objs\t\t\t:= chan.o core.o devlink.o dpll.o\t\\\ndrivers/dpll/zl3073x/Makefile-5-\t\t\t\t flash.o fw.o out.o prop.o ref.o synth.o\ndrivers/dpll/zl3073x/Makefile-6-\ndrivers/dpll/zl3073x/Makefile:7:obj-$(CONFIG_ZL3073X_I2C)\t+= zl3073x_i2c.o\ndrivers/dpll/zl3073x/Makefile:8:zl3073x_i2c-objs\t\t:= i2c.o\ndrivers/dpll/zl3073x/Makefile-9-\ndrivers/dpll/zl3073x/Makefile:10:obj-$(CONFIG_ZL3073X_SPI)\t+= zl3073x_spi.o\ndrivers/dpll/zl3073x/Makefile:11:zl3073x_spi-objs\t\t:= spi.o\n--\ndrivers/dpll/zl3073x/chan.c-13-/**\ndrivers/dpll/zl3073x/chan.c:14: * zl3073x_chan_state_update - update DPLL channel status from HW\ndrivers/dpll/zl3073x/chan.c:15: * @zldev: pointer to zl3073x_dev structure\ndrivers/dpll/zl3073x/chan.c-16- * @index: DPLL channel index\n--\ndrivers/dpll/zl3073x/chan.c-19- */\ndrivers/dpll/zl3073x/chan.c:20:int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index)\ndrivers/dpll/zl3073x/chan.c-21-{\ndrivers/dpll/zl3073x/chan.c:22:\tstruct zl3073x_chan *chan = \u0026zldev-\u003echan[index];\ndrivers/dpll/zl3073x/chan.c-23-\tu64 val;\n--\ndrivers/dpll/zl3073x/chan.c-25-\ndrivers/dpll/zl3073x/chan.c:26:\trc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MON_STATUS(index),\ndrivers/dpll/zl3073x/chan.c-27-\t\t\t \u0026chan-\u003emon_status);\n--\ndrivers/dpll/zl3073x/chan.c-30-\ndrivers/dpll/zl3073x/chan.c:31:\trc = zl3073x_read_u8(zldev, ZL_REG_DPLL_REFSEL_STATUS(index),\ndrivers/dpll/zl3073x/chan.c-32-\t\t\t \u0026chan-\u003erefsel_status);\n--\ndrivers/dpll/zl3073x/chan.c-38-\t */\ndrivers/dpll/zl3073x/chan.c:39:\tif (!zl3073x_chan_is_locked(chan)) {\ndrivers/dpll/zl3073x/chan.c:40:\t\tif (!zl3073x_chan_mode_is_nco(chan))\ndrivers/dpll/zl3073x/chan.c-41-\t\t\tchan-\u003edf_offset = ZL_DPLL_DF_OFFSET_UNKNOWN;\n--\ndrivers/dpll/zl3073x/chan.c-44-\ndrivers/dpll/zl3073x/chan.c:45:\trc = zl3073x_poll_zero_u8(zldev, ZL_REG_DPLL_DF_READ(index),\ndrivers/dpll/zl3073x/chan.c-46-\t\t\t\t ZL_DPLL_DF_READ_SEM,\n--\ndrivers/dpll/zl3073x/chan.c-50-\ndrivers/dpll/zl3073x/chan.c:51:\trc = zl3073x_write_u8(zldev, ZL_REG_DPLL_DF_READ(index),\ndrivers/dpll/zl3073x/chan.c-52-\t\t\t ZL_DPLL_DF_READ_SEM | ZL_DPLL_DF_READ_REF_OFST);\n--\ndrivers/dpll/zl3073x/chan.c-55-\ndrivers/dpll/zl3073x/chan.c:56:\trc = zl3073x_poll_zero_u8(zldev, ZL_REG_DPLL_DF_READ(index),\ndrivers/dpll/zl3073x/chan.c-57-\t\t\t\t ZL_DPLL_DF_READ_SEM,\n--\ndrivers/dpll/zl3073x/chan.c-61-\ndrivers/dpll/zl3073x/chan.c:62:\trc = zl3073x_read_u48(zldev, ZL_REG_DPLL_DF_OFFSET(index), \u0026val);\ndrivers/dpll/zl3073x/chan.c-63-\tif (rc)\n--\ndrivers/dpll/zl3073x/chan.c-71-/**\ndrivers/dpll/zl3073x/chan.c:72: * zl3073x_chan_nco_mode_set - switch DPLL channel to NCO mode\ndrivers/dpll/zl3073x/chan.c:73: * @zldev: pointer to zl3073x_dev structure\ndrivers/dpll/zl3073x/chan.c-74- * @index: DPLL channel index\n--\ndrivers/dpll/zl3073x/chan.c-82- */\ndrivers/dpll/zl3073x/chan.c:83:int zl3073x_chan_nco_mode_set(struct zl3073x_dev *zldev, u8 index)\ndrivers/dpll/zl3073x/chan.c-84-{\ndrivers/dpll/zl3073x/chan.c:85:\tstruct zl3073x_chan *chan = \u0026zldev-\u003echan[index];\ndrivers/dpll/zl3073x/chan.c-86-\tu8 prev_mode, df_read;\n--\ndrivers/dpll/zl3073x/chan.c-89-\ndrivers/dpll/zl3073x/chan.c:90:\tprev_mode = zl3073x_chan_mode_get(chan);\ndrivers/dpll/zl3073x/chan.c-91-\n--\ndrivers/dpll/zl3073x/chan.c-96-\tif (prev_mode == ZL_DPLL_MODE_REFSEL_MODE_FREERUN) {\ndrivers/dpll/zl3073x/chan.c:97:\t\tzl3073x_chan_mode_set(chan, ZL_DPLL_MODE_REFSEL_MODE_NCO);\ndrivers/dpll/zl3073x/chan.c-98-\ndrivers/dpll/zl3073x/chan.c:99:\t\trc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index),\ndrivers/dpll/zl3073x/chan.c-100-\t\t\t\t chan-\u003emode_refsel);\ndrivers/dpll/zl3073x/chan.c-101-\t\tif (rc) {\ndrivers/dpll/zl3073x/chan.c:102:\t\t\tzl3073x_chan_mode_set(chan, prev_mode);\ndrivers/dpll/zl3073x/chan.c-103-\t\t\treturn rc;\n--\ndrivers/dpll/zl3073x/chan.c-120-\t\t FIELD_PREP(ZL_DPLL_DF_READ_CMD, ZL_DPLL_DF_READ_CMD_ACC_I);\ndrivers/dpll/zl3073x/chan.c:121:\trc = zl3073x_write_u8(zldev, ZL_REG_DPLL_DF_READ(index), df_read);\ndrivers/dpll/zl3073x/chan.c-122-\tif (rc)\n--\ndrivers/dpll/zl3073x/chan.c-130-\ndrivers/dpll/zl3073x/chan.c:131:\tzl3073x_chan_mode_set(chan, ZL_DPLL_MODE_REFSEL_MODE_NCO);\ndrivers/dpll/zl3073x/chan.c:132:\trc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index),\ndrivers/dpll/zl3073x/chan.c-133-\t\t\t chan-\u003emode_refsel);\ndrivers/dpll/zl3073x/chan.c-134-\tif (rc) {\ndrivers/dpll/zl3073x/chan.c:135:\t\tzl3073x_chan_mode_set(chan, prev_mode);\ndrivers/dpll/zl3073x/chan.c-136-\t\treturn rc;\n--\ndrivers/dpll/zl3073x/chan.c-147-\t */\ndrivers/dpll/zl3073x/chan.c:148:\trc = zl3073x_read_u48(zldev, ZL_REG_DPLL_DF_OFFSET(index), \u0026val);\ndrivers/dpll/zl3073x/chan.c-149-\tif (rc) {\n--\ndrivers/dpll/zl3073x/chan.c-161-/**\ndrivers/dpll/zl3073x/chan.c:162: * zl3073x_chan_state_fetch - fetch DPLL channel state from hardware\ndrivers/dpll/zl3073x/chan.c:163: * @zldev: pointer to zl3073x_dev structure\ndrivers/dpll/zl3073x/chan.c-164- * @index: DPLL channel index to fetch state for\n--\ndrivers/dpll/zl3073x/chan.c-170- */\ndrivers/dpll/zl3073x/chan.c:171:int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index)\ndrivers/dpll/zl3073x/chan.c-172-{\ndrivers/dpll/zl3073x/chan.c:173:\tstruct zl3073x_chan *chan = \u0026zldev-\u003echan[index];\ndrivers/dpll/zl3073x/chan.c-174-\tint rc, i;\ndrivers/dpll/zl3073x/chan.c-175-\ndrivers/dpll/zl3073x/chan.c:176:\trc = zl3073x_read_u8(zldev, ZL_REG_DPLL_CTRL(index), \u0026chan-\u003ectrl);\ndrivers/dpll/zl3073x/chan.c-177-\tif (rc)\n--\ndrivers/dpll/zl3073x/chan.c-179-\ndrivers/dpll/zl3073x/chan.c:180:\trc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index),\ndrivers/dpll/zl3073x/chan.c-181-\t\t\t \u0026chan-\u003emode_refsel);\n--\ndrivers/dpll/zl3073x/chan.c-185-\tdev_dbg(zldev-\u003edev, \"DPLL%u mode: %u, ref: %u\\n\", index,\ndrivers/dpll/zl3073x/chan.c:186:\t\tzl3073x_chan_mode_get(chan), zl3073x_chan_ref_get(chan));\ndrivers/dpll/zl3073x/chan.c-187-\ndrivers/dpll/zl3073x/chan.c:188:\trc = zl3073x_chan_state_update(zldev, index);\ndrivers/dpll/zl3073x/chan.c-189-\tif (rc)\n--\ndrivers/dpll/zl3073x/chan.c-195-\t */\ndrivers/dpll/zl3073x/chan.c:196:\tif (zl3073x_chan_mode_is_nco(chan))\ndrivers/dpll/zl3073x/chan.c-197-\t\tchan-\u003edf_offset = ZL_DPLL_DF_OFFSET_UNKNOWN;\n--\ndrivers/dpll/zl3073x/chan.c-200-\t\t\"DPLL%u lock_state: %u, ho: %u, sel_state: %u, sel_ref: %u\\n\",\ndrivers/dpll/zl3073x/chan.c:201:\t\tindex, zl3073x_chan_lock_state_get(chan),\ndrivers/dpll/zl3073x/chan.c:202:\t\tzl3073x_chan_is_ho_ready(chan) ? 1 : 0,\ndrivers/dpll/zl3073x/chan.c:203:\t\tzl3073x_chan_refsel_state_get(chan),\ndrivers/dpll/zl3073x/chan.c:204:\t\tzl3073x_chan_refsel_ref_get(chan));\ndrivers/dpll/zl3073x/chan.c-205-\ndrivers/dpll/zl3073x/chan.c:206:\trc = zl3073x_read_u16(zldev, ZL_REG_OUTPUT_STEP_TIME_MASK,\ndrivers/dpll/zl3073x/chan.c-207-\t\t\t \u0026chan-\u003eout_step_time_mask);\n--\ndrivers/dpll/zl3073x/chan.c-213-\t/* Read DPLL configuration from mailbox */\ndrivers/dpll/zl3073x/chan.c:214:\trc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD,\ndrivers/dpll/zl3073x/chan.c-215-\t\t\t ZL_REG_DPLL_MB_MASK, BIT(index));\n--\ndrivers/dpll/zl3073x/chan.c-220-\tfor (i = 0; i \u003c ARRAY_SIZE(chan-\u003eref_prio); i++) {\ndrivers/dpll/zl3073x/chan.c:221:\t\trc = zl3073x_read_u8(zldev, ZL_REG_DPLL_REF_PRIO(i),\ndrivers/dpll/zl3073x/chan.c-222-\t\t\t\t \u0026chan-\u003eref_prio[i]);\n--\ndrivers/dpll/zl3073x/chan.c-230-/**\ndrivers/dpll/zl3073x/chan.c:231: * zl3073x_chan_state_get - get current DPLL channel state\ndrivers/dpll/zl3073x/chan.c:232: * @zldev: pointer to zl3073x_dev structure\ndrivers/dpll/zl3073x/chan.c-233- * @index: DPLL channel index to get state for\n--\ndrivers/dpll/zl3073x/chan.c-236- */\ndrivers/dpll/zl3073x/chan.c:237:const struct zl3073x_chan *zl3073x_chan_state_get(struct zl3073x_dev *zldev,\ndrivers/dpll/zl3073x/chan.c-238-\t\t\t\t\t\t u8 index)\n--\ndrivers/dpll/zl3073x/chan.c-243-/**\ndrivers/dpll/zl3073x/chan.c:244: * zl3073x_chan_tod_ready_wait - wait for ToD semaphore to clear\ndrivers/dpll/zl3073x/chan.c:245: * @zldev: pointer to zl3073x device\ndrivers/dpll/zl3073x/chan.c-246- * @ch: DPLL channel index\n--\ndrivers/dpll/zl3073x/chan.c-252- */\ndrivers/dpll/zl3073x/chan.c:253:static int zl3073x_chan_tod_ready_wait(struct zl3073x_dev *zldev, u8 ch)\ndrivers/dpll/zl3073x/chan.c-254-{\n--\ndrivers/dpll/zl3073x/chan.c-258-\ndrivers/dpll/zl3073x/chan.c:259:\trc = zl3073x_read_u8(zldev, ZL_REG_DPLL_TOD_CTRL(ch), \u0026tod_ctrl);\ndrivers/dpll/zl3073x/chan.c-260-\tif (rc)\n--\ndrivers/dpll/zl3073x/chan.c-271-\ndrivers/dpll/zl3073x/chan.c:272:\trc = zl3073x_poll_zero_u8(zldev, ZL_REG_DPLL_TOD_CTRL(ch),\ndrivers/dpll/zl3073x/chan.c-273-\t\t\t\t ZL_DPLL_TOD_CTRL_SEM, timeout);\n--\ndrivers/dpll/zl3073x/chan.c-278-/**\ndrivers/dpll/zl3073x/chan.c:279: * zl3073x_chan_tod_ctrl - issue ToD command\ndrivers/dpll/zl3073x/chan.c:280: * @zldev: pointer to zl3073x device\ndrivers/dpll/zl3073x/chan.c-281- * @ch: DPLL channel index\n--\ndrivers/dpll/zl3073x/chan.c-289- */\ndrivers/dpll/zl3073x/chan.c:290:static int zl3073x_chan_tod_ctrl(struct zl3073x_dev *zldev, u8 ch, u8 cmd)\ndrivers/dpll/zl3073x/chan.c-291-{\ndrivers/dpll/zl3073x/chan.c:292:\treturn zl3073x_write_u8(zldev, ZL_REG_DPLL_TOD_CTRL(ch),\ndrivers/dpll/zl3073x/chan.c-293-\t\t\t\tZL_DPLL_TOD_CTRL_SEM | cmd);\n--\ndrivers/dpll/zl3073x/chan.c-296-/**\ndrivers/dpll/zl3073x/chan.c:297: * zl3073x_chan_tod_read - read ToD registers after issuing a command\ndrivers/dpll/zl3073x/chan.c:298: * @zldev: pointer to zl3073x device\ndrivers/dpll/zl3073x/chan.c-299- * @ch: DPLL channel index\n--\ndrivers/dpll/zl3073x/chan.c-303- *\ndrivers/dpll/zl3073x/chan.c:304: * Context: Caller must serialize all zl3073x_chan_tod_* calls externally.\ndrivers/dpll/zl3073x/chan.c-305- * Return: 0 on success, \u003c0 on error\ndrivers/dpll/zl3073x/chan.c-306- */\ndrivers/dpll/zl3073x/chan.c:307:int zl3073x_chan_tod_read(struct zl3073x_dev *zldev, u8 ch,\ndrivers/dpll/zl3073x/chan.c-308-\t\t\t bool next_hz, struct timespec64 *ts,\n--\ndrivers/dpll/zl3073x/chan.c-321-\t/* Wait for any previous ToD operation to complete */\ndrivers/dpll/zl3073x/chan.c:322:\trc = zl3073x_chan_tod_ready_wait(zldev, ch);\ndrivers/dpll/zl3073x/chan.c-323-\tif (rc)\n--\ndrivers/dpll/zl3073x/chan.c-326-\tptp_read_system_prets(sts);\ndrivers/dpll/zl3073x/chan.c:327:\trc = zl3073x_chan_tod_ctrl(zldev, ch, cmd);\ndrivers/dpll/zl3073x/chan.c-328-\tif (rc)\n--\ndrivers/dpll/zl3073x/chan.c-330-\ndrivers/dpll/zl3073x/chan.c:331:\trc = zl3073x_chan_tod_ready_wait(zldev, ch);\ndrivers/dpll/zl3073x/chan.c-332-\tif (rc)\n--\ndrivers/dpll/zl3073x/chan.c-335-\ndrivers/dpll/zl3073x/chan.c:336:\trc = zl3073x_read_u48(zldev, ZL_REG_DPLL_TOD_SEC(ch), \u0026sec);\ndrivers/dpll/zl3073x/chan.c-337-\tif (rc)\n--\ndrivers/dpll/zl3073x/chan.c-340-\t/* HW nanoseconds are always in [0, NSEC_PER_SEC) range */\ndrivers/dpll/zl3073x/chan.c:341:\trc = zl3073x_read_u32(zldev, ZL_REG_DPLL_TOD_NS(ch), \u0026nsec);\ndrivers/dpll/zl3073x/chan.c-342-\tif (rc)\n--\ndrivers/dpll/zl3073x/chan.c-351-/**\ndrivers/dpll/zl3073x/chan.c:352: * zl3073x_chan_tod_write - write ToD registers and trigger 1 Hz update\ndrivers/dpll/zl3073x/chan.c:353: * @zldev: pointer to zl3073x device\ndrivers/dpll/zl3073x/chan.c-354- * @ch: DPLL channel index\n--\ndrivers/dpll/zl3073x/chan.c-356- *\ndrivers/dpll/zl3073x/chan.c:357: * Context: Caller must serialize all zl3073x_chan_tod_* calls externally.\ndrivers/dpll/zl3073x/chan.c-358- * Return: 0 on success, \u003c0 on error\ndrivers/dpll/zl3073x/chan.c-359- */\ndrivers/dpll/zl3073x/chan.c:360:int zl3073x_chan_tod_write(struct zl3073x_dev *zldev, u8 ch,\ndrivers/dpll/zl3073x/chan.c-361-\t\t\t struct timespec64 ts)\n--\ndrivers/dpll/zl3073x/chan.c-365-\t/* Wait for any previous ToD operation to complete */\ndrivers/dpll/zl3073x/chan.c:366:\trc = zl3073x_chan_tod_ready_wait(zldev, ch);\ndrivers/dpll/zl3073x/chan.c-367-\tif (rc)\n--\ndrivers/dpll/zl3073x/chan.c-369-\ndrivers/dpll/zl3073x/chan.c:370:\trc = zl3073x_write_u48(zldev, ZL_REG_DPLL_TOD_SEC(ch), ts.tv_sec);\ndrivers/dpll/zl3073x/chan.c-371-\tif (rc)\n--\ndrivers/dpll/zl3073x/chan.c-373-\ndrivers/dpll/zl3073x/chan.c:374:\trc = zl3073x_write_u32(zldev, ZL_REG_DPLL_TOD_NS(ch), ts.tv_nsec);\ndrivers/dpll/zl3073x/chan.c-375-\tif (rc)\n--\ndrivers/dpll/zl3073x/chan.c-377-\ndrivers/dpll/zl3073x/chan.c:378:\treturn zl3073x_chan_tod_ctrl(zldev, ch,\ndrivers/dpll/zl3073x/chan.c-379-\t\t\t\t ZL_DPLL_TOD_CTRL_CMD_WR_NEXT_1HZ);\n--\ndrivers/dpll/zl3073x/chan.c-382-/**\ndrivers/dpll/zl3073x/chan.c:383: * zl3073x_chan_tod_adjust - atomic ToD read-modify-write with rollover guard\ndrivers/dpll/zl3073x/chan.c:384: * @zldev: pointer to zl3073x device\ndrivers/dpll/zl3073x/chan.c-385- * @ch: DPLL channel index\n--\ndrivers/dpll/zl3073x/chan.c-392- *\ndrivers/dpll/zl3073x/chan.c:393: * Context: Caller must serialize all zl3073x_chan_tod_* calls externally.\ndrivers/dpll/zl3073x/chan.c-394- * Return: 0 on success, \u003c0 on error\ndrivers/dpll/zl3073x/chan.c-395- */\ndrivers/dpll/zl3073x/chan.c:396:int zl3073x_chan_tod_adjust(struct zl3073x_dev *zldev, u8 ch,\ndrivers/dpll/zl3073x/chan.c-397-\t\t\t struct timespec64 delta)\n--\ndrivers/dpll/zl3073x/chan.c-403-\t/* Read predicted ToD at next 1 Hz tick */\ndrivers/dpll/zl3073x/chan.c:404:\trc = zl3073x_chan_tod_read(zldev, ch, true, \u0026ts_next, NULL);\ndrivers/dpll/zl3073x/chan.c-405-\tif (rc)\n--\ndrivers/dpll/zl3073x/chan.c-408-\t/* Read current ToD to determine remaining margin */\ndrivers/dpll/zl3073x/chan.c:409:\trc = zl3073x_chan_tod_read(zldev, ch, false, \u0026ts_cur, NULL);\ndrivers/dpll/zl3073x/chan.c-410-\tif (rc)\n--\ndrivers/dpll/zl3073x/chan.c-426-\t/* Write adjusted ToD back and wait for completion */\ndrivers/dpll/zl3073x/chan.c:427:\trc = zl3073x_chan_tod_write(zldev, ch, ts_next);\ndrivers/dpll/zl3073x/chan.c-428-\tif (rc)\n--\ndrivers/dpll/zl3073x/chan.c-430-\ndrivers/dpll/zl3073x/chan.c:431:\treturn zl3073x_chan_tod_ready_wait(zldev, ch);\ndrivers/dpll/zl3073x/chan.c-432-}\n--\ndrivers/dpll/zl3073x/chan.c-434-/**\ndrivers/dpll/zl3073x/chan.c:435: * zl3073x_chan_df_offset_set - write delta frequency offset to hardware\ndrivers/dpll/zl3073x/chan.c:436: * @zldev: pointer to zl3073x device\ndrivers/dpll/zl3073x/chan.c-437- * @ch: DPLL channel index\n--\ndrivers/dpll/zl3073x/chan.c-441- */\ndrivers/dpll/zl3073x/chan.c:442:int zl3073x_chan_df_offset_set(struct zl3073x_dev *zldev, u8 ch, s64 offset)\ndrivers/dpll/zl3073x/chan.c-443-{\n--\ndrivers/dpll/zl3073x/chan.c-445-\ndrivers/dpll/zl3073x/chan.c:446:\trc = zl3073x_write_u48(zldev, ZL_REG_DPLL_DF_OFFSET(ch), offset);\ndrivers/dpll/zl3073x/chan.c-447-\tif (!rc)\n--\ndrivers/dpll/zl3073x/chan.c-453-/**\ndrivers/dpll/zl3073x/chan.c:454: * zl3073x_chan_tie_write - adjust DPLL phase using TIE write\ndrivers/dpll/zl3073x/chan.c:455: * @zldev: pointer to zl3073x device\ndrivers/dpll/zl3073x/chan.c-456- * @ch: DPLL channel index\n--\ndrivers/dpll/zl3073x/chan.c-463- */\ndrivers/dpll/zl3073x/chan.c:464:int zl3073x_chan_tie_write(struct zl3073x_dev *zldev, u8 ch, s64 delta_ns)\ndrivers/dpll/zl3073x/chan.c-465-{\n--\ndrivers/dpll/zl3073x/chan.c-471-\t/* Wait for any previous TIE operation to complete */\ndrivers/dpll/zl3073x/chan.c:472:\trc = zl3073x_poll_zero_u8(zldev, ZL_REG_DPLL_TIE_CTRL,\ndrivers/dpll/zl3073x/chan.c-473-\t\t\t\t ZL_DPLL_TIE_CTRL_OP,\n--\ndrivers/dpll/zl3073x/chan.c-480-\ndrivers/dpll/zl3073x/chan.c:481:\trc = zl3073x_write_u48(zldev, ZL_REG_DPLL_TIE_DATA(ch), tie_data);\ndrivers/dpll/zl3073x/chan.c-482-\tif (rc)\n--\ndrivers/dpll/zl3073x/chan.c-484-\ndrivers/dpll/zl3073x/chan.c:485:\trc = zl3073x_write_u8(zldev, ZL_REG_DPLL_TIE_CTRL_MASK, BIT(ch));\ndrivers/dpll/zl3073x/chan.c-486-\tif (rc)\n--\ndrivers/dpll/zl3073x/chan.c-488-\ndrivers/dpll/zl3073x/chan.c:489:\treturn zl3073x_write_u8(zldev, ZL_REG_DPLL_TIE_CTRL,\ndrivers/dpll/zl3073x/chan.c-490-\t\t\t\tZL_DPLL_TIE_CTRL_OP_WR);\n--\ndrivers/dpll/zl3073x/chan.c-493-/**\ndrivers/dpll/zl3073x/chan.c:494: * zl3073x_chan_phase_step - execute one output phase step operation\ndrivers/dpll/zl3073x/chan.c:495: * @zldev: pointer to zl3073x device\ndrivers/dpll/zl3073x/chan.c-496- * @ch: DPLL channel index\n--\ndrivers/dpll/zl3073x/chan.c-505- */\ndrivers/dpll/zl3073x/chan.c:506:int zl3073x_chan_phase_step(struct zl3073x_dev *zldev, u8 ch,\ndrivers/dpll/zl3073x/chan.c-507-\t\t\t u16 out_mask, s32 step_cycles,\n--\ndrivers/dpll/zl3073x/chan.c-515-\t/* Wait for any previous phase step operation to complete */\ndrivers/dpll/zl3073x/chan.c:516:\trc = zl3073x_poll_zero_u8(zldev, ZL_REG_OUTPUT_PHASE_STEP_CTRL,\ndrivers/dpll/zl3073x/chan.c-517-\t\t\t\t ZL_OUTPUT_PHASE_STEP_CTRL_OP,\n--\ndrivers/dpll/zl3073x/chan.c-521-\ndrivers/dpll/zl3073x/chan.c:522:\trc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_PHASE_STEP_DATA,\ndrivers/dpll/zl3073x/chan.c-523-\t\t\t step_cycles);\n--\ndrivers/dpll/zl3073x/chan.c-526-\ndrivers/dpll/zl3073x/chan.c:527:\trc = zl3073x_write_u16(zldev, ZL_REG_OUTPUT_PHASE_STEP_MASK, out_mask);\ndrivers/dpll/zl3073x/chan.c-528-\tif (rc)\n--\ndrivers/dpll/zl3073x/chan.c-530-\ndrivers/dpll/zl3073x/chan.c:531:\trc = zl3073x_write_u8(zldev, ZL_REG_OUTPUT_PHASE_STEP_NUMBER, 1);\ndrivers/dpll/zl3073x/chan.c-532-\tif (rc)\n--\ndrivers/dpll/zl3073x/chan.c-540-\ndrivers/dpll/zl3073x/chan.c:541:\treturn zl3073x_write_u8(zldev, ZL_REG_OUTPUT_PHASE_STEP_CTRL, ctrl);\ndrivers/dpll/zl3073x/chan.c-542-}\n--\ndrivers/dpll/zl3073x/chan.c-544-/**\ndrivers/dpll/zl3073x/chan.c:545: * zl3073x_chan_state_set - commit DPLL channel state changes to hardware\ndrivers/dpll/zl3073x/chan.c:546: * @zldev: pointer to zl3073x_dev structure\ndrivers/dpll/zl3073x/chan.c-547- * @index: DPLL channel index to set state for\n--\ndrivers/dpll/zl3073x/chan.c-556- */\ndrivers/dpll/zl3073x/chan.c:557:int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index,\ndrivers/dpll/zl3073x/chan.c:558:\t\t\t const struct zl3073x_chan *chan)\ndrivers/dpll/zl3073x/chan.c-559-{\ndrivers/dpll/zl3073x/chan.c:560:\tstruct zl3073x_chan *dchan = \u0026zldev-\u003echan[index];\ndrivers/dpll/zl3073x/chan.c-561-\tint rc, i;\n--\ndrivers/dpll/zl3073x/chan.c-568-\tif (dchan-\u003ectrl != chan-\u003ectrl) {\ndrivers/dpll/zl3073x/chan.c:569:\t\trc = zl3073x_write_u8(zldev, ZL_REG_DPLL_CTRL(index),\ndrivers/dpll/zl3073x/chan.c-570-\t\t\t\t chan-\u003ectrl);\n--\ndrivers/dpll/zl3073x/chan.c-576-\tif (dchan-\u003emode_refsel != chan-\u003emode_refsel) {\ndrivers/dpll/zl3073x/chan.c:577:\t\trc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index),\ndrivers/dpll/zl3073x/chan.c-578-\t\t\t\t chan-\u003emode_refsel);\n--\ndrivers/dpll/zl3073x/chan.c-592-\t/* Read DPLL configuration into mailbox */\ndrivers/dpll/zl3073x/chan.c:593:\trc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD,\ndrivers/dpll/zl3073x/chan.c-594-\t\t\t ZL_REG_DPLL_MB_MASK, BIT(index));\n--\ndrivers/dpll/zl3073x/chan.c-600-\t\tif (dchan-\u003eref_prio[i] != chan-\u003eref_prio[i]) {\ndrivers/dpll/zl3073x/chan.c:601:\t\t\trc = zl3073x_write_u8(zldev,\ndrivers/dpll/zl3073x/chan.c-602-\t\t\t\t\t ZL_REG_DPLL_REF_PRIO(i),\n--\ndrivers/dpll/zl3073x/chan.c-609-\t/* Commit DPLL configuration */\ndrivers/dpll/zl3073x/chan.c:610:\trc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_WR,\ndrivers/dpll/zl3073x/chan.c-611-\t\t\t ZL_REG_DPLL_MB_MASK, BIT(index));\n--\ndrivers/dpll/zl3073x/chan.h=13=struct ptp_system_timestamp;\ndrivers/dpll/zl3073x/chan.h:14:struct zl3073x_dev;\ndrivers/dpll/zl3073x/chan.h-15-\ndrivers/dpll/zl3073x/chan.h-16-/**\ndrivers/dpll/zl3073x/chan.h:17: * struct zl3073x_chan - DPLL channel state\ndrivers/dpll/zl3073x/chan.h-18- * @ctrl: DPLL control register value\n--\ndrivers/dpll/zl3073x/chan.h-25- */\ndrivers/dpll/zl3073x/chan.h:26:struct zl3073x_chan {\ndrivers/dpll/zl3073x/chan.h-27-\tstruct_group(cfg,\n--\ndrivers/dpll/zl3073x/chan.h-41-\ndrivers/dpll/zl3073x/chan.h:42:int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index);\ndrivers/dpll/zl3073x/chan.h:43:const struct zl3073x_chan *zl3073x_chan_state_get(struct zl3073x_dev *zldev,\ndrivers/dpll/zl3073x/chan.h-44-\t\t\t\t\t\t u8 index);\ndrivers/dpll/zl3073x/chan.h:45:int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index,\ndrivers/dpll/zl3073x/chan.h:46:\t\t\t const struct zl3073x_chan *chan);\ndrivers/dpll/zl3073x/chan.h-47-\ndrivers/dpll/zl3073x/chan.h:48:int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index);\ndrivers/dpll/zl3073x/chan.h:49:int zl3073x_chan_nco_mode_set(struct zl3073x_dev *zldev, u8 index);\ndrivers/dpll/zl3073x/chan.h-50-\ndrivers/dpll/zl3073x/chan.h:51:int zl3073x_chan_tod_read(struct zl3073x_dev *zldev, u8 ch,\ndrivers/dpll/zl3073x/chan.h-52-\t\t\t bool next_hz, struct timespec64 *ts,\ndrivers/dpll/zl3073x/chan.h-53-\t\t\t struct ptp_system_timestamp *sts);\ndrivers/dpll/zl3073x/chan.h:54:int zl3073x_chan_tod_write(struct zl3073x_dev *zldev, u8 ch,\ndrivers/dpll/zl3073x/chan.h-55-\t\t\t struct timespec64 ts);\ndrivers/dpll/zl3073x/chan.h:56:int zl3073x_chan_tod_adjust(struct zl3073x_dev *zldev, u8 ch,\ndrivers/dpll/zl3073x/chan.h-57-\t\t\t struct timespec64 delta);\ndrivers/dpll/zl3073x/chan.h:58:int zl3073x_chan_phase_step(struct zl3073x_dev *zldev, u8 ch,\ndrivers/dpll/zl3073x/chan.h-59-\t\t\t u16 out_mask, s32 step_cycles, bool tod_step);\ndrivers/dpll/zl3073x/chan.h-60-\ndrivers/dpll/zl3073x/chan.h:61:int zl3073x_chan_df_offset_set(struct zl3073x_dev *zldev, u8 ch, s64 offset);\ndrivers/dpll/zl3073x/chan.h-62-\ndrivers/dpll/zl3073x/chan.h:63:int zl3073x_chan_tie_write(struct zl3073x_dev *zldev, u8 ch, s64 delta_ns);\ndrivers/dpll/zl3073x/chan.h-64-\ndrivers/dpll/zl3073x/chan.h-65-/**\ndrivers/dpll/zl3073x/chan.h:66: * zl3073x_chan_df_offset_get - get cached df_offset vs tracked reference\ndrivers/dpll/zl3073x/chan.h-67- * @chan: pointer to channel state\n--\ndrivers/dpll/zl3073x/chan.h=71=static inline s64\ndrivers/dpll/zl3073x/chan.h:72:zl3073x_chan_df_offset_get(const struct zl3073x_chan *chan)\ndrivers/dpll/zl3073x/chan.h-73-{\n--\ndrivers/dpll/zl3073x/chan.h-77-/**\ndrivers/dpll/zl3073x/chan.h:78: * zl3073x_chan_mode_get - get DPLL channel operating mode\ndrivers/dpll/zl3073x/chan.h-79- * @chan: pointer to channel state\n--\ndrivers/dpll/zl3073x/chan.h-82- */\ndrivers/dpll/zl3073x/chan.h:83:static inline u8 zl3073x_chan_mode_get(const struct zl3073x_chan *chan)\ndrivers/dpll/zl3073x/chan.h-84-{\n--\ndrivers/dpll/zl3073x/chan.h-88-/**\ndrivers/dpll/zl3073x/chan.h:89: * zl3073x_chan_ref_get - get manually selected reference\ndrivers/dpll/zl3073x/chan.h-90- * @chan: pointer to channel state\n--\ndrivers/dpll/zl3073x/chan.h-93- */\ndrivers/dpll/zl3073x/chan.h:94:static inline u8 zl3073x_chan_ref_get(const struct zl3073x_chan *chan)\ndrivers/dpll/zl3073x/chan.h-95-{\n--\ndrivers/dpll/zl3073x/chan.h-99-/**\ndrivers/dpll/zl3073x/chan.h:100: * zl3073x_chan_mode_set - set DPLL channel operating mode\ndrivers/dpll/zl3073x/chan.h-101- * @chan: pointer to channel state\n--\ndrivers/dpll/zl3073x/chan.h-103- */\ndrivers/dpll/zl3073x/chan.h:104:static inline void zl3073x_chan_mode_set(struct zl3073x_chan *chan, u8 mode)\ndrivers/dpll/zl3073x/chan.h-105-{\n--\ndrivers/dpll/zl3073x/chan.h-109-/**\ndrivers/dpll/zl3073x/chan.h:110: * zl3073x_chan_ref_set - set manually selected reference\ndrivers/dpll/zl3073x/chan.h-111- * @chan: pointer to channel state\n--\ndrivers/dpll/zl3073x/chan.h-113- */\ndrivers/dpll/zl3073x/chan.h:114:static inline void zl3073x_chan_ref_set(struct zl3073x_chan *chan, u8 ref)\ndrivers/dpll/zl3073x/chan.h-115-{\n--\ndrivers/dpll/zl3073x/chan.h-119-/**\ndrivers/dpll/zl3073x/chan.h:120: * zl3073x_chan_ref_prio_get - get reference priority\ndrivers/dpll/zl3073x/chan.h-121- * @chan: pointer to channel state\n--\ndrivers/dpll/zl3073x/chan.h=126=static inline u8\ndrivers/dpll/zl3073x/chan.h:127:zl3073x_chan_ref_prio_get(const struct zl3073x_chan *chan, u8 ref)\ndrivers/dpll/zl3073x/chan.h-128-{\n--\ndrivers/dpll/zl3073x/chan.h-137-/**\ndrivers/dpll/zl3073x/chan.h:138: * zl3073x_chan_ref_prio_set - set reference priority\ndrivers/dpll/zl3073x/chan.h-139- * @chan: pointer to channel state\n--\n\n"
} |
| 5/2 | 2026/08/01 02:52 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 35591 output: 21 thoughts: 87 Thoughts: **Analyzing the Microchip Azurite Driver** Okay, so I'm looking at this driver, specifically for Microchip Azurite DPLL/PTP/SyncE devices. My initial thought is, these things are typically instantiated using I2C or SPI communication protocols. Standard fare. But wait a minute... I remember something about `syzkaller` being able to instantiate I2C devices directly using the `/sys/bus/i2c/devices/i2c-%d/new_device` interface. That's interesting, and potentially relevant for fuzzing and testing this driver. The next logical step, before I dig too deeply, is to quickly check whether this driver utilizes the standard `i2c_driver` and `spi_driver` infrastructure. If it does, my understanding of the system is correct and these interfaces can likely be leveraged, potentially by syzkaller. If not, I need to understand how the driver handles hardware access more closely. I suspect this will be the case, and is the more likely answer, but I'll start with the simpler path, because it provides a good overview for me. |
| 6/2 | 2026/08/01 02:52 | tool | grepper |
0mArgs:{
"Expression": "module_i2c_driver"
}Results: {
"Output": "\nFull output is too long, showing 500 out of 8227 lines.\nUse more precise expression if possible.\n\nDocumentation/firmware-guide/acpi/enumeration.rst=310=input driver::\n--\nDocumentation/firmware-guide/acpi/enumeration.rst-328-\t};\nDocumentation/firmware-guide/acpi/enumeration.rst:329:\tmodule_i2c_driver(mpu3050_i2c_driver);\nDocumentation/firmware-guide/acpi/enumeration.rst-330-\n--\nDocumentation/i2c/writing-clients.rst=229=driver module is usually enough.\n--\nDocumentation/i2c/writing-clients.rst-244-\nDocumentation/i2c/writing-clients.rst:245: The module_i2c_driver() macro can be used to reduce above code.\nDocumentation/i2c/writing-clients.rst-246-\nDocumentation/i2c/writing-clients.rst:247: module_i2c_driver(foo_driver);\nDocumentation/i2c/writing-clients.rst-248-\n--\narch/powerpc/platforms/83xx/mcu_mpc8349emitx.c=214=static struct i2c_driver mcu_driver = {\n--\narch/powerpc/platforms/83xx/mcu_mpc8349emitx.c-223-\narch/powerpc/platforms/83xx/mcu_mpc8349emitx.c:224:module_i2c_driver(mcu_driver);\narch/powerpc/platforms/83xx/mcu_mpc8349emitx.c-225-\n--\ndrivers/auxdisplay/ht16k33.c=770=static struct i2c_driver ht16k33_driver = {\n--\ndrivers/auxdisplay/ht16k33.c-778-};\ndrivers/auxdisplay/ht16k33.c:779:module_i2c_driver(ht16k33_driver);\ndrivers/auxdisplay/ht16k33.c-780-\n--\ndrivers/auxdisplay/lcd2s.c=368=static struct i2c_driver lcd2s_i2c_driver = {\n--\ndrivers/auxdisplay/lcd2s.c-376-};\ndrivers/auxdisplay/lcd2s.c:377:module_i2c_driver(lcd2s_i2c_driver);\ndrivers/auxdisplay/lcd2s.c-378-\n--\ndrivers/auxdisplay/max6959.c=175=static struct i2c_driver max6959_i2c_driver = {\n--\ndrivers/auxdisplay/max6959.c-184-};\ndrivers/auxdisplay/max6959.c:185:module_i2c_driver(max6959_i2c_driver);\ndrivers/auxdisplay/max6959.c-186-\n--\ndrivers/char/ipmi/ipmb_dev_int.c=369=static struct i2c_driver ipmb_driver = {\n--\ndrivers/char/ipmi/ipmb_dev_int.c-377-};\ndrivers/char/ipmi/ipmb_dev_int.c:378:module_i2c_driver(ipmb_driver);\ndrivers/char/ipmi/ipmb_dev_int.c-379-\n--\ndrivers/char/ipmi/ipmi_ipmb.c=574=static struct i2c_driver ipmi_ipmb_driver = {\n--\ndrivers/char/ipmi/ipmi_ipmb.c-583-};\ndrivers/char/ipmi/ipmi_ipmb.c:584:module_i2c_driver(ipmi_ipmb_driver);\ndrivers/char/ipmi/ipmi_ipmb.c-585-\n--\ndrivers/char/ipmi/ssif_bmc.c=1256=kunit_test_suite(ssif_bmc_test_suite);\n--\ndrivers/char/ipmi/ssif_bmc.c-1258-\ndrivers/char/ipmi/ssif_bmc.c:1259:module_i2c_driver(ssif_bmc_driver);\ndrivers/char/ipmi/ssif_bmc.c-1260-\n--\ndrivers/char/tpm/st33zp24/i2c.c=156=static struct i2c_driver st33zp24_i2c_driver = {\n--\ndrivers/char/tpm/st33zp24/i2c.c-167-\ndrivers/char/tpm/st33zp24/i2c.c:168:module_i2c_driver(st33zp24_i2c_driver);\ndrivers/char/tpm/st33zp24/i2c.c-169-\n--\ndrivers/char/tpm/tpm_i2c_atmel.c=217=static struct i2c_driver i2c_atmel_driver = {\n--\ndrivers/char/tpm/tpm_i2c_atmel.c-227-\ndrivers/char/tpm/tpm_i2c_atmel.c:228:module_i2c_driver(i2c_atmel_driver);\ndrivers/char/tpm/tpm_i2c_atmel.c-229-\n--\ndrivers/char/tpm/tpm_i2c_infineon.c=720=static struct i2c_driver tpm_tis_i2c_driver = {\n--\ndrivers/char/tpm/tpm_i2c_infineon.c-730-\ndrivers/char/tpm/tpm_i2c_infineon.c:731:module_i2c_driver(tpm_tis_i2c_driver);\ndrivers/char/tpm/tpm_i2c_infineon.c-732-MODULE_AUTHOR(\"Peter Huewe \u003cpeter.huewe@infineon.com\u003e\");\n--\ndrivers/char/tpm/tpm_i2c_nuvoton.c=645=static struct i2c_driver i2c_nuvoton_driver = {\n--\ndrivers/char/tpm/tpm_i2c_nuvoton.c-655-\ndrivers/char/tpm/tpm_i2c_nuvoton.c:656:module_i2c_driver(i2c_nuvoton_driver);\ndrivers/char/tpm/tpm_i2c_nuvoton.c-657-\n--\ndrivers/char/tpm/tpm_tis_i2c.c=393=static struct i2c_driver tpm_tis_i2c_driver = {\n--\ndrivers/char/tpm/tpm_tis_i2c.c-402-};\ndrivers/char/tpm/tpm_tis_i2c.c:403:module_i2c_driver(tpm_tis_i2c_driver);\ndrivers/char/tpm/tpm_tis_i2c.c-404-\n--\ndrivers/char/tpm/tpm_tis_i2c_cr50.c=823=static struct i2c_driver cr50_i2c_driver = {\n--\ndrivers/char/tpm/tpm_tis_i2c_cr50.c-833-\ndrivers/char/tpm/tpm_tis_i2c_cr50.c:834:module_i2c_driver(cr50_i2c_driver);\ndrivers/char/tpm/tpm_tis_i2c_cr50.c-835-\n--\ndrivers/clk/clk-cdce706.c=687=static struct i2c_driver cdce706_i2c_driver = {\n--\ndrivers/clk/clk-cdce706.c-694-};\ndrivers/clk/clk-cdce706.c:695:module_i2c_driver(cdce706_i2c_driver);\ndrivers/clk/clk-cdce706.c-696-\n--\ndrivers/clk/clk-cdce925.c=846=static struct i2c_driver cdce925_driver = {\n--\ndrivers/clk/clk-cdce925.c-853-};\ndrivers/clk/clk-cdce925.c:854:module_i2c_driver(cdce925_driver);\ndrivers/clk/clk-cdce925.c-855-\n--\ndrivers/clk/clk-cs2000-cp.c=623=static struct i2c_driver cs2000_driver = {\n--\ndrivers/clk/clk-cs2000-cp.c-633-\ndrivers/clk/clk-cs2000-cp.c:634:module_i2c_driver(cs2000_driver);\ndrivers/clk/clk-cs2000-cp.c-635-\n--\ndrivers/clk/clk-max9485.c=380=static struct i2c_driver max9485_driver = {\n--\ndrivers/clk/clk-max9485.c-388-};\ndrivers/clk/clk-max9485.c:389:module_i2c_driver(max9485_driver);\ndrivers/clk/clk-max9485.c-390-\n--\ndrivers/clk/clk-renesas-pcie.c=414=static struct i2c_driver rs9_driver = {\n--\ndrivers/clk/clk-renesas-pcie.c-422-};\ndrivers/clk/clk-renesas-pcie.c:423:module_i2c_driver(rs9_driver);\ndrivers/clk/clk-renesas-pcie.c-424-\n--\ndrivers/clk/clk-si514.c=393=static struct i2c_driver si514_driver = {\n--\ndrivers/clk/clk-si514.c-400-};\ndrivers/clk/clk-si514.c:401:module_i2c_driver(si514_driver);\ndrivers/clk/clk-si514.c-402-\n--\ndrivers/clk/clk-si521xx.c=384=static struct i2c_driver si521xx_driver = {\n--\ndrivers/clk/clk-si521xx.c-392-};\ndrivers/clk/clk-si521xx.c:393:module_i2c_driver(si521xx_driver);\ndrivers/clk/clk-si521xx.c-394-\n--\ndrivers/clk/clk-si5341.c=1847=static struct i2c_driver si5341_driver = {\n--\ndrivers/clk/clk-si5341.c-1855-};\ndrivers/clk/clk-si5341.c:1856:module_i2c_driver(si5341_driver);\ndrivers/clk/clk-si5341.c-1857-\n--\ndrivers/clk/clk-si5351.c=1711=static struct i2c_driver si5351_driver = {\n--\ndrivers/clk/clk-si5351.c-1718-};\ndrivers/clk/clk-si5351.c:1719:module_i2c_driver(si5351_driver);\ndrivers/clk/clk-si5351.c-1720-\n--\ndrivers/clk/clk-si544.c=497=static struct i2c_driver si544_driver = {\n--\ndrivers/clk/clk-si544.c-504-};\ndrivers/clk/clk-si544.c:505:module_i2c_driver(si544_driver);\ndrivers/clk/clk-si544.c-506-\n--\ndrivers/clk/clk-si570.c=523=static struct i2c_driver si570_driver = {\n--\ndrivers/clk/clk-si570.c-530-};\ndrivers/clk/clk-si570.c:531:module_i2c_driver(si570_driver);\ndrivers/clk/clk-si570.c-532-\n--\ndrivers/clk/clk-versaclock3.c=1157=static struct i2c_driver vc3_driver = {\n--\ndrivers/clk/clk-versaclock3.c-1163-};\ndrivers/clk/clk-versaclock3.c:1164:module_i2c_driver(vc3_driver);\ndrivers/clk/clk-versaclock3.c-1165-\n--\ndrivers/clk/clk-versaclock5.c=1340=static struct i2c_driver vc5_driver = {\n--\ndrivers/clk/clk-versaclock5.c-1349-};\ndrivers/clk/clk-versaclock5.c:1350:module_i2c_driver(vc5_driver);\ndrivers/clk/clk-versaclock5.c-1351-\n--\ndrivers/clk/clk-versaclock7.c=1302=static struct i2c_driver vc7_i2c_driver = {\n--\ndrivers/clk/clk-versaclock7.c-1310-};\ndrivers/clk/clk-versaclock7.c:1311:module_i2c_driver(vc7_i2c_driver);\ndrivers/clk/clk-versaclock7.c-1312-\n--\ndrivers/dpll/zl3073x/i2c.c=48=static struct i2c_driver zl3073x_i2c_driver = {\n--\ndrivers/dpll/zl3073x/i2c.c-55-};\ndrivers/dpll/zl3073x/i2c.c:56:module_i2c_driver(zl3073x_i2c_driver);\ndrivers/dpll/zl3073x/i2c.c-57-\n--\ndrivers/extcon/extcon-lc824206xa.c=483=static struct i2c_driver lc824206xa_driver = {\n--\ndrivers/extcon/extcon-lc824206xa.c-490-\ndrivers/extcon/extcon-lc824206xa.c:491:module_i2c_driver(lc824206xa_driver);\ndrivers/extcon/extcon-lc824206xa.c-492-\n--\ndrivers/extcon/extcon-max14526.c=288=static struct i2c_driver max14526_driver = {\n--\ndrivers/extcon/extcon-max14526.c-296-};\ndrivers/extcon/extcon-max14526.c:297:module_i2c_driver(max14526_driver);\ndrivers/extcon/extcon-max14526.c-298-\n--\ndrivers/extcon/extcon-ptn5150.c=401=static struct i2c_driver ptn5150_i2c_driver = {\n--\ndrivers/extcon/extcon-ptn5150.c-409-};\ndrivers/extcon/extcon-ptn5150.c:410:module_i2c_driver(ptn5150_i2c_driver);\ndrivers/extcon/extcon-ptn5150.c-411-\n--\ndrivers/fsi/fsi-master-i2cr.c=302=static struct i2c_driver i2cr_driver = {\n--\ndrivers/fsi/fsi-master-i2cr.c-310-\ndrivers/fsi/fsi-master-i2cr.c:311:module_i2c_driver(i2cr_driver)\ndrivers/fsi/fsi-master-i2cr.c-312-\n--\ndrivers/gpio/gpio-adnp.c=514=static struct i2c_driver adnp_i2c_driver = {\n--\ndrivers/gpio/gpio-adnp.c-521-};\ndrivers/gpio/gpio-adnp.c:522:module_i2c_driver(adnp_i2c_driver);\ndrivers/gpio/gpio-adnp.c-523-\n--\ndrivers/gpio/gpio-ds4520.c=62=static struct i2c_driver ds4520_gpio_driver = {\n--\ndrivers/gpio/gpio-ds4520.c-69-};\ndrivers/gpio/gpio-ds4520.c:70:module_i2c_driver(ds4520_gpio_driver);\ndrivers/gpio/gpio-ds4520.c-71-\n--\ndrivers/gpio/gpio-fxl6408.c=158=static struct i2c_driver fxl6408_driver = {\n--\ndrivers/gpio/gpio-fxl6408.c-166-};\ndrivers/gpio/gpio-fxl6408.c:167:module_i2c_driver(fxl6408_driver);\ndrivers/gpio/gpio-fxl6408.c-168-\n--\ndrivers/gpio/gpio-gw-pld.c=123=static struct i2c_driver gw_pld_driver = {\n--\ndrivers/gpio/gpio-gw-pld.c-130-};\ndrivers/gpio/gpio-gw-pld.c:131:module_i2c_driver(gw_pld_driver);\ndrivers/gpio/gpio-gw-pld.c-132-\n--\ndrivers/gpio/gpio-pca9570.c=181=static struct i2c_driver pca9570_driver = {\n--\ndrivers/gpio/gpio-pca9570.c-188-};\ndrivers/gpio/gpio-pca9570.c:189:module_i2c_driver(pca9570_driver);\ndrivers/gpio/gpio-pca9570.c-190-\n--\ndrivers/gpio/gpio-tpic2810.c=120=static struct i2c_driver tpic2810_driver = {\n--\ndrivers/gpio/gpio-tpic2810.c-127-};\ndrivers/gpio/gpio-tpic2810.c:128:module_i2c_driver(tpic2810_driver);\ndrivers/gpio/gpio-tpic2810.c-129-\n--\ndrivers/gpio/gpio-ts4900.c=183=static struct i2c_driver ts4900_gpio_driver = {\n--\ndrivers/gpio/gpio-ts4900.c-190-};\ndrivers/gpio/gpio-ts4900.c:191:module_i2c_driver(ts4900_gpio_driver);\ndrivers/gpio/gpio-ts4900.c-192-\n--\ndrivers/gpio/gpio-waveshare-dsi.c=197=static struct i2c_driver waveshare_gpio_regulator_driver = {\n--\ndrivers/gpio/gpio-waveshare-dsi.c-204-\ndrivers/gpio/gpio-waveshare-dsi.c:205:module_i2c_driver(waveshare_gpio_regulator_driver);\ndrivers/gpio/gpio-waveshare-dsi.c-206-\n--\ndrivers/gpu/drm/bridge/analogix/analogix-anx6345.c=780=static struct i2c_driver anx6345_driver = {\n--\ndrivers/gpu/drm/bridge/analogix/analogix-anx6345.c-788-};\ndrivers/gpu/drm/bridge/analogix/analogix-anx6345.c:789:module_i2c_driver(anx6345_driver);\ndrivers/gpu/drm/bridge/analogix/analogix-anx6345.c-790-\n--\ndrivers/gpu/drm/bridge/analogix/analogix-anx78xx.c=1347=static struct i2c_driver anx78xx_driver = {\n--\ndrivers/gpu/drm/bridge/analogix/analogix-anx78xx.c-1354-};\ndrivers/gpu/drm/bridge/analogix/analogix-anx78xx.c:1355:module_i2c_driver(anx78xx_driver);\ndrivers/gpu/drm/bridge/analogix/analogix-anx78xx.c-1356-\n--\ndrivers/gpu/drm/bridge/analogix/anx7625.c=3008=static struct i2c_driver anx7625_driver = {\n--\ndrivers/gpu/drm/bridge/analogix/anx7625.c-3019-\ndrivers/gpu/drm/bridge/analogix/anx7625.c:3020:module_i2c_driver(anx7625_driver);\ndrivers/gpu/drm/bridge/analogix/anx7625.c-3021-\n--\ndrivers/gpu/drm/bridge/chrontel-ch7033.c=604=static struct i2c_driver ch7033_driver = {\n--\ndrivers/gpu/drm/bridge/chrontel-ch7033.c-613-\ndrivers/gpu/drm/bridge/chrontel-ch7033.c:614:module_i2c_driver(ch7033_driver);\ndrivers/gpu/drm/bridge/chrontel-ch7033.c-615-\n--\ndrivers/gpu/drm/bridge/cros-ec-anx7688.c=175=static struct i2c_driver cros_ec_anx7688_bridge_driver = {\n--\ndrivers/gpu/drm/bridge/cros-ec-anx7688.c-183-\ndrivers/gpu/drm/bridge/cros-ec-anx7688.c:184:module_i2c_driver(cros_ec_anx7688_bridge_driver);\ndrivers/gpu/drm/bridge/cros-ec-anx7688.c-185-\n--\ndrivers/gpu/drm/bridge/ite-it6263.c=918=static struct i2c_driver it6263_driver = {\n--\ndrivers/gpu/drm/bridge/ite-it6263.c-925-};\ndrivers/gpu/drm/bridge/ite-it6263.c:926:module_i2c_driver(it6263_driver);\ndrivers/gpu/drm/bridge/ite-it6263.c-927-\n--\ndrivers/gpu/drm/bridge/ite-it6505.c=3667=static struct i2c_driver it6505_i2c_driver = {\n--\ndrivers/gpu/drm/bridge/ite-it6505.c-3678-\ndrivers/gpu/drm/bridge/ite-it6505.c:3679:module_i2c_driver(it6505_i2c_driver);\ndrivers/gpu/drm/bridge/ite-it6505.c-3680-\n--\ndrivers/gpu/drm/bridge/ite-it66121.c=1727=static struct i2c_driver it66121_driver = {\n--\ndrivers/gpu/drm/bridge/ite-it66121.c-1736-\ndrivers/gpu/drm/bridge/ite-it66121.c:1737:module_i2c_driver(it66121_driver);\ndrivers/gpu/drm/bridge/ite-it66121.c-1738-\n--\ndrivers/gpu/drm/bridge/lontium-lt8713sx.c=581=static struct i2c_driver lt8713sx_driver = {\n--\ndrivers/gpu/drm/bridge/lontium-lt8713sx.c-591-\ndrivers/gpu/drm/bridge/lontium-lt8713sx.c:592:module_i2c_driver(lt8713sx_driver);\ndrivers/gpu/drm/bridge/lontium-lt8713sx.c-593-MODULE_LICENSE(\"GPL\");\n--\ndrivers/gpu/drm/bridge/lontium-lt8912b.c=824=static struct i2c_driver lt8912_i2c_driver = {\n--\ndrivers/gpu/drm/bridge/lontium-lt8912b.c-833-};\ndrivers/gpu/drm/bridge/lontium-lt8912b.c:834:module_i2c_driver(lt8912_i2c_driver);\ndrivers/gpu/drm/bridge/lontium-lt8912b.c-835-\n--\ndrivers/gpu/drm/bridge/lontium-lt9211.c=786=static struct i2c_driver lt9211_driver = {\n--\ndrivers/gpu/drm/bridge/lontium-lt9211.c-794-};\ndrivers/gpu/drm/bridge/lontium-lt9211.c:795:module_i2c_driver(lt9211_driver);\ndrivers/gpu/drm/bridge/lontium-lt9211.c-796-\n--\ndrivers/gpu/drm/bridge/lontium-lt9611.c=1243=static struct i2c_driver lt9611_driver = {\n--\ndrivers/gpu/drm/bridge/lontium-lt9611.c-1251-};\ndrivers/gpu/drm/bridge/lontium-lt9611.c:1252:module_i2c_driver(lt9611_driver);\ndrivers/gpu/drm/bridge/lontium-lt9611.c-1253-\n--\ndrivers/gpu/drm/bridge/lontium-lt9611uxc.c=909=static struct i2c_driver lt9611uxc_driver = {\n--\ndrivers/gpu/drm/bridge/lontium-lt9611uxc.c-918-};\ndrivers/gpu/drm/bridge/lontium-lt9611uxc.c:919:module_i2c_driver(lt9611uxc_driver);\ndrivers/gpu/drm/bridge/lontium-lt9611uxc.c-920-\n--\ndrivers/gpu/drm/bridge/nxp-ptn3460.c=332=static struct i2c_driver ptn3460_driver = {\n--\ndrivers/gpu/drm/bridge/nxp-ptn3460.c-340-};\ndrivers/gpu/drm/bridge/nxp-ptn3460.c:341:module_i2c_driver(ptn3460_driver);\ndrivers/gpu/drm/bridge/nxp-ptn3460.c-342-\n--\ndrivers/gpu/drm/bridge/parade-ps8622.c=538=static struct i2c_driver ps8622_driver = {\n--\ndrivers/gpu/drm/bridge/parade-ps8622.c-546-};\ndrivers/gpu/drm/bridge/parade-ps8622.c:547:module_i2c_driver(ps8622_driver);\ndrivers/gpu/drm/bridge/parade-ps8622.c-548-\n--\ndrivers/gpu/drm/bridge/parade-ps8640.c=739=static struct i2c_driver ps8640_driver = {\n--\ndrivers/gpu/drm/bridge/parade-ps8640.c-746-};\ndrivers/gpu/drm/bridge/parade-ps8640.c:747:module_i2c_driver(ps8640_driver);\ndrivers/gpu/drm/bridge/parade-ps8640.c-748-\n--\ndrivers/gpu/drm/bridge/sii902x.c=1245=static struct i2c_driver sii902x_driver = {\n--\ndrivers/gpu/drm/bridge/sii902x.c-1253-};\ndrivers/gpu/drm/bridge/sii902x.c:1254:module_i2c_driver(sii902x_driver);\ndrivers/gpu/drm/bridge/sii902x.c-1255-\n--\ndrivers/gpu/drm/bridge/sii9234.c=953=static struct i2c_driver sii9234_driver = {\n--\ndrivers/gpu/drm/bridge/sii9234.c-962-\ndrivers/gpu/drm/bridge/sii9234.c:963:module_i2c_driver(sii9234_driver);\ndrivers/gpu/drm/bridge/sii9234.c-964-MODULE_DESCRIPTION(\"Silicon Image SII9234 HDMI/MHL bridge driver\");\n--\ndrivers/gpu/drm/bridge/sil-sii8620.c=2378=static struct i2c_driver sii8620_driver = {\n--\ndrivers/gpu/drm/bridge/sil-sii8620.c-2387-\ndrivers/gpu/drm/bridge/sil-sii8620.c:2388:module_i2c_driver(sii8620_driver);\ndrivers/gpu/drm/bridge/sil-sii8620.c-2389-MODULE_DESCRIPTION(\"Silicon Image SiI8620 HDMI/MHL bridge driver\");\n--\ndrivers/gpu/drm/bridge/tc358767.c=2625=static struct i2c_driver tc358767_driver = {\n--\ndrivers/gpu/drm/bridge/tc358767.c-2633-};\ndrivers/gpu/drm/bridge/tc358767.c:2634:module_i2c_driver(tc358767_driver);\ndrivers/gpu/drm/bridge/tc358767.c-2635-\n--\ndrivers/gpu/drm/bridge/tc358768.c=1452=static struct i2c_driver tc358768_driver = {\n--\ndrivers/gpu/drm/bridge/tc358768.c-1460-};\ndrivers/gpu/drm/bridge/tc358768.c:1461:module_i2c_driver(tc358768_driver);\ndrivers/gpu/drm/bridge/tc358768.c-1462-\n--\ndrivers/gpu/drm/bridge/tc358775.c=743=static struct i2c_driver tc358775_driver = {\n--\ndrivers/gpu/drm/bridge/tc358775.c-751-};\ndrivers/gpu/drm/bridge/tc358775.c:752:module_i2c_driver(tc358775_driver);\ndrivers/gpu/drm/bridge/tc358775.c-753-\n--\ndrivers/gpu/drm/bridge/tda998x_drv.c=2067=static struct i2c_driver tda998x_driver = {\n--\ndrivers/gpu/drm/bridge/tda998x_drv.c-2076-\ndrivers/gpu/drm/bridge/tda998x_drv.c:2077:module_i2c_driver(tda998x_driver);\ndrivers/gpu/drm/bridge/tda998x_drv.c-2078-\n--\ndrivers/gpu/drm/bridge/ti-dlpc3433.c=403=static struct i2c_driver dlpc3433_driver = {\n--\ndrivers/gpu/drm/bridge/ti-dlpc3433.c-411-};\ndrivers/gpu/drm/bridge/ti-dlpc3433.c:412:module_i2c_driver(dlpc3433_driver);\ndrivers/gpu/drm/bridge/ti-dlpc3433.c-413-\n--\ndrivers/gpu/drm/bridge/ti-sn65dsi83.c=1089=static struct i2c_driver sn65dsi83_driver = {\n--\ndrivers/gpu/drm/bridge/ti-sn65dsi83.c-1097-};\ndrivers/gpu/drm/bridge/ti-sn65dsi83.c:1098:module_i2c_driver(sn65dsi83_driver);\ndrivers/gpu/drm/bridge/ti-sn65dsi83.c-1099-\n--\ndrivers/gpu/drm/bridge/ti-tdp158.c=105=static struct i2c_driver tdp158_driver = {\n--\ndrivers/gpu/drm/bridge/ti-tdp158.c-111-};\ndrivers/gpu/drm/bridge/ti-tdp158.c:112:module_i2c_driver(tdp158_driver);\ndrivers/gpu/drm/bridge/ti-tdp158.c-113-\n--\ndrivers/gpu/drm/bridge/waveshare-dsi.c=195=static struct i2c_driver ws_bridge_driver = {\n--\ndrivers/gpu/drm/bridge/waveshare-dsi.c-201-};\ndrivers/gpu/drm/bridge/waveshare-dsi.c:202:module_i2c_driver(ws_bridge_driver);\ndrivers/gpu/drm/bridge/waveshare-dsi.c-203-\n--\ndrivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c=261=static struct i2c_driver lcd_olinuxino_driver = {\n--\ndrivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c-269-\ndrivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c:270:module_i2c_driver(lcd_olinuxino_driver);\ndrivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c-271-\n--\ndrivers/gpu/drm/sitronix/st7571-i2c.c=143=static struct i2c_driver st7571_i2c_driver = {\n--\ndrivers/gpu/drm/sitronix/st7571-i2c.c-152-\ndrivers/gpu/drm/sitronix/st7571-i2c.c:153:module_i2c_driver(st7571_i2c_driver);\ndrivers/gpu/drm/sitronix/st7571-i2c.c-154-\n--\ndrivers/gpu/drm/solomon/ssd130x-i2c.c=112=static struct i2c_driver ssd130x_i2c_driver = {\n--\ndrivers/gpu/drm/solomon/ssd130x-i2c.c-120-};\ndrivers/gpu/drm/solomon/ssd130x-i2c.c:121:module_i2c_driver(ssd130x_i2c_driver);\ndrivers/gpu/drm/solomon/ssd130x-i2c.c-122-\n--\ndrivers/hid/i2c-hid/i2c-hid-acpi.c=126=static struct i2c_driver i2c_hid_acpi_driver = {\n--\ndrivers/hid/i2c-hid/i2c-hid-acpi.c-138-\ndrivers/hid/i2c-hid/i2c-hid-acpi.c:139:module_i2c_driver(i2c_hid_acpi_driver);\ndrivers/hid/i2c-hid/i2c-hid-acpi.c-140-\n--\ndrivers/hid/i2c-hid/i2c-hid-of-elan.c=216=static struct i2c_driver elan_i2c_hid_ts_driver = {\n--\ndrivers/hid/i2c-hid/i2c-hid-of-elan.c-226-};\ndrivers/hid/i2c-hid/i2c-hid-of-elan.c:227:module_i2c_driver(elan_i2c_hid_ts_driver);\ndrivers/hid/i2c-hid/i2c-hid-of-elan.c-228-\n--\ndrivers/hid/i2c-hid/i2c-hid-of-goodix.c=124=static struct i2c_driver goodix_i2c_hid_ts_driver = {\n--\ndrivers/hid/i2c-hid/i2c-hid-of-goodix.c-134-};\ndrivers/hid/i2c-hid/i2c-hid-of-goodix.c:135:module_i2c_driver(goodix_i2c_hid_ts_driver);\ndrivers/hid/i2c-hid/i2c-hid-of-goodix.c-136-\n--\ndrivers/hid/i2c-hid/i2c-hid-of.c=153=static struct i2c_driver i2c_hid_of_driver = {\n--\ndrivers/hid/i2c-hid/i2c-hid-of.c-166-\ndrivers/hid/i2c-hid/i2c-hid-of.c:167:module_i2c_driver(i2c_hid_of_driver);\ndrivers/hid/i2c-hid/i2c-hid-of.c-168-\n--\ndrivers/hwmon/ad7414.c=219=static struct i2c_driver ad7414_driver = {\n--\ndrivers/hwmon/ad7414.c-227-\ndrivers/hwmon/ad7414.c:228:module_i2c_driver(ad7414_driver);\ndrivers/hwmon/ad7414.c-229-\n--\ndrivers/hwmon/ad7418.c=299=static struct i2c_driver ad7418_driver = {\n--\ndrivers/hwmon/ad7418.c-307-\ndrivers/hwmon/ad7418.c:308:module_i2c_driver(ad7418_driver);\ndrivers/hwmon/ad7418.c-309-\n--\ndrivers/hwmon/adc128d818.c=494=static struct i2c_driver adc128_driver = {\n--\ndrivers/hwmon/adc128d818.c-505-\ndrivers/hwmon/adc128d818.c:506:module_i2c_driver(adc128_driver);\ndrivers/hwmon/adc128d818.c-507-\n--\ndrivers/hwmon/adm1025.c=557=static struct i2c_driver adm1025_driver = {\n--\ndrivers/hwmon/adm1025.c-567-\ndrivers/hwmon/adm1025.c:568:module_i2c_driver(adm1025_driver);\ndrivers/hwmon/adm1025.c-569-\n--\ndrivers/hwmon/adm1026.c=1865=static struct i2c_driver adm1026_driver = {\n--\ndrivers/hwmon/adm1026.c-1875-\ndrivers/hwmon/adm1026.c:1876:module_i2c_driver(adm1026_driver);\ndrivers/hwmon/adm1026.c-1877-\n--\ndrivers/hwmon/adm1029.c=390=static struct i2c_driver adm1029_driver = {\n--\ndrivers/hwmon/adm1029.c-400-\ndrivers/hwmon/adm1029.c:401:module_i2c_driver(adm1029_driver);\ndrivers/hwmon/adm1029.c-402-\n--\ndrivers/hwmon/adm1031.c=1064=static struct i2c_driver adm1031_driver = {\n--\ndrivers/hwmon/adm1031.c-1074-\ndrivers/hwmon/adm1031.c:1075:module_i2c_driver(adm1031_driver);\ndrivers/hwmon/adm1031.c-1076-\n--\n\n"
} |
| 7/2 | 2026/08/01 02:52 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 46621 output: 24 thoughts: 320 Thoughts: **Analyzing the Syzkaller Fuzzing Potential of the zl3073x Driver** Okay, so the zl3073x driver registers itself with the kernel through `module_i2c_driver` and `module_spi_driver`. The crucial question here is whether syzkaller can effectively fuzz these I2C and SPI drivers. I know syzkaller has `syz_i2c_setup` and `syz_spi_setup` which is a good sign, and the system bus offers the ability to instantiate via sysfs `/sys/bus/i2c/devices/i2c-0/new_device`. Wait, no, I just checked the kernel and I don't see `syz_i2c_setup` defined. But syzkaller *can* write the device name to `/sys/bus/i2c/devices/i2c-0/new_device`, which could work as well. My next step is to determine if the driver is even *reachable*. The patch modifies files like `drivers/dpll/zl3073x/chan.c`, `core.c`, and `dpll.c`. These changes are focused on adding PTP clock support to the DPLL driver, which suggests core functional logic is being modified. Now, a key question: can this driver even be reached within a virtualized environment like the one syzkaller uses? The driver is designed specifically for a Microchip Azurite DPLL device. If syzkaller can instantiate the driver through I2C/SPI, it should be able to reach the probe function. Then, I can interact with the driver through DPLL netlink and PTP ioctls. I should probably check if there's any hardware dependency that would prevent the driver from probing in a virtual environment. Let's see if there are specific register accesses or other device-specific requirements that could cause problems. |
| 8/2 | 2026/08/01 02:52 | tool | grepper |
0mArgs:{
"Expression": "zl3073x_dev_probe"
}Results: {
"Output": "drivers/dpll/zl3073x/core.c=925=zl3073x_devm_dpll_init(struct zl3073x_dev *zldev)\n--\ndrivers/dpll/zl3073x/core.c-972-/**\ndrivers/dpll/zl3073x/core.c:973: * zl3073x_dev_probe - initialize zl3073x device\ndrivers/dpll/zl3073x/core.c-974- * @zldev: pointer to zl3073x device\n--\ndrivers/dpll/zl3073x/core.c-979- */\ndrivers/dpll/zl3073x/core.c:980:int zl3073x_dev_probe(struct zl3073x_dev *zldev)\ndrivers/dpll/zl3073x/core.c-981-{\n--\ndrivers/dpll/zl3073x/core.c-1060-}\ndrivers/dpll/zl3073x/core.c:1061:EXPORT_SYMBOL_NS_GPL(zl3073x_dev_probe, \"ZL3073X\");\ndrivers/dpll/zl3073x/core.c-1062-\n--\ndrivers/dpll/zl3073x/core.h=104=struct zl3073x_dev *zl3073x_devm_alloc(struct device *dev);\ndrivers/dpll/zl3073x/core.h:105:int zl3073x_dev_probe(struct zl3073x_dev *zldev);\ndrivers/dpll/zl3073x/core.h-106-\n--\ndrivers/dpll/zl3073x/i2c.c=11=static int zl3073x_i2c_probe(struct i2c_client *client)\n--\ndrivers/dpll/zl3073x/i2c.c-24-\ndrivers/dpll/zl3073x/i2c.c:25:\treturn zl3073x_dev_probe(zldev);\ndrivers/dpll/zl3073x/i2c.c-26-}\n--\ndrivers/dpll/zl3073x/spi.c=11=static int zl3073x_spi_probe(struct spi_device *spi)\n--\ndrivers/dpll/zl3073x/spi.c-24-\ndrivers/dpll/zl3073x/spi.c:25:\treturn zl3073x_dev_probe(zldev);\ndrivers/dpll/zl3073x/spi.c-26-}\n"
} |
| 9/2 | 2026/08/01 02:52 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 48123 output: 45 thoughts: 33 Thoughts: Okay, let's dive into this. Here's how I'd approach this task, as if these were my internal thoughts: **Analyzing `zl3073x_dev_probe`'s Register Reading** Alright, my initial focus is on the `zl3073x_dev_probe` function. I need to quickly determine its hardware interaction strategy. Specifically, I'm interested in whether it explicitly reads hardware registers during its initialization or probing sequence. My hunch is, since this is a probe function, it probably does. The real question is, does it *validate* those register values? Does it have error handling in place that would result in a probe failure if the expected register values don't match what it reads? This is key. The presence or absence of this validation logic tells me a lot about the robustness of the driver's hardware detection and configuration approach. I'll be looking for comparisons against expected values, conditional branching based on register contents, and ultimately, any `return` statements that indicate a probing failure. I'll need to confirm that this is not a blind read and proceed setup, since a blind read could potentially lead to serious issues. |
| 10/2 | 2026/08/01 02:53 | tool | read-file |
0mArgs:{
"File": "drivers/dpll/zl3073x/core.c",
"FirstLine": 970,
"LineCount": 100
}Results: {
"Contents": " 970:\t}\n 971:\t\n 972:\t/**\n 973:\t * zl3073x_dev_probe - initialize zl3073x device\n 974:\t * @zldev: pointer to zl3073x device\n 975:\t *\n 976:\t * Common initialization of zl3073x device structure.\n 977:\t *\n 978:\t * Returns: 0 on success, \u003c0 on error\n 979:\t */\n 980:\tint zl3073x_dev_probe(struct zl3073x_dev *zldev)\n 981:\t{\n 982:\t\tu16 id, revision, fw_ver;\n 983:\t\tunsigned int i;\n 984:\t\tu32 cfg_ver;\n 985:\t\tint rc;\n 986:\t\n 987:\t\t/* Read chip ID */\n 988:\t\trc = zl3073x_read_u16(zldev, ZL_REG_ID, \u0026id);\n 989:\t\tif (rc)\n 990:\t\t\treturn rc;\n 991:\t\n 992:\t\t/* Detect chip variant */\n 993:\t\tfor (i = 0; i \u003c ARRAY_SIZE(zl3073x_chip_ids); i++) {\n 994:\t\t\tif (zl3073x_chip_ids[i].id == id)\n 995:\t\t\t\tbreak;\n 996:\t\t}\n 997:\t\n 998:\t\tif (i == ARRAY_SIZE(zl3073x_chip_ids))\n 999:\t\t\treturn dev_err_probe(zldev-\u003edev, -ENODEV,\n1000:\t\t\t\t\t \"Unknown chip ID: 0x%04x\\n\", id);\n1001:\t\n1002:\t\tzldev-\u003einfo = \u0026zl3073x_chip_ids[i];\n1003:\t\n1004:\t\t/* Read revision, firmware version and custom config version */\n1005:\t\trc = zl3073x_read_u16(zldev, ZL_REG_REVISION, \u0026revision);\n1006:\t\tif (rc)\n1007:\t\t\treturn rc;\n1008:\t\trc = zl3073x_read_u16(zldev, ZL_REG_FW_VER, \u0026fw_ver);\n1009:\t\tif (rc)\n1010:\t\t\treturn rc;\n1011:\t\trc = zl3073x_read_u32(zldev, ZL_REG_CUSTOM_CONFIG_VER, \u0026cfg_ver);\n1012:\t\tif (rc)\n1013:\t\t\treturn rc;\n1014:\t\n1015:\t\tdev_dbg(zldev-\u003edev, \"ChipID(%X), ChipRev(%X), FwVer(%u)\\n\", id,\n1016:\t\t\trevision, fw_ver);\n1017:\t\tdev_dbg(zldev-\u003edev, \"Custom config version: %lu.%lu.%lu.%lu\\n\",\n1018:\t\t\tFIELD_GET(GENMASK(31, 24), cfg_ver),\n1019:\t\t\tFIELD_GET(GENMASK(23, 16), cfg_ver),\n1020:\t\t\tFIELD_GET(GENMASK(15, 8), cfg_ver),\n1021:\t\t\tFIELD_GET(GENMASK(7, 0), cfg_ver));\n1022:\t\n1023:\t\t/* Generate random clock ID as the device has not such property that\n1024:\t\t * could be used for this purpose. A user can later change this value\n1025:\t\t * using devlink.\n1026:\t\t */\n1027:\t\tzldev-\u003eclock_id = get_random_u64();\n1028:\t\n1029:\t\t/* Default phase offset averaging factor */\n1030:\t\tzldev-\u003ephase_avg_factor = 2;\n1031:\t\n1032:\t\t/* Initialize mutex for operations where multiple reads, writes\n1033:\t\t * and/or polls are required to be done atomically.\n1034:\t\t */\n1035:\t\trc = devm_mutex_init(zldev-\u003edev, \u0026zldev-\u003emultiop_lock);\n1036:\t\tif (rc)\n1037:\t\t\treturn dev_err_probe(zldev-\u003edev, rc,\n1038:\t\t\t\t\t \"Failed to initialize mutex\\n\");\n1039:\t\trc = devm_mutex_init(zldev-\u003edev, \u0026zldev-\u003ephase_step_lock);\n1040:\t\tif (rc)\n1041:\t\t\treturn dev_err_probe(zldev-\u003edev, rc,\n1042:\t\t\t\t\t \"Failed to initialize mutex\\n\");\n1043:\t\trc = devm_mutex_init(zldev-\u003edev, \u0026zldev-\u003etie_lock);\n1044:\t\tif (rc)\n1045:\t\t\treturn dev_err_probe(zldev-\u003edev, rc,\n1046:\t\t\t\t\t \"Failed to initialize mutex\\n\");\n1047:\t\n1048:\t\t/* Register DPLL channels */\n1049:\t\trc = zl3073x_devm_dpll_init(zldev);\n1050:\t\tif (rc)\n1051:\t\t\treturn rc;\n1052:\t\n1053:\t\t/* Register the devlink instance and parameters */\n1054:\t\trc = zl3073x_devlink_register(zldev);\n1055:\t\tif (rc)\n1056:\t\t\treturn dev_err_probe(zldev-\u003edev, rc,\n1057:\t\t\t\t\t \"Failed to register devlink instance\\n\");\n1058:\t\n1059:\t\treturn 0;\n1060:\t}\n1061:\tEXPORT_SYMBOL_NS_GPL(zl3073x_dev_probe, \"ZL3073X\");\n1062:\t\n1063:\tMODULE_AUTHOR(\"Ivan Vecera \u003civecera@redhat.com\u003e\");\n1064:\tMODULE_DESCRIPTION(\"Microchip ZL3073x core driver\");\n1065:\tMODULE_LICENSE(\"GPL\");\n"
} |
| 11/2 | 2026/08/01 02:53 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 50100 output: 22 thoughts: 212 Thoughts: **Analyzing the Probe Function's Behavior with Syzkaller** Okay, so I'm looking at this `probe` function and how it interacts with `syzkaller`. The first thing that jumps out is that it's reading `ZL_REG_ID` and comparing it to `zl3073x_chip_ids`. The critical point here is that if `syzkaller` tries to instantiate the I2C device, it's going to attempt an I2C read. Without real hardware, that read is almost certainly going to fail or return garbage – probably zero. And that means the probe is going to fail, which isn't what we want. Hold on a second... doesn't `syzkaller` have something for setting up I2C? I seem to recall a function called `syz_i2c_setup`. Let me double-check. *Looks it up*. Ah, right, that's not quite what I was hoping for. `syzkaller` doesn't have a generic I2C device *emulator*. It can inject traffic if there's a specific pseudo-syscall, or if it uses `i2c-dev` to act as a master, but it doesn't seem to be able to easily emulate a slave device and respond to, say, a `zl3073x_read_u16` call. So, we're still stuck with the potential probe failure. We need to find a workaround. Back to the drawing board... |
| 12/2 | 2026/08/01 02:53 | tool | grepper |
0mError:no matches Args: {
"Expression": "syz_i2c_setup"
}Results: {
"error": "no matches"
} |
| 13/2 | 2026/08/01 02:53 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 50670 output: 20 thoughts: 11 Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert: **Investigating `syz_spi_setup`** Alright, I'm digging into this code and my mind immediately jumps to `syz_spi_setup`. I need to understand its role. What's the context here? Is it a key function in a fuzzing setup? Is it related to driver initialization? I need to quickly recall what that function does, its parameters, and how it interacts with the SPI subsystem. More specifically, what is `syz_` prefix and what is its relation with the SPI. Is it related to the kernel module? Is it related to the interface? From there I want to see how that function figures into the bigger picture. I need to understand what's happening *around* it. Are there any particular entry points for this function that I need to be aware of? That's where I should start. From there I can determine if any problems can occur. |
| 14/2 | 2026/08/01 02:53 | tool | grepper |
0mError:no matches Args: {
"Expression": "syz_spi_setup"
}Results: {
"error": "no matches"
} |
| 15/2 | 2026/08/01 02:53 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 50931 output: 24 thoughts: 212 Thoughts: **Emulating I2C Devices for System Testing** Okay, so I'm thinking about how to test this `zl3073x` device with syzkaller. Can syzkaller actually *emulate* I2C devices effectively? I know there's this `i2c-stub` thing, but from what I recall, it's pretty rudimentary. It's not going to cut it for any serious probing, especially if you need specific register reads for the probe to succeed. Right, so, as I understand it, syzkaller doesn't provide a general mechanism to emulate arbitrary I2C or SPI devices that rely on particular register reads for successful probing. Unless there's a custom mock driver, or it's a USB device (which can use the dummy_hcd/raw-gadget approach), a PCI device (which is problematic unless it's a very generic type or has a custom emulator), or a network device (tun/tap works there). Since `zl3073x` is an I2C/SPI device, its probe function, `zl3073x_dev_probe`, will, by definition, be reading `ZL_REG_ID` via I2C or SPI. If the hardware isn't actually present, that read is either going to fail or, at best, return something like 0xff or 0x00. Hmm, need to see the expected values. I should really check what values are expected in `zl3073x_chip_ids`. That seems like the next logical step. |
| 16/2 | 2026/08/01 02:53 | tool | grepper |
0mArgs:{
"Expression": "zl3073x_chip_ids"
}Results: {
"Output": "drivers/dpll/zl3073x/core.c-25-\ndrivers/dpll/zl3073x/core.c:26:static const struct zl3073x_chip_info zl3073x_chip_ids[] = {\ndrivers/dpll/zl3073x/core.c-27-\tZL_CHIP_INFO(0x0E30, 2, ZL3073X_FLAG_REF_PHASE_COMP_32),\n--\ndrivers/dpll/zl3073x/core.c=980=int zl3073x_dev_probe(struct zl3073x_dev *zldev)\n--\ndrivers/dpll/zl3073x/core.c-992-\t/* Detect chip variant */\ndrivers/dpll/zl3073x/core.c:993:\tfor (i = 0; i \u003c ARRAY_SIZE(zl3073x_chip_ids); i++) {\ndrivers/dpll/zl3073x/core.c:994:\t\tif (zl3073x_chip_ids[i].id == id)\ndrivers/dpll/zl3073x/core.c-995-\t\t\tbreak;\n--\ndrivers/dpll/zl3073x/core.c-997-\ndrivers/dpll/zl3073x/core.c:998:\tif (i == ARRAY_SIZE(zl3073x_chip_ids))\ndrivers/dpll/zl3073x/core.c-999-\t\treturn dev_err_probe(zldev-\u003edev, -ENODEV,\n--\ndrivers/dpll/zl3073x/core.c-1001-\ndrivers/dpll/zl3073x/core.c:1002:\tzldev-\u003einfo = \u0026zl3073x_chip_ids[i];\ndrivers/dpll/zl3073x/core.c-1003-\n"
} |
| 17/2 | 2026/08/01 02:53 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 52040 output: 26 thoughts: 200 Thoughts: **Analyzing the zl3073x Driver Probing** Okay, so I'm looking at this `zl3073x` driver and how it's probed. It looks like the chip identification happens via these hex IDs, something like `0x0E30`, as I'd expect. The immediate concern is the hardware dependency; if the physical chip isn't there, the `zl3073x_read_u16` function is going to crap out. It'll either fail outright, or, even worse, return a useless value like `0x0000` or `0xFFFF`, which is pretty common behavior. This leads to the probe failing with an `-ENODEV` error. That's a pretty standard error code, so I'm not surprised there. This immediately means I can't just spin up a virtualized environment and expect this driver to load cleanly. I need the actual hardware, or a decent emulator to trick the driver into thinking it's there. Hold on, though... is there an I2C emulator in the kernel that syzkaller might be able to use to fake the device and allow the driver to probe? I know there's `i2c-stub`, which is kind of an interesting option, but it only supports really basic SMBus commands, and the device addresses have to be configured in advance. Even if syzkaller *could* load `i2c-stub`, it would still need to know exactly which register offsets to write the correct chip ID to before even attempting to probe the driver. That seems like a bit of a challenge. I need to figure out whether the driver actually uses SMBus or raw I2C transfers. That's the first thing I need to check. I think I'll go through the driver code and see if I can figure this out... it's probably critical to understand this before proceeding further. |
| 18/2 | 2026/08/01 02:53 | tool | grepper |
0mArgs:{
"Expression": "zl3073x_read_u16"
}Results: {
"Output": "drivers/dpll/zl3073x/chan.c=171=int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index)\n--\ndrivers/dpll/zl3073x/chan.c-205-\ndrivers/dpll/zl3073x/chan.c:206:\trc = zl3073x_read_u16(zldev, ZL_REG_OUTPUT_STEP_TIME_MASK,\ndrivers/dpll/zl3073x/chan.c-207-\t\t\t \u0026chan-\u003eout_step_time_mask);\n--\ndrivers/dpll/zl3073x/core.c=178=int zl3073x_write_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 val)\n--\ndrivers/dpll/zl3073x/core.c-183-/**\ndrivers/dpll/zl3073x/core.c:184: * zl3073x_read_u16 - read value from 16bit register\ndrivers/dpll/zl3073x/core.c-185- * @zldev: zl3073x device pointer\n--\ndrivers/dpll/zl3073x/core.c-192- */\ndrivers/dpll/zl3073x/core.c:193:int zl3073x_read_u16(struct zl3073x_dev *zldev, unsigned int reg, u16 *val)\ndrivers/dpll/zl3073x/core.c-194-{\n--\ndrivers/dpll/zl3073x/core.c=980=int zl3073x_dev_probe(struct zl3073x_dev *zldev)\n--\ndrivers/dpll/zl3073x/core.c-987-\t/* Read chip ID */\ndrivers/dpll/zl3073x/core.c:988:\trc = zl3073x_read_u16(zldev, ZL_REG_ID, \u0026id);\ndrivers/dpll/zl3073x/core.c-989-\tif (rc)\n--\ndrivers/dpll/zl3073x/core.c-1004-\t/* Read revision, firmware version and custom config version */\ndrivers/dpll/zl3073x/core.c:1005:\trc = zl3073x_read_u16(zldev, ZL_REG_REVISION, \u0026revision);\ndrivers/dpll/zl3073x/core.c-1006-\tif (rc)\ndrivers/dpll/zl3073x/core.c-1007-\t\treturn rc;\ndrivers/dpll/zl3073x/core.c:1008:\trc = zl3073x_read_u16(zldev, ZL_REG_FW_VER, \u0026fw_ver);\ndrivers/dpll/zl3073x/core.c-1009-\tif (rc)\n--\ndrivers/dpll/zl3073x/core.h=147=int zl3073x_read_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 *val);\ndrivers/dpll/zl3073x/core.h:148:int zl3073x_read_u16(struct zl3073x_dev *zldev, unsigned int reg, u16 *val);\ndrivers/dpll/zl3073x/core.h-149-int zl3073x_read_u32(struct zl3073x_dev *zldev, unsigned int reg, u32 *val);\n--\ndrivers/dpll/zl3073x/devlink.c=25=zl3073x_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,\n--\ndrivers/dpll/zl3073x/devlink.c-33-\ndrivers/dpll/zl3073x/devlink.c:34:\trc = zl3073x_read_u16(zldev, ZL_REG_ID, \u0026id);\ndrivers/dpll/zl3073x/devlink.c-35-\tif (rc)\n--\ndrivers/dpll/zl3073x/devlink.c-44-\ndrivers/dpll/zl3073x/devlink.c:45:\trc = zl3073x_read_u16(zldev, ZL_REG_REVISION, \u0026revision);\ndrivers/dpll/zl3073x/devlink.c-46-\tif (rc)\n--\ndrivers/dpll/zl3073x/devlink.c-55-\ndrivers/dpll/zl3073x/devlink.c:56:\trc = zl3073x_read_u16(zldev, ZL_REG_FW_VER, \u0026fw_ver);\ndrivers/dpll/zl3073x/devlink.c-57-\tif (rc)\n--\ndrivers/dpll/zl3073x/dpll.c=1232=zl3073x_dpll_temp_get(const struct dpll_device *dpll, void *dpll_priv,\n--\ndrivers/dpll/zl3073x/dpll.c-1241-\ndrivers/dpll/zl3073x/dpll.c:1242:\trc = zl3073x_read_u16(zldev, ZL_REG_DIE_TEMP_STATUS, \u0026val);\ndrivers/dpll/zl3073x/dpll.c-1243-\tif (rc)\n--\ndrivers/dpll/zl3073x/ref.c=79=int zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)\n--\ndrivers/dpll/zl3073x/ref.c-115-\t/* Read frequency related registers */\ndrivers/dpll/zl3073x/ref.c:116:\trc = zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_BASE, \u0026ref-\u003efreq_base);\ndrivers/dpll/zl3073x/ref.c-117-\tif (rc)\ndrivers/dpll/zl3073x/ref.c-118-\t\treturn rc;\ndrivers/dpll/zl3073x/ref.c:119:\trc = zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_MULT, \u0026ref-\u003efreq_mult);\ndrivers/dpll/zl3073x/ref.c-120-\tif (rc)\ndrivers/dpll/zl3073x/ref.c-121-\t\treturn rc;\ndrivers/dpll/zl3073x/ref.c:122:\trc = zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_M, \u0026ref-\u003efreq_ratio_m);\ndrivers/dpll/zl3073x/ref.c-123-\tif (rc)\ndrivers/dpll/zl3073x/ref.c-124-\t\treturn rc;\ndrivers/dpll/zl3073x/ref.c:125:\trc = zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_N, \u0026ref-\u003efreq_ratio_n);\ndrivers/dpll/zl3073x/ref.c-126-\tif (rc)\n--\ndrivers/dpll/zl3073x/synth.c=23=int zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 index)\n--\ndrivers/dpll/zl3073x/synth.c-45-\t */\ndrivers/dpll/zl3073x/synth.c:46:\trc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_BASE, \u0026synth-\u003efreq_base);\ndrivers/dpll/zl3073x/synth.c-47-\tif (rc)\n--\ndrivers/dpll/zl3073x/synth.c-53-\ndrivers/dpll/zl3073x/synth.c:54:\trc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_M, \u0026synth-\u003efreq_m);\ndrivers/dpll/zl3073x/synth.c-55-\tif (rc)\n--\ndrivers/dpll/zl3073x/synth.c-57-\ndrivers/dpll/zl3073x/synth.c:58:\trc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_N, \u0026synth-\u003efreq_n);\ndrivers/dpll/zl3073x/synth.c-59-\tif (rc)\n"
} |
| 19/2 | 2026/08/01 02:53 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 55143 output: 119 thoughts: 716 Thoughts: **WorthFuzzing=false: Unreachable Code in a Hardware-Dependent Driver** Okay, let me break this down. My initial thought is that the `zl3073x` driver uses `regmap` internally, which means that `zl3073x_read_u16` will, in turn, call `regmap_read`. The question is, can syzkaller fuzz `regmap` devices? The answer, as I see it, is generally no, unless a specific pseudo-syscall exists or if the device itself gets instantiated. I realize that `zl3073x_dev_probe` will fail early on. Specifically, it'll fail at the `zl3073x_read_u16(zldev, ZL_REG_ID, &id)` call because any I2C or SPI read in a syzkaller context will result in a failure or junk data like `0x0000` or `0xFFFF`. Now, I considered `i2c-stub` and whether that might work. I know that `i2c-stub` can be loaded with a specific `chip_addr`, which would let it respond to I2C reads. However, syzkaller doesn't know the specific chip ID, `ZL_REG_ID` is probably something like `0x0E30`, and the addresses of the registers to write this chip ID to, so the correct values would not be written. The likelihood of syzkaller randomly writing the correct values to register `0x00` and `0x01` (or whatever the endianness is), to set the correct ID before attempting to probe the driver, is infinitesimally small. Plus, `i2c-stub` isn't syzkaller's typical approach for faking arbitrary devices, and requires module parameters to set up the addresses. I also remember `syz_usb_connect` which can emulate USB devices, but here we are talking about I2C/SPI. It's a different beast. So, the next question is, are there any other ways this code can be reached? No. I've reconfirmed that this code resides solely within the `drivers/dpll/zl3073x/` directory, and it's a very specific hardware driver for Microchip Azurite DPLL/PTP/SyncE devices. Crucially, these devices are not emulated in environments like QEMU. They are physical devices. Now, let's see. I'll double check to see if there is any software emulation of the device, just to be sure. Nope. It's a physical DPLL chip. Now, let me go back to the instructions I was given. They explicitly state to set "WorthFuzzing=false" if a patch contains: * Code paths that are impossible to reach in virtualized environments, even when using software-emulated hardware. * Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers that require physical PCIe cards not emulated in QEMU. This is a vendor-specific I2C/SPI DPLL driver. It requires physical hardware which is not emulated in standard QEMU. This meets the criteria perfectly. I've reviewed the patch again. It modifies files like `chan.c`, `core.c`, and `dpll.c` within the `zl3073x` driver, adding PTP clock support. But because the driver itself can't be probed without the physical Microchip Azurite chip, none of this code can be executed by syzkaller. Therefore, I'm setting `WorthFuzzing=false`. My reasoning is this: the code is solely within a hardware-specific driver, which is for a specific DPLL chip which cannot be emulated in standard virtualization environments like QEMU or GCE. |
| 20/2 | 2026/08/01 02:53 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch adds PTP clock support to the Microchip Azurite DPLL driver (zl3073x). This is a vendor-specific hardware driver for I2C/SPI connected DPLL devices. Since the physical hardware is not emulated in standard QEMU/GCE environments, the driver's probe function will fail to read the expected hardware chip IDs, making the modified code paths unreachable during fuzzing.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch adds PTP clock support to the Microchip Azurite DPLL driver (zl3073x). This is a vendor-specific hardware driver for I2C/SPI connected DPLL devices. Since the physical hardware is not emulated in standard QEMU/GCE environments, the driver's probe function will fail to read the expected hardware chip IDs, making the modified code paths unreachable during fuzzing.",
"WorthFuzzing": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|