| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/10/09 21:55 | flow | patch-triage |
5mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"KMSANReasoning": "",
"NeedsKMSAN": false,
"Reasoning": "This patch series introduces a new driver for the SiTime SiT95316/SiT95317 I2C-controlled DPLL clock generator chips along with Device Tree documentation. The driver requires physical SiTime hardware on an I2C bus with specific clock inputs and register responses, which is not emulated in standard QEMU/GCE virtualized environments. Because probe will fail immediately without the physical ASIC, no DPLL devices or pins are registered and the driver's runtime logic is structurally unreachable. No core subsystem or core DPLL code was modified.",
"WorthFuzzing": false
} |
| 1/1 | 2026/10/09 21:55 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 4367861b3ff40ec74931b84d543b874db1b6aff6\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Oct 9 21:54:58 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/Documentation/devicetree/bindings/dpll/sitime,sit95316.yaml b/Documentation/devicetree/bindings/dpll/sitime,sit95316.yaml\nnew file mode 100644\nindex 0000000000000..200b9f709a572\n--- /dev/null\n+++ b/Documentation/devicetree/bindings/dpll/sitime,sit95316.yaml\n@@ -0,0 +1,213 @@\n+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)\n+%YAML 1.2\n+---\n+$id: http://devicetree.org/schemas/dpll/sitime,sit95316.yaml#\n+$schema: http://devicetree.org/meta-schemas/core.yaml#\n+\n+title: SiTime SiT95316/SiT95317 DPLL Clock Generator\n+\n+maintainers:\n+ - Ali Rouhi \u003carouhi@sitime.com\u003e\n+\n+description: |\n+ SiTime SiT95316 and SiT95317 are I2C-controlled programmable clock\n+ generators with integrated DPLL for synchronization applications. Both\n+ variants contain four PLLs with automatic/manual reference selection,\n+ DCO frequency adjustment, and phase offset measurement via an on-chip\n+ TDC (Time-to-Digital Converter).\n+\n+ Both parts have 4 differential input pairs whose lanes can also be\n+ driven independently as single-ended references, so 8 inputs are\n+ individually selectable. SiT95317 drives 8 outputs, SiT95316\n+ drives 12.\n+\n+ Pin numbering. An input pin's reg is its lane: 2k is INkP and 2k + 1\n+ is INkN, 0 to 7. A pair the loaded configuration runs differential is\n+ one input, described at 2k. An output pin's reg is its physical output\n+ OUTn, 0 to 11; the SiT95317 bonds out OUT0, OUT3, OUT4, OUT5, OUT7, OUT8,\n+ OUT9 and OUT11.\n+\n+properties:\n+ compatible:\n+ enum:\n+ - sitime,sit95316\n+ - sitime,sit95317\n+\n+ reg:\n+ maxItems: 1\n+\n+ clocks:\n+ maxItems: 1\n+\n+ clock-names:\n+ items:\n+ - const: xtal\n+\n+ clock-frequency:\n+ description:\n+ XO rate in Hz feeding XIN/XO_CLK, for firmware that describes the\n+ oscillator as a property rather than as a clock provider, such as\n+ ACPI. Use either this or \"clocks\", not both.\n+\n+ reset-gpios:\n+ maxItems: 1\n+ description:\n+ GPIO connected to the chip's active-low reset pin (RESETB).\n+\n+ interrupts:\n+ maxItems: 1\n+ description:\n+ Interrupt from the chip's active-low INTRB output. Asserted when\n+ the device detects a status change such as lock acquisition or loss.\n+\n+ dpll-types:\n+ maxItems: 4\n+\n+ vdd-supply:\n+ description: Core supply of the PLLs, 1.8 V (the VDD pins).\n+\n+ vddin-supply:\n+ description:\n+ Supply of the input receivers and dividers, 3.3 V (VDDIN).\n+\n+ vdds-supply:\n+ description:\n+ Supply of the two GPIO pins that have their own rail (VDDS). The\n+ SiT95316 has no such pin.\n+\n+ input-pins:\n+ type: object\n+ patternProperties:\n+ \"^pin@[0-9a-f]+$\":\n+ type: object\n+ properties:\n+ reg:\n+ maximum: 7\n+\n+ supported-frequencies-hz:\n+ description:\n+ The rate wired to the input, in Hz, as a single entry. An\n+ input runs at the rate the board feeds it and has no\n+ frequency to select, so that entry is what the pin reports\n+ as its frequency.\n+\n+patternProperties:\n+ \"^vddo([0-9]|1[01]|0_1|10_11)-supply$\":\n+ description: |\n+ Supply of an output driver, named after its VDDO pin. The SiT95317\n+ has one per output, VDDO0 to VDDO7, numbered as that part's\n+ datasheet numbers its outputs; in the pin numbering of this binding\n+ those are outputs 0, 3, 4, 5, 7, 8, 9 and 11, in that order. The\n+ SiT95316 has VDDO0_1, VDDO2 to VDDO9 and VDDO10_11, the two shared\n+ pins each powering the pair of outputs they are named after.\n+\n+required:\n+ - compatible\n+ - reg\n+\n+dependencies:\n+ clocks: [ clock-names ]\n+\n+oneOf:\n+ - required:\n+ - clocks\n+ - required:\n+ - clock-frequency\n+\n+allOf:\n+ - $ref: /schemas/dpll/dpll-device.yaml#\n+ - if:\n+ properties:\n+ compatible:\n+ const: sitime,sit95317\n+ then:\n+ properties:\n+ output-pins:\n+ patternProperties:\n+ \"^pin@[0-9a-f]+$\":\n+ properties:\n+ reg:\n+ enum: [0, 3, 4, 5, 7, 8, 9, 11]\n+ patternProperties:\n+ \"^vddo(8|9|1[01]|0_1|10_11)-supply$\": false\n+ else:\n+ properties:\n+ vdds-supply: false\n+ output-pins:\n+ patternProperties:\n+ \"^pin@[0-9a-f]+$\":\n+ properties:\n+ reg:\n+ maximum: 11\n+ patternProperties:\n+ \"^vddo(1[01]|[01])-supply$\": false\n+\n+unevaluatedProperties: false\n+\n+examples:\n+ - |\n+ #include \u003cdt-bindings/gpio/gpio.h\u003e\n+ #include \u003cdt-bindings/interrupt-controller/irq.h\u003e\n+\n+ i2c {\n+ #address-cells = \u003c1\u003e;\n+ #size-cells = \u003c0\u003e;\n+\n+ dpll@68 {\n+ compatible = \"sitime,sit95316\";\n+ reg = \u003c0x68\u003e;\n+ clocks = \u003c\u0026xo2\u003e;\n+ clock-names = \"xtal\";\n+ reset-gpios = \u003c\u0026gpio 78 GPIO_ACTIVE_LOW\u003e;\n+ interrupts = \u003c12 IRQ_TYPE_LEVEL_LOW\u003e;\n+ vdd-supply = \u003c\u0026reg_1v8\u003e;\n+ vddin-supply = \u003c\u0026reg_3v3\u003e;\n+ vddo0_1-supply = \u003c\u0026reg_3v3\u003e;\n+ vddo10_11-supply = \u003c\u0026reg_1v8\u003e;\n+ dpll-types = \"eec\", \"eec\", \"eec\", \"eec\";\n+\n+ input-pins {\n+ #address-cells = \u003c1\u003e;\n+ #size-cells = \u003c0\u003e;\n+\n+ /* IN0P and IN0N: pair 0 runs single-ended */\n+ pin@0 {\n+ reg = \u003c0\u003e;\n+ label = \"clkin0\";\n+ connection-type = \"ext\";\n+ supported-frequencies-hz = /bits/ 64 \u003c10000000\u003e;\n+ };\n+\n+ pin@1 {\n+ reg = \u003c1\u003e;\n+ label = \"clkin1\";\n+ connection-type = \"synce\";\n+ supported-frequencies-hz = /bits/ 64 \u003c156250000\u003e;\n+ };\n+ };\n+\n+ output-pins {\n+ #address-cells = \u003c1\u003e;\n+ #size-cells = \u003c0\u003e;\n+\n+ pin@0 {\n+ reg = \u003c0\u003e;\n+ label = \"clkout0\";\n+ supported-frequencies-hz = /bits/ 64 \u003c156250000\u003e;\n+ };\n+\n+ pin@1 {\n+ reg = \u003c1\u003e;\n+ label = \"clkout1\";\n+ supported-frequencies-hz = /bits/ 64 \u003c25000000\u003e;\n+ };\n+\n+ pin@a {\n+ reg = \u003c10\u003e;\n+ label = \"clkout10\";\n+ supported-frequencies-hz = /bits/ 64 \u003c10000000\u003e;\n+ };\n+ };\n+ };\n+ };\n+...\ndiff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml\nindex ba20029693734..65da27d6e84d8 100644\n--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml\n+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml\n@@ -1563,6 +1563,8 @@ patternProperties:\n description: SiRF Technology, Inc.\n \"^sis,.*\":\n description: Silicon Integrated Systems Corp.\n+ \"^sitime,.*\":\n+ description: SiTime Corporation\n \"^sitronix,.*\":\n description: Sitronix Technology Corporation\n \"^skov,.*\":\ndiff --git a/MAINTAINERS b/MAINTAINERS\nindex 72ca3aab21066..64e15831055e3 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -25182,6 +25182,13 @@ S:\tMaintained\n W:\thttp://www.winischhofer.at/linuxsisusbvga.shtml\n F:\tdrivers/usb/misc/sisusbvga/\n \n+SITIME SIT9531X DPLL DRIVER\n+M:\tAli Rouhi \u003carouhi@sitime.com\u003e\n+L:\tnetdev@vger.kernel.org\n+S:\tMaintained\n+F:\tDocumentation/devicetree/bindings/dpll/sitime,sit95316.yaml\n+F:\tdrivers/dpll/sit9531x/\n+\n SL28 CPLD MFD DRIVER\n M:\tMichael Walle \u003cmwalle@kernel.org\u003e\n S:\tMaintained\ndiff --git a/drivers/dpll/Kconfig b/drivers/dpll/Kconfig\nindex be98969f040ab..f8f7ca121b932 100644\n--- a/drivers/dpll/Kconfig\n+++ b/drivers/dpll/Kconfig\n@@ -23,6 +23,8 @@ config DPLL_REFCNT_TRACKER\n \n \t If unsure, say N.\n \n+source \"drivers/dpll/sit9531x/Kconfig\"\n+\n source \"drivers/dpll/zl3073x/Kconfig\"\n \n endmenu\ndiff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile\nindex 9e7a3a3e592e8..4adc50d748d4d 100644\n--- a/drivers/dpll/Makefile\n+++ b/drivers/dpll/Makefile\n@@ -8,4 +8,5 @@ dpll-y += dpll_core.o\n dpll-y += dpll_netlink.o\n dpll-y += dpll_nl.o\n \n+obj-$(CONFIG_SIT9531X_DPLL)\t+= sit9531x/\n obj-$(CONFIG_ZL3073X)\t+= zl3073x/\ndiff --git a/drivers/dpll/sit9531x/Kconfig b/drivers/dpll/sit9531x/Kconfig\nnew file mode 100644\nindex 0000000000000..47aea8674327c\n--- /dev/null\n+++ b/drivers/dpll/sit9531x/Kconfig\n@@ -0,0 +1,17 @@\n+# SPDX-License-Identifier: GPL-2.0-only\n+\n+config SIT9531X_DPLL\n+\ttristate \"SiTime SiT9531x DPLL driver\"\n+\tdepends on I2C \u0026\u0026 NET\n+\tselect DPLL\n+\tselect REGMAP_I2C\n+\thelp\n+\t Driver for SiTime SiT9531x family clock generators\n+\t (SiT95317, SiT95316).\n+\n+\t This driver registers each on-chip PLL as a DPLL device\n+\t and exposes input/output clocks as DPLL pins, providing\n+\t runtime configuration via Generic Netlink.\n+\n+\t To compile this driver as a module, choose M here: the\n+\t module will be called sit9531x.\ndiff --git a/drivers/dpll/sit9531x/Makefile b/drivers/dpll/sit9531x/Makefile\nnew file mode 100644\nindex 0000000000000..b97d2656a4603\n--- /dev/null\n+++ b/drivers/dpll/sit9531x/Makefile\n@@ -0,0 +1,4 @@\n+# SPDX-License-Identifier: GPL-2.0-only\n+\n+obj-$(CONFIG_SIT9531X_DPLL) += sit9531x.o\n+sit9531x-y := core.o dpll.o prop.o\ndiff --git a/drivers/dpll/sit9531x/core.c b/drivers/dpll/sit9531x/core.c\nnew file mode 100644\nindex 0000000000000..c2d6fbd0363f1\n--- /dev/null\n+++ b/drivers/dpll/sit9531x/core.c\n@@ -0,0 +1,5298 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * SiTime SiT9531x DPLL core driver\n+ *\n+ * Copyright (C) 2026 SiTime Corp.\n+ * Author: Ali Rouhi \u003carouhi@sitime.com\u003e\n+ * Author: Oleg Zadorozhnyi \u003cOleg.Zadorozhnyi@devoxsoftware.com\u003e\n+ *\n+ * Core I2C probe, regmap configuration, hardware state management,\n+ * and periodic work thread.\n+ */\n+\n+#include \u003clinux/bitfield.h\u003e\n+#include \u003clinux/bits.h\u003e\n+#include \u003clinux/clk.h\u003e\n+#include \u003clinux/delay.h\u003e\n+#include \u003clinux/dev_printk.h\u003e\n+#include \u003clinux/device.h\u003e\n+#include \u003clinux/gpio/consumer.h\u003e\n+#include \u003clinux/i2c.h\u003e\n+#include \u003clinux/interrupt.h\u003e\n+#include \u003clinux/kernel.h\u003e\n+#include \u003clinux/kthread.h\u003e\n+#include \u003clinux/module.h\u003e\n+#include \u003clinux/pm.h\u003e\n+#include \u003clinux/property.h\u003e\n+#include \u003clinux/regmap.h\u003e\n+#include \u003clinux/slab.h\u003e\n+#include \u003clinux/string.h\u003e\n+\n+#include \"core.h\"\n+#include \"dpll.h\"\n+#include \"prop.h\"\n+#include \"regs.h\"\n+\n+/*\n+ * Number of input + output pin positions for pin index allocation. The two\n+ * extra input positions are the crystal and the INTSYNC destination, the\n+ * extra output position is the INTSYNC source.\n+ */\n+#define SIT9531X_NUM_INPUT_PINS\t\t(SIT9531X_MAX_INPUTS + 2)\n+#define SIT9531X_NUM_OUTPUT_PINS\t(SIT9531X_MAX_OUTPUTS + 1)\n+#define SIT9531X_NUM_PINS_TOTAL\t\t(SIT9531X_NUM_INPUT_PINS + \\\n+\t\t\t\t\t SIT9531X_NUM_OUTPUT_PINS)\n+\n+#define SIT9531X_CHIP(_id, _nin, _nout, _name, _map) \\\n+\t{ .id = (_id), .num_inputs = (_nin), .num_outputs = (_nout), \\\n+\t .name = (_name), .clkout_map = (_map) }\n+\n+/* Per-variant output index -\u003e physical slot mapping */\n+static const u8 clkout_map_95317[] = {0, 3, 4, 5, 7, 8, 9, 11};\n+static const u8 clkout_map_95316[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};\n+\n+static const struct sit9531x_chip_info sit9531x_chip_ids[] = {\n+\tSIT9531X_CHIP(SIT9531X_VARIANT_ID_95317, 8, 8, \"SiT95317\",\n+\t\t clkout_map_95317),\n+\tSIT9531X_CHIP(SIT9531X_VARIANT_ID_95316, 8, 12, \"SiT95316\",\n+\t\t clkout_map_95316),\n+};\n+\n+#define SIT9531X_RANGE_OFFSET\tSIT9531X_PAGE_SIZE\n+\n+/*\n+ * Everything the device holds can change without the driver writing it,\n+ * so nothing here is cacheable except the page selector. Caching that\n+ * one spares a read of it before every access: the range code selects\n+ * the page through a read-modify-write, and with no cache that read goes\n+ * to the bus each time.\n+ *\n+ * The cached value is only as good as the last transfer, though. A\n+ * transfer that fails can leave the part on another page than the cache\n+ * records, and a part that lost power across suspend is back on page 0.\n+ * Every accessor drops the entry on an error and resume drops it too, so\n+ * the next access reads the selector again.\n+ */\n+static bool sit9531x_volatile_reg(struct device *dev __maybe_unused,\n+\t\t\t\t unsigned int reg)\n+{\n+\treturn reg != SIT9531X_PAGE_SEL;\n+}\n+\n+static const struct regmap_range_cfg sit9531x_regmap_range = {\n+\t.range_min\t= SIT9531X_RANGE_OFFSET,\n+\t.range_max\t= SIT9531X_RANGE_OFFSET +\n+\t\t\t (SIT9531X_NUM_PAGES * SIT9531X_PAGE_SIZE) - 1,\n+\t.selector_reg\t= SIT9531X_PAGE_SEL,\n+\t.selector_mask\t= GENMASK(7, 0),\n+\t.selector_shift\t= 0,\n+\t.window_start\t= 0,\n+\t.window_len\t= SIT9531X_PAGE_SIZE,\n+};\n+\n+const struct regmap_config sit9531x_regmap_config = {\n+\t.reg_bits\t= 8,\n+\t.val_bits\t= 8,\n+\t.max_register\t= SIT9531X_RANGE_OFFSET +\n+\t\t\t (SIT9531X_NUM_PAGES * SIT9531X_PAGE_SIZE) - 1,\n+\t.ranges\t\t= \u0026sit9531x_regmap_range,\n+\t.num_ranges\t= 1,\n+\t.volatile_reg\t= sit9531x_volatile_reg,\n+\t.cache_type\t= REGCACHE_MAPLE,\n+};\n+\n+static void sit9531x_page_cache_drop(struct sit9531x_dev *sitdev)\n+{\n+\tregcache_drop_region(sitdev-\u003eregmap, SIT9531X_PAGE_SEL,\n+\t\t\t SIT9531X_PAGE_SEL);\n+}\n+\n+/*\n+ * sit9531x_read_u8 - read an 8-bit register\n+ * @reg:\tregister in SIT9531X_REG(page, offset) form\n+ * @val:\toutput value\n+ */\n+int sit9531x_read_u8(struct sit9531x_dev *sitdev, unsigned int reg,\n+\t\t u8 *val)\n+{\n+\tunsigned int vreg, tmp;\n+\tint rc;\n+\n+\tvreg = (SIT9531X_REG_PAGE(reg) * SIT9531X_PAGE_SIZE) +\n+\t SIT9531X_REG_OFFSET(reg) + SIT9531X_RANGE_OFFSET;\n+\n+\trc = regmap_read(sitdev-\u003eregmap, vreg, \u0026tmp);\n+\tif (rc) {\n+\t\tsit9531x_page_cache_drop(sitdev);\n+\t\tdev_err(sitdev-\u003edev,\n+\t\t\t\"Failed to read page 0x%02x reg 0x%02x: %d\\n\",\n+\t\t\tSIT9531X_REG_PAGE(reg), SIT9531X_REG_OFFSET(reg), rc);\n+\t} else {\n+\t\t*val = (u8)tmp;\n+\t}\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * sit9531x_write_u8 - write an 8-bit register\n+ * @reg:\tregister in SIT9531X_REG(page, offset) form\n+ * @val:\tvalue to write\n+ */\n+int sit9531x_write_u8(struct sit9531x_dev *sitdev, unsigned int reg,\n+\t\t u8 val)\n+{\n+\tunsigned int vreg;\n+\tint rc;\n+\n+\tvreg = (SIT9531X_REG_PAGE(reg) * SIT9531X_PAGE_SIZE) +\n+\t SIT9531X_REG_OFFSET(reg) + SIT9531X_RANGE_OFFSET;\n+\n+\trc = regmap_write(sitdev-\u003eregmap, vreg, val);\n+\tif (rc) {\n+\t\tsit9531x_page_cache_drop(sitdev);\n+\t\tdev_err(sitdev-\u003edev,\n+\t\t\t\"Failed to write page 0x%02x reg 0x%02x: %d\\n\",\n+\t\t\tSIT9531X_REG_PAGE(reg), SIT9531X_REG_OFFSET(reg), rc);\n+\t}\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * sit9531x_read_pll_u8 - read a register on a PLL page\n+ * @val:\toutput value\n+ */\n+int sit9531x_read_pll_u8(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t u8 offset, u8 *val)\n+{\n+\tu8 page = sit9531x_pll_page(pll_idx);\n+\n+\treturn sit9531x_read_u8(sitdev, SIT9531X_REG(page, offset), val);\n+}\n+\n+/*\n+ * sit9531x_write_pll_u8 - write a register on a PLL page\n+ * @val:\tvalue to write\n+ */\n+int sit9531x_write_pll_u8(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t u8 offset, u8 val)\n+{\n+\tu8 page = sit9531x_pll_page(pll_idx);\n+\n+\treturn sit9531x_write_u8(sitdev, SIT9531X_REG(page, offset), val);\n+}\n+\n+/*\n+ * sit9531x_update_pll_u8 - read-modify-write a register on a PLL page\n+ * @mask:\tbits to modify\n+ * @val:\tnew value for masked bits\n+ */\n+int sit9531x_update_pll_u8(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t u8 offset, u8 mask, u8 val)\n+{\n+\tu8 page = sit9531x_pll_page(pll_idx);\n+\tunsigned int vreg;\n+\tint rc;\n+\n+\tvreg = (page * SIT9531X_PAGE_SIZE) + offset + SIT9531X_RANGE_OFFSET;\n+\n+\t/*\n+\t * A read-modify-write is a read and a write, and either can leave\n+\t * the cached page selector wrong, so fail the way the single\n+\t * accessors do.\n+\t */\n+\trc = regmap_update_bits(sitdev-\u003eregmap, vreg, mask, val);\n+\tif (rc) {\n+\t\tsit9531x_page_cache_drop(sitdev);\n+\t\tdev_err(sitdev-\u003edev,\n+\t\t\t\"Failed to update page 0x%02x reg 0x%02x: %d\\n\",\n+\t\t\tpage, offset, rc);\n+\t}\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * sit9531x_input_get_regs - get force mask and state register addresses\n+ * @index:\tlogical input index\n+ * @force_reg:\toutput force mask register address\n+ * @state_reg:\toutput state register address\n+ *\n+ * Selects the correct Page 0x02 register pair based on the pair's\n+ * signal mode and the lane (P/N) the index refers to.\n+ */\n+static void sit9531x_input_get_regs(const struct sit9531x_dev *sitdev,\n+\t\t\t\t u8 index,\n+\t\t\t\t unsigned int *force_reg,\n+\t\t\t\t unsigned int *state_reg)\n+{\n+\tif (sitdev-\u003eref[index].sig_mode == SIT9531X_MODE_DE) {\n+\t\t*force_reg = SIT9531X_REG_IN_DE_FORCE;\n+\t\t*state_reg = SIT9531X_REG_IN_DE_STATE;\n+\t} else if (sit9531x_input_is_n(index)) {\n+\t\t*force_reg = SIT9531X_REG_IN_SEN_FORCE;\n+\t\t*state_reg = SIT9531X_REG_IN_SEN_STATE;\n+\t} else {\n+\t\t*force_reg = SIT9531X_REG_IN_SEP_FORCE;\n+\t\t*state_reg = SIT9531X_REG_IN_SEP_STATE;\n+\t}\n+}\n+\n+/*\n+ * sit9531x_input_disable - disable an input reference\n+ * @index:\tlogical input index (0-N)\n+ *\n+ * Sets the force mask bit and clears the state bit for the given\n+ * input, effectively disabling it. Register selection depends on\n+ * the pair's signal mode (SE/DE) and the lane (P/N); the bit within\n+ * each register addresses the input pair.\n+ *\n+ * Both writes are attempted even when the first fails, and the first\n+ * error is returned. Neither is rolled back: the force and state bits\n+ * only mean something together, so a transient bus error can leave the\n+ * force bit asserted over a state bit that was never programmed, and the\n+ * error is what says the override is not to be trusted.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+int sit9531x_input_disable(struct sit9531x_dev *sitdev, u8 index)\n+{\n+\tunsigned int force_reg, state_reg;\n+\tstruct sit9531x_ref *ref;\n+\tu8 pair, val;\n+\tint rc, ret;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (index \u003e= SIT9531X_MAX_INPUTS)\n+\t\treturn -EINVAL;\n+\n+\tref = \u0026sitdev-\u003eref[index];\n+\tpair = sit9531x_input_pair(index);\n+\tsit9531x_input_get_regs(sitdev, index, \u0026force_reg, \u0026state_reg);\n+\n+\trc = sit9531x_read_u8(sitdev, force_reg, \u0026val);\n+\tif (!rc)\n+\t\trc = sit9531x_write_u8(sitdev, force_reg, val | BIT(pair));\n+\n+\tret = sit9531x_read_u8(sitdev, state_reg, \u0026val);\n+\tif (!ret)\n+\t\tret = sit9531x_write_u8(sitdev, state_reg, val \u0026 ~BIT(pair));\n+\tif (ret \u0026\u0026 !rc)\n+\t\trc = ret;\n+\n+\t/*\n+\t * Drop the claim even when the pair could not be programmed. The\n+\t * force bit may be asserted over a state bit that never reached the\n+\t * device, so the receiver cannot be relied on; leaving the cache\n+\t * saying it is on makes the next enable skip itself and report a\n+\t * success the signal does not back. Clearing the force bit again\n+\t * is harmless, so the worst this costs is one redundant write.\n+\t */\n+\tref-\u003eenabled = false;\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * sit9531x_input_enable - enable an input reference\n+ * @index:\tlogical input index (0-N)\n+ *\n+ * Clears the force mask bit for the given input, returning it to\n+ * hardware default (enabled).\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+int sit9531x_input_enable(struct sit9531x_dev *sitdev, u8 index)\n+{\n+\tunsigned int force_reg, state_reg;\n+\tstruct sit9531x_ref *ref;\n+\tu8 pair, val;\n+\tint rc;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (index \u003e= SIT9531X_MAX_INPUTS)\n+\t\treturn -EINVAL;\n+\n+\tref = \u0026sitdev-\u003eref[index];\n+\tpair = sit9531x_input_pair(index);\n+\tsit9531x_input_get_regs(sitdev, index, \u0026force_reg, \u0026state_reg);\n+\n+\trc = sit9531x_read_u8(sitdev, force_reg, \u0026val);\n+\tif (rc)\n+\t\treturn rc;\n+\trc = sit9531x_write_u8(sitdev, force_reg, val \u0026 ~BIT(pair));\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tref-\u003eenabled = true;\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Output enable / disable (Hi-Z control)\n+ *\n+ * SiT9531x outputs can be configured as differential (DIFF) or\n+ * single-ended (CMOS) depending on the factory blob. Each output slot\n+ * has THREE Hi-Z force/state register pairs on Page 0x03: one for the\n+ * differential path and one for each CMOS pad, OutP and OutN.\n+ *\n+ * A mute writes all three so the output goes quiet however it is wired.\n+ * That includes both CMOS pads, as SiTime's Output_Disable_Control_HiZ.py\n+ * does: the register map names bit 3 of ODRn_MISC0 CMOS_ENP and bit 2\n+ * CMOS_ENN, while its table of values gives 0100 as CMOS on OutP, so the\n+ * one pad a single-ended configuration drives cannot be told from it.\n+ *\n+ * slot 0-7 :\n+ * DIFF 0xF2/0xF3 CMOS OutP 0xF8/0xF9 CMOS OutN 0xF6/0xF7\n+ * slot 8-11:\n+ * DIFF 0xF4/0xF5 bits 3:0 CMOS OutP 0xFA/0xFB\n+ * CMOS OutN 0xF4/0xF5 bits 7:4\n+ *\n+ * MASK bit = 1 -\u003e driver takes control of that output's Hi-Z state\n+ * STATE bit = 0 -\u003e output is forced to Hi-Z (muted)\n+ * STATE bit = 1 -\u003e output is driven (active)\n+ *\n+ * The output \"index\" in the driver is logical; the physical slot comes\n+ * from info-\u003eclkout_map[].\n+ */\n+\n+enum {\n+\tSIT9531X_HIZ_DIFF,\n+\tSIT9531X_HIZ_CMOS_P,\n+\tSIT9531X_HIZ_CMOS_N,\n+\tSIT9531X_HIZ_PAIRS,\n+};\n+\n+struct sit9531x_hiz_pair {\n+\tunsigned int mask;\n+\tunsigned int state;\n+\tu8 bit;\n+};\n+\n+static void sit9531x_output_get_hiz_regs(u8 slot,\n+\t\t\t\t\t struct sit9531x_hiz_pair *p)\n+{\n+\tstruct sit9531x_hiz_pair *diff = \u0026p[SIT9531X_HIZ_DIFF];\n+\tstruct sit9531x_hiz_pair *cmos_p = \u0026p[SIT9531X_HIZ_CMOS_P];\n+\tstruct sit9531x_hiz_pair *cmos_n = \u0026p[SIT9531X_HIZ_CMOS_N];\n+\n+\tif (slot \u003c= 7) {\n+\t\tdiff-\u003emask = SIT9531X_REG_HIZ_DIFF_07_MASK;\n+\t\tdiff-\u003estate = SIT9531X_REG_HIZ_DIFF_07_STATE;\n+\t\tdiff-\u003ebit = slot;\n+\t\tcmos_p-\u003emask = SIT9531X_REG_HIZ_SE_07_MASK;\n+\t\tcmos_p-\u003estate = SIT9531X_REG_HIZ_SE_07_STATE;\n+\t\tcmos_p-\u003ebit = slot;\n+\t\tcmos_n-\u003emask = SIT9531X_REG_HIZ_SEN_07_MASK;\n+\t\tcmos_n-\u003estate = SIT9531X_REG_HIZ_SEN_07_STATE;\n+\t\tcmos_n-\u003ebit = slot;\n+\t} else {\n+\t\tdiff-\u003emask = SIT9531X_REG_HIZ_DIFF_811_MASK;\n+\t\tdiff-\u003estate = SIT9531X_REG_HIZ_DIFF_811_STATE;\n+\t\tdiff-\u003ebit = slot - 8;\n+\t\tcmos_p-\u003emask = SIT9531X_REG_HIZ_SE_811_MASK;\n+\t\tcmos_p-\u003estate = SIT9531X_REG_HIZ_SE_811_STATE;\n+\t\tcmos_p-\u003ebit = slot - 8;\n+\t\tcmos_n-\u003emask = SIT9531X_REG_HIZ_DIFF_811_MASK;\n+\t\tcmos_n-\u003estate = SIT9531X_REG_HIZ_DIFF_811_STATE;\n+\t\tcmos_n-\u003ebit = slot - 8 + SIT9531X_HIZ_SEN_811_SHIFT;\n+\t}\n+}\n+\n+/*\n+ * sit9531x_output_mode_fetch - read how an output is wired\n+ *\n+ * The Hi-Z force is a separate register pair for the differential path\n+ * and for each CMOS pad, and only the pairs belonging to the way the\n+ * output is actually wired say anything about whether it is quiet. The\n+ * others hold whatever the loaded configuration left in them.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+static int sit9531x_output_mode_fetch(struct sit9531x_dev *sitdev, u8 out_idx)\n+{\n+\tconst struct sit9531x_chip_info *info = sitdev-\u003einfo;\n+\tu8 slot, page, reg, val;\n+\tint rc;\n+\n+\tslot = info-\u003eclkout_map[out_idx];\n+\tpage = (slot \u003e SIT9531X_PAGE_OUTSYS0_SLOT_MAX) ?\n+\t\tSIT9531X_PAGE_OUTSYS1 : SIT9531X_PAGE_OUTSYS0;\n+\treg = SIT9531X_OUT_MISC0_BASE +\n+\t SIT9531X_OUT_MISC0_STRIDE * (slot % 6);\n+\n+\trc = sit9531x_read_u8(sitdev, SIT9531X_REG(page, reg), \u0026val);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tsitdev-\u003eout[out_idx].cmos = !!(val \u0026 (SIT9531X_OUT_CMOS_ENP |\n+\t\t\t\t\t SIT9531X_OUT_CMOS_ENN));\n+\n+\treturn 0;\n+}\n+\n+static int sit9531x_hiz_pair_forced(struct sit9531x_dev *sitdev,\n+\t\t\t\t const struct sit9531x_hiz_pair *p,\n+\t\t\t\t bool *forced)\n+{\n+\tu8 mask, state;\n+\tint rc;\n+\n+\trc = sit9531x_read_u8(sitdev, p-\u003emask, \u0026mask);\n+\tif (rc)\n+\t\treturn rc;\n+\trc = sit9531x_read_u8(sitdev, p-\u003estate, \u0026state);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\t*forced = (mask \u0026 BIT(p-\u003ebit)) \u0026\u0026 !(state \u0026 BIT(p-\u003ebit));\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Report whether a slot is currently forced into Hi-Z, i.e. the driver\n+ * (or the loaded profile) took control of the pin (MASK bit set) and\n+ * forces it to Hi-Z (STATE bit clear). Only the pairs that match how\n+ * the output is wired are read: that is what controls the pad. A CMOS\n+ * output reads as muted only when both of its pads are forced, since\n+ * which one the configuration drives cannot be told (see above).\n+ */\n+static int sit9531x_output_forced_hiz(struct sit9531x_dev *sitdev,\n+\t\t\t\t u8 out_idx, bool *muted)\n+{\n+\tstruct sit9531x_hiz_pair p[SIT9531X_HIZ_PAIRS];\n+\tint rc;\n+\n+\tsit9531x_output_get_hiz_regs(sitdev-\u003einfo-\u003eclkout_map[out_idx], p);\n+\n+\t/*\n+\t * Testing a pair the output is not wired through and taking it as\n+\t * proof of a mute answers from a register nothing drives, and\n+\t * disagrees with itself when a mute lands on one pair and fails\n+\t * on another.\n+\t */\n+\tif (!sitdev-\u003eout[out_idx].cmos)\n+\t\treturn sit9531x_hiz_pair_forced(sitdev,\n+\t\t\t\t\t\t\u0026p[SIT9531X_HIZ_DIFF], muted);\n+\n+\trc = sit9531x_hiz_pair_forced(sitdev, \u0026p[SIT9531X_HIZ_CMOS_P], muted);\n+\tif (rc || !*muted)\n+\t\treturn rc;\n+\n+\treturn sit9531x_hiz_pair_forced(sitdev, \u0026p[SIT9531X_HIZ_CMOS_N],\n+\t\t\t\t\tmuted);\n+}\n+\n+/*\n+ * sit9531x_output_state_refresh - read an output's mute state back\n+ *\n+ * Used when a mute could not be confirmed at the time it was written. The\n+ * driver does not poll output state, so without this the cached value would\n+ * stand until something else happened to write it.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+int sit9531x_output_state_refresh(struct sit9531x_dev *sitdev, u8 out_idx)\n+{\n+\tbool muted;\n+\tint rc;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\trc = sit9531x_output_forced_hiz(sitdev, out_idx, \u0026muted);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tsitdev-\u003eout[out_idx].enabled = !muted;\n+\tsitdev-\u003eout[out_idx].state_stale = false;\n+\n+\treturn 0;\n+}\n+\n+static int sit9531x_hiz_set_bit(struct sit9531x_dev *sitdev,\n+\t\t\t\tunsigned int reg, u8 bit, bool set)\n+{\n+\tu8 cur, new_val;\n+\tint rc;\n+\n+\trc = sit9531x_read_u8(sitdev, reg, \u0026cur);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tnew_val = set ? (cur | BIT(bit)) : (cur \u0026 ~BIT(bit));\n+\n+\treturn sit9531x_write_u8(sitdev, reg, new_val);\n+}\n+\n+/* Attempts to re-lock the output loops before reporting them open. */\n+#define SIT9531X_LOOP_LOCK_TRIES\t3\n+\n+/*\n+ * Commit a programming sequence started by sit9531x_prg_enter():\n+ * update the NVM shadow and re-lock the loops. The sleep gives the\n+ * hardware its required settling time after the loop-lock command;\n+ * it is intentional despite the caller holding multiop_lock, as the\n+ * whole NVM + lock sequence must be atomic.\n+ */\n+static int sit9531x_prg_commit(struct sit9531x_dev *sitdev)\n+{\n+\tint rc, rc2 = 0, rc3;\n+\tu8 attempt;\n+\n+\trc = sit9531x_write_u8(sitdev, SIT9531X_REG_PRG_DIR_GEN,\n+\t\t\t SIT9531X_UPDATE_NVM);\n+\n+\t/*\n+\t * Issue the loop lock even if the update failed: every caller\n+\t * commits whatever happened after the entry so that the chip never\n+\t * stays in the PRG_CMD state with its loops open, and returning\n+\t * early here would defeat that.\n+\t *\n+\t * Re-lock the loops. Leaving them open is worse than any other\n+\t * failure this function can report, and nothing else closes them,\n+\t * so retry, as the priority commit retries the release of its\n+\t * forced holdover.\n+\t */\n+\tfor (attempt = 0; attempt \u003c SIT9531X_LOOP_LOCK_TRIES; attempt++) {\n+\t\trc2 = sit9531x_write_u8(sitdev, SIT9531X_REG_PRG_DIR_GEN,\n+\t\t\t\t\tSIT9531X_LOOP_LOCK);\n+\t\tif (!rc2)\n+\t\t\tbreak;\n+\t\tusleep_range(1000, 2000);\n+\t}\n+\tif (rc2)\n+\t\tdev_err(sitdev-\u003edev,\n+\t\t\t\"output loops left unlocked after programming: %d\\n\",\n+\t\t\trc2);\n+\n+\tmsleep(100);\n+\n+\t/*\n+\t * Put the output-system debug block back the way the device powers\n+\t * up. Its key register unlocks every debug register while it holds\n+\t * the unlock value, and each programming sequence writes that value\n+\t * itself, so nothing needs it left unlocked in between.\n+\t */\n+\trc3 = sit9531x_write_u8(sitdev, SIT9531X_REG_OUTSYS_DEBUG,\n+\t\t\t\tSIT9531X_DEBUG_LOCK_VAL);\n+\n+\tif (rc)\n+\t\treturn rc;\n+\tif (rc2)\n+\t\treturn rc2;\n+\n+\t/*\n+\t * The programming is committed and the loops are locked by now;\n+\t * only the debug key stayed open, and the next sequence writes it\n+\t * again. Failing here would make the caller skip what follows a\n+\t * change that did take effect -- and the core drops an identical\n+\t * retry, so it would never run.\n+\t */\n+\tif (rc3)\n+\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t \"output debug block left unlocked after programming: %d\\n\",\n+\t\t\t rc3);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Enter the output-system programming state: unlock the debug\n+ * registers on Page 3 and issue the PRG_CMD state command. Register\n+ * writes that reconfigure the output system only take effect when\n+ * they are made inside this state.\n+ */\n+static int sit9531x_prg_enter(struct sit9531x_dev *sitdev)\n+{\n+\tint rc;\n+\n+\trc = sit9531x_write_u8(sitdev, SIT9531X_REG_OUTSYS_DEBUG,\n+\t\t\t SIT9531X_DEBUG_UNLOCK_VAL);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = sit9531x_write_u8(sitdev, SIT9531X_REG_PRG_DIR_GEN,\n+\t\t\t SIT9531X_PRG_CMD_STATE);\n+\tif (rc) {\n+\t\t/*\n+\t\t * The debug block is unlocked at this point, and a transfer\n+\t\t * that reported an error may still have reached the part --\n+\t\t * which would leave the device in PRG_CMD with its output\n+\t\t * loops open. Callers skip the commit when the entry fails,\n+\t\t * so leave the state here the way every completed sequence\n+\t\t * does: nothing was changed, so the update commits what the\n+\t\t * part already holds, the loops are re-locked, and the\n+\t\t * settling time is waited out before the caller drops the\n+\t\t * lock and the next request programs the part. The entry\n+\t\t * failure is the error to report.\n+\t\t */\n+\t\tsit9531x_prg_commit(sitdev);\n+\t\treturn rc;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * sit9531x_output_hiz_write - mute or unmute an output\n+ *\n+ * Muting takes control of the pin (MASK=1) and forces it to Hi-Z\n+ * (STATE=0) on every Hi-Z pair of the slot -- differential, CMOS OutP\n+ * and CMOS OutN -- because the output must go quiet whichever way it is\n+ * wired; unmuting releases the override on all of them and hands the pin\n+ * back to the device. The caller must already be in the programming\n+ * state.\n+ */\n+static int sit9531x_output_hiz_write(struct sit9531x_dev *sitdev, u8 slot,\n+\t\t\t\t bool mute)\n+{\n+\tstruct sit9531x_hiz_pair p[SIT9531X_HIZ_PAIRS];\n+\tu8 mask[SIT9531X_HIZ_PAIRS], state[SIT9531X_HIZ_PAIRS];\n+\tint i, rc, undo_rc;\n+\n+\tsit9531x_output_get_hiz_regs(slot, p);\n+\n+\tif (!mute) {\n+\t\tfor (i = 0; i \u003c SIT9531X_HIZ_PAIRS; i++) {\n+\t\t\trc = sit9531x_hiz_set_bit(sitdev, p[i].mask, p[i].bit,\n+\t\t\t\t\t\t false);\n+\t\t\tif (rc)\n+\t\t\t\treturn rc;\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\n+\t/*\n+\t * Remember every pair as it was: if the mute fails part-way, that\n+\t * is what the pad goes back to -- which may already be a mute, from\n+\t * an earlier request or from the loaded profile.\n+\t */\n+\tfor (i = 0; i \u003c SIT9531X_HIZ_PAIRS; i++) {\n+\t\trc = sit9531x_read_u8(sitdev, p[i].state, \u0026state[i]);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\t\trc = sit9531x_read_u8(sitdev, p[i].mask, \u0026mask[i]);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\t}\n+\n+\t/*\n+\t * Forced value first, override enable second. Muted is decoded as\n+\t * MASK set with STATE clear, so enabling the override while STATE\n+\t * still holds whatever the loaded configuration left there can pin\n+\t * the pad driven for the width of an I2C transfer.\n+\t */\n+\tfor (i = 0; i \u003c SIT9531X_HIZ_PAIRS; i++) {\n+\t\trc = sit9531x_hiz_set_bit(sitdev, p[i].state, p[i].bit, false);\n+\t\tif (!rc)\n+\t\t\trc = sit9531x_hiz_set_bit(sitdev, p[i].mask, p[i].bit,\n+\t\t\t\t\t\t true);\n+\t\tif (rc)\n+\t\t\tbreak;\n+\t}\n+\tif (!rc)\n+\t\treturn 0;\n+\n+\t/*\n+\t * Only part of the mute reached the device. Put every pair it may\n+\t * have touched back to the bits read above -- override first, then\n+\t * value, the reverse of the order they went on -- so the pad ends\n+\t * where the request started rather than on part of a mute, or\n+\t * un-muted when it was muted before.\n+\t */\n+\tfor (; i \u003e= 0; i--) {\n+\t\tu8 bit = BIT(p[i].bit);\n+\n+\t\tundo_rc = sit9531x_hiz_set_bit(sitdev, p[i].mask, p[i].bit,\n+\t\t\t\t\t mask[i] \u0026 bit);\n+\t\tif (!undo_rc)\n+\t\t\tundo_rc = sit9531x_hiz_set_bit(sitdev, p[i].state,\n+\t\t\t\t\t\t p[i].bit,\n+\t\t\t\t\t\t state[i] \u0026 bit);\n+\t\tif (undo_rc)\n+\t\t\tdev_err(sitdev-\u003edev,\n+\t\t\t\t\"slot%u: Hi-Z override left part applied (%d)\\n\",\n+\t\t\t\tslot, undo_rc);\n+\t}\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * sit9531x_output_disable - mute an output (force Hi-Z)\n+ * @index:\tlogical output index (0..info-\u003enum_outputs-1)\n+ *\n+ * Sets MASK and clears STATE on every Hi-Z pair of the slot, differential\n+ * and both CMOS pads, so that the output is muted regardless of its\n+ * electrical configuration. The\n+ * writes are wrapped in the PRG_CMD / NVM update / loop lock sequence\n+ * so the new state is applied by the hardware.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+int sit9531x_output_disable(struct sit9531x_dev *sitdev, u8 index)\n+{\n+\tconst struct sit9531x_chip_info *info = sitdev-\u003einfo;\n+\tbool muted;\n+\tu8 slot;\n+\tint rc, ret, state_rc;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (index \u003e= info-\u003enum_outputs)\n+\t\treturn -EINVAL;\n+\n+\tslot = info-\u003eclkout_map[index];\n+\trc = sit9531x_prg_enter(sitdev);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = sit9531x_output_hiz_write(sitdev, slot, true);\n+\n+\t/*\n+\t * Always leave the PRG_CMD programming state, even on a mid-sequence\n+\t * write failure: prg_enter() unlocked the output loops, so returning\n+\t * without prg_commit() would strand the chip in the programming state\n+\t * with the loops unlocked. Best effort -- keep the first error.\n+\t */\n+\tret = sit9531x_prg_commit(sitdev);\n+\tif (ret \u0026\u0026 !rc)\n+\t\trc = ret;\n+\n+\t/*\n+\t * Keep the software state aligned to what hardware now drives even\n+\t * when one write in the sequence failed. The commit above may have\n+\t * applied a partial mask/state combination.\n+\t */\n+\tstate_rc = sit9531x_output_forced_hiz(sitdev, index, \u0026muted);\n+\tif (!state_rc) {\n+\t\tsitdev-\u003eout[index].enabled = !muted;\n+\t\tsitdev-\u003eout[index].state_stale = false;\n+\t} else {\n+\t\t/*\n+\t\t * The writes may well have landed; what failed is the proof.\n+\t\t * Mark the cached state for a read-through rather than\n+\t\t * reporting the value it had before this call.\n+\t\t */\n+\t\tsitdev-\u003eout[index].state_stale = true;\n+\t\tif (!rc)\n+\t\t\trc = state_rc;\n+\t}\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * sit9531x_output_enable - un-mute an output (active state)\n+ * @index:\tlogical output index (0..info-\u003enum_outputs-1)\n+ *\n+ * Releases MASK on every Hi-Z pair so the device drives the output\n+ * again as the loaded profile configures it. The writes are wrapped\n+ * in the PRG_CMD / NVM update / loop lock sequence so the new state is\n+ * applied by the hardware.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+int sit9531x_output_enable(struct sit9531x_dev *sitdev, u8 index)\n+{\n+\tconst struct sit9531x_chip_info *info = sitdev-\u003einfo;\n+\tbool muted;\n+\tu8 slot;\n+\tint rc, ret, state_rc;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (index \u003e= info-\u003enum_outputs)\n+\t\treturn -EINVAL;\n+\n+\tslot = info-\u003eclkout_map[index];\n+\trc = sit9531x_prg_enter(sitdev);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = sit9531x_output_hiz_write(sitdev, slot, false);\n+\n+\t/*\n+\t * Always leave the PRG_CMD programming state, even on a mid-sequence\n+\t * write failure: prg_enter() unlocked the output loops, so returning\n+\t * without prg_commit() would strand the chip in the programming state\n+\t * with the loops unlocked. Best effort -- keep the first error.\n+\t */\n+\tret = sit9531x_prg_commit(sitdev);\n+\tif (ret \u0026\u0026 !rc)\n+\t\trc = ret;\n+\n+\t/* See sit9531x_output_disable(): commit can apply part of it. */\n+\tstate_rc = sit9531x_output_forced_hiz(sitdev, index, \u0026muted);\n+\tif (!state_rc) {\n+\t\tsitdev-\u003eout[index].enabled = !muted;\n+\t\tsitdev-\u003eout[index].state_stale = false;\n+\t} else {\n+\t\t/*\n+\t\t * The writes may well have landed; what failed is the proof.\n+\t\t * Mark the cached state for a read-through rather than\n+\t\t * reporting the value it had before this call.\n+\t\t */\n+\t\tsitdev-\u003eout[index].state_stale = true;\n+\t\tif (!rc)\n+\t\t\trc = state_rc;\n+\t}\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * Input priority selection\n+ *\n+ * The SiT9531x has an 11-slot priority table per PLL on Page 1. Each\n+ * register holds two slots nibble-packed: the earlier (even, 2n) slot\n+ * in [7:4] and the later (odd, 2n+1) slot in [3:0].\n+ *\n+ * The procedure:\n+ * 1. Force PLL into holdover (PLL page reg 0x6F bit 4)\n+ * 2. Write priority slots on Page 1\n+ * 3. Small change update (Page 0 reg 0x0F bit 1)\n+ * 4. Release holdover, unless it was forced before step 1 by something\n+ * other than this driver\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+\n+/* Page-1 register holding priority slot @slot of @pll_idx. */\n+static u16 sit9531x_prio_reg(u8 pll_idx, u8 slot)\n+{\n+\treturn SIT9531X_REG(SIT9531X_PAGE_PRIOSYS,\n+\t\t\t SIT9531X_PRIO_BASE_REG +\n+\t\t\t SIT9531X_PRIO_REGS_PER_PLL * pll_idx +\n+\t\t\t slot / SIT9531X_PRIO_SLOTS_PER_REG);\n+}\n+\n+/*\n+ * Extract priority slot @slot from its register value. The register\n+ * holding slots 2n and 2n+1 keeps the earlier slot in the high nibble\n+ * (CLK_SPARE\u003c2n\u003eSEL) and the later one in the low nibble.\n+ */\n+static u8 sit9531x_prio_slot_get(u8 val, u8 slot)\n+{\n+\tif (slot \u0026 1)\n+\t\treturn val \u0026 SIT9531X_PRIO_NIBBLE_MASK;\n+\n+\treturn val \u003e\u003e SIT9531X_PRIO_HI_SHIFT;\n+}\n+\n+/* Place source @src in priority slot @slot of a register value. */\n+static u8 sit9531x_prio_slot_set(u8 val, u8 slot, u8 src)\n+{\n+\tif (slot \u0026 1)\n+\t\treturn (val \u0026 (SIT9531X_PRIO_NIBBLE_MASK \u003c\u003c\n+\t\t\t SIT9531X_PRIO_HI_SHIFT)) |\n+\t\t (src \u0026 SIT9531X_PRIO_NIBBLE_MASK);\n+\n+\treturn (val \u0026 SIT9531X_PRIO_NIBBLE_MASK) |\n+\t ((src \u0026 SIT9531X_PRIO_NIBBLE_MASK) \u003c\u003c\n+\t\tSIT9531X_PRIO_HI_SHIFT);\n+}\n+\n+/*\n+ * Commit a priority-table programming sequence through the Page-0\n+ * programming directive register.\n+ *\n+ * A small change update is all the table needs. The NVM-bank and\n+ * loop-lock directives that the output system issues do not belong\n+ * here: the former programs non-volatile storage from the efuse and\n+ * the latter only means anything after an escape to the PRG_CMD\n+ * state. This matches the documented input_priority_sel() procedure.\n+ */\n+static int sit9531x_prio_prg_commit(struct sit9531x_dev *sitdev)\n+{\n+\tint rc;\n+\n+\trc = sit9531x_write_u8(sitdev, SIT9531X_REG_GLOBAL_UPDATE,\n+\t\t\t SIT9531X_SMALL_UPDATE_CMD);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tusleep_range(1000, 2000);\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * sit9531x_input_mon_fetch - read the clock monitor status of every lane\n+ * @sitdev:\tdevice pointer\n+ *\n+ * Four registers carry a nibble per lane. Loss of signal and frequency\n+ * drift are what separate an input the device could lock to from one it\n+ * could not, which the priority commit uses to choose the active\n+ * selection and the pins report as their operational state.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock, or run before registration.\n+ */\n+int sit9531x_input_mon_fetch(struct sit9531x_dev *sitdev)\n+{\n+\tstatic const unsigned int regs[] = {\n+\t\tSIT9531X_CLKMON_P_STATUS_01, SIT9531X_CLKMON_P_STATUS_23,\n+\t\tSIT9531X_CLKMON_N_STATUS_01, SIT9531X_CLKMON_N_STATUS_23,\n+\t};\n+\tu8 val[ARRAY_SIZE(regs)], i, pair, nib;\n+\tint rc;\n+\n+\tfor (i = 0; i \u003c ARRAY_SIZE(regs); i++) {\n+\t\trc = sit9531x_read_u8(sitdev, regs[i], \u0026val[i]);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\t}\n+\n+\tfor (i = 0; i \u003c sitdev-\u003einfo-\u003enum_inputs; i++) {\n+\t\tstruct sit9531x_ref *ref = \u0026sitdev-\u003eref[i];\n+\n+\t\t/* Lane 2k is INkP, 2k + 1 INkN; a register holds two pairs. */\n+\t\tpair = sit9531x_input_pair(i);\n+\t\tnib = val[(sit9531x_input_is_n(i) ? 2 : 0) + pair / 2];\n+\t\tnib = (pair \u0026 1) ? nib \u003e\u003e 4 : nib \u0026 0x0F;\n+\n+\t\tref-\u003elos = !!(nib \u0026 SIT9531X_CLKMON_LOSS);\n+\t\tref-\u003equal_fail = !ref-\u003elos \u0026\u0026\n+\t\t\t\t !!(nib \u0026 (SIT9531X_CLKMON_FINE_DRIFT |\n+\t\t\t\t\t SIT9531X_CLKMON_COARSE_DRIFT));\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Can the device lock to this source now? Only the input lanes have a\n+ * monitor; the on-chip oscillator and the inter-PLL net are taken as\n+ * present. A differential pair is watched through its P lane, which is\n+ * the one its table entries are canonicalised to.\n+ */\n+static bool sit9531x_prio_src_live(const struct sit9531x_dev *sitdev, u8 src)\n+{\n+\tu8 index = sit9531x_hw_src_input(src);\n+\n+\tif (index \u003e= sitdev-\u003einfo-\u003enum_inputs)\n+\t\treturn true;\n+\n+\treturn !sitdev-\u003eref[index].los;\n+}\n+\n+/*\n+ * Fold a source code to the lane a DPLL pin actually represents.\n+ *\n+ * Differential input pairs expose only the P lane as a DPLL pin. A\n+ * priority table entry encoded as an N lane for such a pair must map to\n+ * the P-lane source for pin-facing operations (membership, priority slots,\n+ * add/remove/set lookups), matching sit9531x_ref_pll_mask_fetch().\n+ */\n+static u8 sit9531x_prio_src_canon(const struct sit9531x_dev *sitdev, u8 src)\n+{\n+\tu8 index = sit9531x_hw_src_input(src);\n+\n+\tif (index \u003e= sitdev-\u003einfo-\u003enum_inputs)\n+\t\treturn src;\n+\n+\tif (sit9531x_input_is_n(index) \u0026\u0026\n+\t sitdev-\u003eref[index].sig_mode == SIT9531X_MODE_DE)\n+\t\treturn sit9531x_input_hw_src(index - 1);\n+\n+\treturn src;\n+}\n+\n+/*\n+ * sit9531x_input_prio_present - is a source listed in a PLL's priority table\n+ * @input_idx:\tinput source in hardware encoding (see\n+ *\t\tsit9531x_input_hw_src())\n+ *\n+ * Answers from the membership mask that every table write and every poll\n+ * refreshes, which is what the pin state getters test. The priority slot\n+ * cannot answer this: a source that is not in the table reports the lowest\n+ * slot, so the slot value alone does not separate absent from last.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+bool sit9531x_input_prio_present(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t\t u8 input_idx)\n+{\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (pll_idx \u003e= SIT9531X_NUM_PLLS)\n+\t\treturn false;\n+\n+\tinput_idx = sit9531x_prio_src_canon(sitdev, input_idx);\n+\tif (input_idx \u003e= SIT9531X_PRIO_NUM_SRC)\n+\t\treturn false;\n+\n+\treturn !!(sitdev-\u003echan[pll_idx].prio_mask \u0026 BIT(input_idx));\n+}\n+\n+/*\n+ * sit9531x_input_prio_get - read an input's priority for a PLL\n+ * @input_idx:\tinput source in hardware encoding (see\n+ *\t\tsit9531x_input_hw_src())\n+ * @prio:\toutput priority (lower is preferred)\n+ *\n+ * Reports the priority configured for the source on this PLL, which is\n+ * kept whether or not the source is currently in the table: state and\n+ * priority are separate attributes, so disconnecting an input and\n+ * connecting it again must not change the priority it reports. The\n+ * value is seeded from the hardware table, and re-seeded whenever the\n+ * read-back shows the table was rewritten by something other than this\n+ * driver. A source that was never listed reports the lowest slot.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+int sit9531x_input_prio_get(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t u8 input_idx, u8 *prio)\n+{\n+\tconst struct sit9531x_chan *chan;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (pll_idx \u003e= SIT9531X_NUM_PLLS)\n+\t\treturn -EINVAL;\n+\tinput_idx = sit9531x_prio_src_canon(sitdev, input_idx);\n+\tif (input_idx \u003e= SIT9531X_PRIO_NUM_SRC)\n+\t\treturn -EINVAL;\n+\n+\tchan = \u0026sitdev-\u003echan[pll_idx];\n+\tif (chan-\u003ecfg_known \u0026 BIT(input_idx))\n+\t\t*prio = chan-\u003ecfg_prio[input_idx];\n+\telse\n+\t\t*prio = SIT9531X_PRIO_MAX_SLOTS - 1;\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Take the configured priorities from a table the hardware holds: each\n+ * listed source gets the first slot it occupies. A source the table does\n+ * not list keeps whatever it had, so a disconnected input comes back with\n+ * its old priority.\n+ */\n+static void sit9531x_prio_cfg_seed(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t\t const u8 *srcs)\n+{\n+\tstruct sit9531x_chan *chan = \u0026sitdev-\u003echan[pll_idx];\n+\tu16 seeded = 0;\n+\tu8 slot, src;\n+\n+\tfor (slot = 0; slot \u003c SIT9531X_PRIO_MAX_SLOTS; slot++) {\n+\t\tsrc = srcs[slot] \u0026 SIT9531X_PRIO_NIBBLE_MASK;\n+\t\tif (!sit9531x_prio_src_usable(src))\n+\t\t\tcontinue;\n+\t\tsrc = sit9531x_prio_src_canon(sitdev, src);\n+\t\tif (seeded \u0026 BIT(src))\n+\t\t\tcontinue;\n+\t\tseeded |= BIT(src);\n+\t\tchan-\u003ecfg_prio[src] = slot;\n+\t\tchan-\u003ecfg_known |= BIT(src);\n+\t}\n+\n+\t/*\n+\t * A source this PLL has never listed gets the lowest slot, the value\n+\t * it reports and the one it is connected at, so its priority does not\n+\t * change when its state does.\n+\t */\n+\tfor (src = 0; src \u003c SIT9531X_PRIO_NUM_SRC; src++) {\n+\t\tif (chan-\u003ecfg_known \u0026 BIT(src))\n+\t\t\tcontinue;\n+\t\tchan-\u003ecfg_prio[src] = SIT9531X_PRIO_MAX_SLOTS - 1;\n+\t\tchan-\u003ecfg_known |= BIT(src);\n+\t}\n+\n+\tmemcpy(chan-\u003eseen_srcs, srcs, sizeof(chan-\u003eseen_srcs));\n+\tchan-\u003eseen_valid = true;\n+}\n+\n+/*\n+ * Build the table for a set of member sources: ordered by configured\n+ * priority, ties kept in the order the hardware table has them, the slots\n+ * past the last member naming no source. Filling them with the code for\n+ * no source rather than with copies of the last member keeps every\n+ * source in exactly one slot, so the order the table encodes is the\n+ * order the priorities say.\n+ */\n+static void sit9531x_prio_table_build(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t\t u16 members, u8 *srcs)\n+{\n+\tconst struct sit9531x_chan *chan = \u0026sitdev-\u003echan[pll_idx];\n+\tu8 order[SIT9531X_PRIO_NUM_SRC], n = 0, i, j, src;\n+\n+\tfor (src = 0; src \u003c SIT9531X_PRIO_NUM_SRC; src++)\n+\t\tif (members \u0026 BIT(src))\n+\t\t\torder[n++] = src;\n+\n+\t/* Insertion sort: at most a dozen entries. */\n+\tfor (i = 1; i \u003c n; i++) {\n+\t\tu8 cur = order[i];\n+\n+\t\tfor (j = i; j \u003e 0; j--) {\n+\t\t\tu8 prev = order[j - 1];\n+\t\t\tu16 kc, kp;\n+\n+\t\t\tkc = (chan-\u003ecfg_known \u0026 BIT(cur)) ?\n+\t\t\t chan-\u003ecfg_prio[cur] : U8_MAX;\n+\t\t\tkp = (chan-\u003ecfg_known \u0026 BIT(prev)) ?\n+\t\t\t chan-\u003ecfg_prio[prev] : U8_MAX;\n+\t\t\tif (kc == kp) {\n+\t\t\t\t/* Keep the hardware order among equals. */\n+\t\t\t\tkc = chan-\u003eprio_last[cur] ?: U8_MAX;\n+\t\t\t\tkp = chan-\u003eprio_last[prev] ?: U8_MAX;\n+\t\t\t}\n+\t\t\tif (kp \u003c= kc)\n+\t\t\t\tbreak;\n+\t\t\torder[j] = prev;\n+\t\t}\n+\t\torder[j] = cur;\n+\t}\n+\n+\tfor (i = 0; i \u003c SIT9531X_PRIO_MAX_SLOTS; i++)\n+\t\tsrcs[i] = i \u003c n ? order[i] : SIT9531X_PRIO_SRC_NONE;\n+}\n+\n+/*\n+ * Refresh a PLL's cached view of its priority table from the source codes\n+ * the table holds -- here after a write, and once per poll from the\n+ * read-back in sit9531x_chan_state_fetch().\n+ *\n+ * The membership mask is what the pin state getters test, the per-slot\n+ * copy is what a rewrite compares against, and the first-slot array\n+ * orders sources of equal priority, so none of them costs a register read\n+ * per pin.\n+ */\n+static void sit9531x_prio_mask_build(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t\t const u8 *srcs)\n+{\n+\tstruct sit9531x_chan *chan = \u0026sitdev-\u003echan[pll_idx];\n+\tu8 first[SIT9531X_PRIO_NUM_SRC] = { 0 };\n+\tu16 mask = 0;\n+\tu8 slot, src, src_canon;\n+\n+\tfor (slot = 0; slot \u003c SIT9531X_PRIO_MAX_SLOTS; slot++) {\n+\t\tsrc = srcs[slot];\n+\t\tsrc \u0026= SIT9531X_PRIO_NIBBLE_MASK;\n+\t\tchan-\u003eprio_srcs[slot] = src;\n+\t\tsrc_canon = sit9531x_prio_src_canon(sitdev, src);\n+\t\tif (!sit9531x_prio_src_usable(src))\n+\t\t\tcontinue;\n+\n+\t\tmask |= BIT(src_canon);\n+\t\tif (!first[src_canon])\n+\t\t\tfirst[src_canon] = slot + 1;\n+\t}\n+\n+\t/*\n+\t * Assign unconditionally: a source that has left the table has no\n+\t * slot, and leaving its old one behind would keep reporting it as\n+\t * listed for as long as the device runs.\n+\t */\n+\tfor (src = 0; src \u003c SIT9531X_PRIO_NUM_SRC; src++)\n+\t\tchan-\u003eprio_last[src] = first[src];\n+\n+\tchan-\u003eprio_mask = mask;\n+}\n+\n+/* Attempts to release a forced holdover before reporting it stuck. */\n+#define SIT9531X_HO_CLEAR_TRIES\t\t3\n+\n+static int sit9531x_prio_table_read(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t\t u8 *srcs);\n+\n+/*\n+ * First source in a table that the device could lock to now, compared as\n+ * canonical codes, or SIT9531X_PRIO_SRC_NONE when no listed source has a\n+ * signal.\n+ */\n+static u8 sit9531x_prio_top_live(const struct sit9531x_dev *sitdev,\n+\t\t\t\t const u8 *srcs)\n+{\n+\tu8 i, src;\n+\n+\tfor (i = 0; i \u003c SIT9531X_PRIO_MAX_SLOTS; i++) {\n+\t\tsrc = srcs[i] \u0026 SIT9531X_PRIO_NIBBLE_MASK;\n+\t\tsrc = sit9531x_prio_src_canon(sitdev, src);\n+\t\tif (sit9531x_prio_src_usable(src) \u0026\u0026\n+\t\t sit9531x_prio_src_live(sitdev, src))\n+\t\t\treturn src;\n+\t}\n+\n+\treturn SIT9531X_PRIO_SRC_NONE;\n+}\n+\n+/*\n+ * Choose the active selection for a table about to be latched. After a\n+ * table write the PLL goes to the source the selection names; it moves to\n+ * another on its own only when that source loses its signal, which is an\n+ * event, not a state. So the selection has to name a source the PLL can\n+ * use, and it follows the priorities the way the DPLL interface defines\n+ * automatic mode -- the highest-priority valid input:\n+ *\n+ * - When the highest-priority source with signal is not the one the\n+ * table held before, the priorities now put another source first, and\n+ * the selection goes to it.\n+ * - Otherwise the write only reorders sources below it, or removes one\n+ * the PLL is not on, and the selection stays where it is while that\n+ * source is still listed and has signal: a change further down the\n+ * table must not pull a PLL off a healthy reference.\n+ * - Otherwise the first listed source with signal; with none alive the\n+ * first listed one is as good as any.\n+ *\n+ * A selection that is still listed but has lost its signal is moved too.\n+ * The PLL has then fallen back on its own and the selection still names\n+ * the dead source; left alone, the next table write sends the PLL back to\n+ * it and it unlocks. The device falls back to the best listed source\n+ * that has signal, which is the one chosen here, so moving the selection\n+ * there does not move the PLL.\n+ *\n+ * @old is the table the device holds before this write.\n+ */\n+static u8 sit9531x_prio_activesel_pick(struct sit9531x_dev *sitdev,\n+\t\t\t\t const u8 *old, const u8 *srcs, u8 cur)\n+{\n+\tu8 top, i;\n+\n+\ttop = sit9531x_prio_top_live(sitdev, srcs);\n+\tif (top != SIT9531X_PRIO_SRC_NONE \u0026\u0026\n+\t top != sit9531x_prio_top_live(sitdev, old))\n+\t\treturn top;\n+\n+\t/*\n+\t * The table is built from canonical codes, so compare in the same\n+\t * terms: a differential pair selected through its N-lane code is the\n+\t * P-lane entry.\n+\t */\n+\tcur = sit9531x_prio_src_canon(sitdev, cur \u0026 SIT9531X_PRIO_NIBBLE_MASK);\n+\n+\tif (sit9531x_prio_src_usable(cur) \u0026\u0026\n+\t sit9531x_prio_src_live(sitdev, cur))\n+\t\tfor (i = 0; i \u003c SIT9531X_PRIO_MAX_SLOTS; i++)\n+\t\t\tif (srcs[i] == cur)\n+\t\t\t\treturn cur;\n+\n+\tif (top != SIT9531X_PRIO_SRC_NONE)\n+\t\treturn top;\n+\n+\treturn srcs[0];\n+}\n+\n+static int sit9531x_prio_table_commit(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t\t const u8 *srcs)\n+{\n+\tstruct sit9531x_chan *chan = \u0026sitdev-\u003echan[pll_idx];\n+\tu8 val, last_old, slot, attempt, written = 0, restored = 0;\n+\tu8 now[SIT9531X_PRIO_MAX_SLOTS];\n+\tint rc = 0, prg_rc, ho_rc = 0;\n+\tbool empty, ho_foreign;\n+\tu16 reg;\n+\n+\tempty = !sit9531x_prio_src_usable(srcs[0]);\n+\n+\t/*\n+\t * A forced holdover already in place is this driver's own when the\n+\t * table lists nothing -- the hold an empty table keeps -- or when a\n+\t * release is still owed from an earlier write. Any other was put\n+\t * there by the loaded configuration or by a tool, and a table write\n+\t * is no reason to lift it: the sequence runs under it and leaves it\n+\t * as found.\n+\t */\n+\trc = sit9531x_read_pll_u8(sitdev, pll_idx, SIT9531X_PLL_REG_HO_CTRL,\n+\t\t\t\t \u0026val);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tho_foreign = (val \u0026 BIT(SIT9531X_PLL_HO_FORCE_BIT)) \u0026\u0026\n+\t\t chan-\u003eprio_mask \u0026\u0026 !chan-\u003eho_release_pending;\n+\n+\t/*\n+\t * A write that reports an error may still have reached the part, so\n+\t * a failure here does not prove the bit stayed clear: nothing is\n+\t * written to the table, but the release below still runs.\n+\t */\n+\trc = sit9531x_write_pll_u8(sitdev, pll_idx, SIT9531X_PLL_REG_HO_CTRL,\n+\t\t\t\t val | BIT(SIT9531X_PLL_HO_FORCE_BIT));\n+\tif (rc)\n+\t\tgoto release;\n+\n+\tusleep_range(10000, 12000);\n+\n+\t/*\n+\t * Two slots share a register, and this writes every slot, so both\n+\t * nibbles are known for every register but the last -- build those\n+\t * bytes outright. Reading first would raise the question of what a\n+\t * read returns between the write and the latch, and the answer does\n+\t * not matter if nothing is read.\n+\t */\n+\tfor (slot = 0; slot + 1 \u003c SIT9531X_PRIO_MAX_SLOTS; slot += 2) {\n+\t\treg = sit9531x_prio_reg(pll_idx, slot);\n+\n+\t\tval = sit9531x_prio_slot_set(0, slot, srcs[slot]);\n+\t\tval = sit9531x_prio_slot_set(val, slot + 1, srcs[slot + 1]);\n+\n+\t\t/*\n+\t\t * Counted before the write: a write that reports an error\n+\t\t * may still have reached the part, so the rollback has to\n+\t\t * cover that register too.\n+\t\t */\n+\t\twritten = slot + 2;\n+\t\trc = sit9531x_write_u8(sitdev, reg, val);\n+\t\tif (rc)\n+\t\t\tgoto rollback;\n+\t}\n+\n+\t/*\n+\t * The last register carries slot 10 in its high nibble and the\n+\t * device's active selection in its low one; see\n+\t * sit9531x_prio_activesel_pick() for how the selection is chosen.\n+\t * It needs the signal state now, not as of the last poll.\n+\t *\n+\t * The slot setter picks its nibble by parity, so the selection is\n+\t * addressed as the slot past the last one. This register has not\n+\t * been written yet in this sequence, so the read returns what the\n+\t * device is running with, and that byte is what a rollback puts\n+\t * back.\n+\t *\n+\t * A table naming no source at all is what removing the last one\n+\t * asks for. There is nothing to point the selection at -- the code\n+\t * for no source is not one the selection takes -- so the nibble is\n+\t * left alone and the PLL is kept in holdover below instead.\n+\t */\n+\treg = sit9531x_prio_reg(pll_idx, slot);\n+\n+\trc = sit9531x_read_u8(sitdev, reg, \u0026last_old);\n+\tif (rc)\n+\t\tgoto rollback;\n+\n+\tval = sit9531x_prio_slot_set(last_old, slot, srcs[slot]);\n+\n+\tif (!empty) {\n+\t\tu8 sel = sit9531x_prio_slot_get(val, slot + 1);\n+\n+\t\t/*\n+\t\t * Without a fresh read the pick would run on the last\n+\t\t * poll's monitor state, up to a poll period old. A source\n+\t\t * that lost its signal since would still look live, and a\n+\t\t * selection naming a dead source sends the PLL to it and it\n+\t\t * unlocks, so no selection is written without the read: the\n+\t\t * slots written so far are rolled back and the request\n+\t\t * fails.\n+\t\t */\n+\t\trc = sit9531x_input_mon_fetch(sitdev);\n+\t\tif (rc)\n+\t\t\tgoto rollback;\n+\t\tsel = sit9531x_prio_activesel_pick(sitdev, chan-\u003eprio_srcs,\n+\t\t\t\t\t\t srcs, sel);\n+\t\tval = sit9531x_prio_slot_set(val, slot + 1, sel);\n+\t}\n+\n+\twritten = SIT9531X_PRIO_MAX_SLOTS;\n+\trc = sit9531x_write_u8(sitdev, reg, val);\n+\tif (rc)\n+\t\tgoto rollback;\n+\n+rollback:\n+\tif (rc \u0026\u0026 written) {\n+\t\t/*\n+\t\t * Put the slots that may have reached the device back the\n+\t\t * way they were, the register whose write reported the error\n+\t\t * included. Latching a table that is neither the previous\n+\t\t * order nor the requested one hands the reference selection\n+\t\t * loop a priority list nobody asked for. The cache is the\n+\t\t * table as last read, which is what those slots held; the\n+\t\t * last register also carries the selection, so it goes back\n+\t\t * as it was read above.\n+\t\t */\n+\t\tfor (slot = 0; slot \u003c written; slot += 2) {\n+\t\t\tu8 old;\n+\n+\t\t\tif (slot + 1 \u003c SIT9531X_PRIO_MAX_SLOTS) {\n+\t\t\t\told = sit9531x_prio_slot_set(0, slot,\n+\t\t\t\t\t\t\t chan-\u003eprio_srcs[slot]);\n+\t\t\t\told = sit9531x_prio_slot_set(old, slot + 1,\n+\t\t\t\t\t\t\t chan-\u003eprio_srcs[slot + 1]);\n+\t\t\t} else {\n+\t\t\t\told = last_old;\n+\t\t\t}\n+\t\t\tif (sit9531x_write_u8(sitdev,\n+\t\t\t\t\t sit9531x_prio_reg(pll_idx, slot),\n+\t\t\t\t\t old))\n+\t\t\t\tbreak;\n+\n+\t\t\trestored = slot + 2;\n+\t\t}\n+\t\twritten = restored;\n+\t}\n+\n+\t/*\n+\t * Latch unconditionally: the slots that reached the device are in\n+\t * the table regardless, so the latch keeps hardware and the cache\n+\t * refresh below consistent with what was actually written.\n+\t */\n+\tprg_rc = sit9531x_prio_prg_commit(sitdev);\n+\tif (prg_rc \u0026\u0026 !rc)\n+\t\trc = prg_rc;\n+\n+\t/*\n+\t * Refresh the cache so a get that follows a set does not have to\n+\t * wait for the next poll. After a complete write that is the table\n+\t * just written. After a failure it is whatever the device holds\n+\t * now -- part request, part restore -- so read it back rather than\n+\t * piece it together: the membership test decides what a failed\n+\t * request rolls back, and it must not answer for writes that did not\n+\t * land. A read-back that fails too leaves the next poll to do it.\n+\t */\n+\tif (!rc) {\n+\t\tsit9531x_prio_mask_build(sitdev, pll_idx, srcs);\n+\t\tmemcpy(chan-\u003eseen_srcs, srcs, sizeof(chan-\u003eseen_srcs));\n+\t\tchan-\u003eseen_valid = true;\n+\t} else if (!sit9531x_prio_table_read(sitdev, pll_idx, now)) {\n+\t\tsit9531x_prio_mask_build(sitdev, pll_idx, now);\n+\t\tmemcpy(chan-\u003eseen_srcs, now, sizeof(chan-\u003eseen_srcs));\n+\t\tchan-\u003eseen_valid = true;\n+\t}\n+\n+release:\n+\t/*\n+\t * A table that names no source keeps the PLL in the holdover forced\n+\t * above: that is the one state in which it follows no input, which\n+\t * is what disconnecting every input asks for. The selection nibble\n+\t * alone would not do it -- it still names the old source, and the\n+\t * PLL keeps following that one for as long as it has signal. The\n+\t * next table write that lists a source releases it.\n+\t *\n+\t * Judged on the table the device holds now, as the refresh above\n+\t * read it, not on the one requested: a write that failed on a PLL\n+\t * listing nothing leaves it listing nothing, and releasing the hold\n+\t * then would hand the PLL back to the source the selection still\n+\t * names while every pin reports disconnected. An empty table whose\n+\t * latch failed is the same case from the other side.\n+\t */\n+\tif (!chan-\u003eprio_mask) {\n+\t\tdev_dbg(sitdev-\u003edev,\n+\t\t\t\"PLL%c: no source listed, holdover kept\\n\",\n+\t\t\t'A' + pll_idx);\n+\t\tchan-\u003eho_release_pending = false;\n+\t\treturn rc;\n+\t}\n+\n+\tif (ho_foreign) {\n+\t\tdev_dbg(sitdev-\u003edev,\n+\t\t\t\"PLL%c: holdover was forced before the write, left in place\\n\",\n+\t\t\t'A' + pll_idx);\n+\t\treturn rc;\n+\t}\n+\n+\t/*\n+\t * Release the forced holdover. Apart from an empty table, nothing\n+\t * in the driver keeps this bit set, and a retry of the same request\n+\t * finds the table already as asked and does not come back here, so\n+\t * a PLL left with it would report holdover until some unrelated\n+\t * table write on the same PLL cleared it. Retry before giving up;\n+\t * if it stays set, say so and leave the release owed to the poll,\n+\t * which retries it every tick until it lands.\n+\t */\n+\tfor (attempt = 0; attempt \u003c SIT9531X_HO_CLEAR_TRIES; attempt++) {\n+\t\tho_rc = sit9531x_update_pll_u8(sitdev, pll_idx,\n+\t\t\t\t\t SIT9531X_PLL_REG_HO_CTRL,\n+\t\t\t\t\t BIT(SIT9531X_PLL_HO_FORCE_BIT),\n+\t\t\t\t\t 0);\n+\t\tif (!ho_rc)\n+\t\t\tbreak;\n+\t\tusleep_range(1000, 2000);\n+\t}\n+\tif (ho_rc) {\n+\t\tdev_err(sitdev-\u003edev, \"PLL%c left in forced holdover: %d\\n\",\n+\t\t\t'A' + pll_idx, ho_rc);\n+\t\tif (!rc)\n+\t\t\trc = ho_rc;\n+\t}\n+\tchan-\u003eho_release_pending = !!ho_rc;\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * sit9531x_prio_table_read - read a PLL's priority-table source codes\n+ * @srcs:\toutput array of SIT9531X_PRIO_MAX_SLOTS source codes\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+static int sit9531x_prio_table_read(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t\t u8 *srcs)\n+{\n+\tu8 val, slot;\n+\tint rc;\n+\n+\tfor (slot = 0; slot \u003c SIT9531X_PRIO_MAX_SLOTS; slot++) {\n+\t\trc = sit9531x_read_u8(sitdev,\n+\t\t\t\t sit9531x_prio_reg(pll_idx, slot), \u0026val);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\n+\t\tsrcs[slot] = sit9531x_prio_slot_get(val, slot);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Rewrite a PLL's table for a new member set, unless it would come out as\n+ * the table already holds: every write forces the PLL into holdover for\n+ * the length of the sequence, so one that changes nothing is a\n+ * disturbance nobody asked for.\n+ */\n+static int sit9531x_prio_table_apply(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t\t u16 members)\n+{\n+\tu8 srcs[SIT9531X_PRIO_MAX_SLOTS];\n+\n+\tif (hweight16(members) \u003e SIT9531X_PRIO_MAX_SLOTS)\n+\t\treturn -ENOSPC;\n+\n+\tsit9531x_prio_table_build(sitdev, pll_idx, members, srcs);\n+\tif (!memcmp(srcs, sitdev-\u003echan[pll_idx].prio_srcs, sizeof(srcs)))\n+\t\treturn 0;\n+\n+\treturn sit9531x_prio_table_commit(sitdev, pll_idx, srcs);\n+}\n+\n+/*\n+ * sit9531x_input_prio_set - set an input's priority on a PLL\n+ * @input_idx:\tinput source in hardware encoding (0-11, see\n+ *\t\tsit9531x_input_hw_src())\n+ * @prio:\tpriority, lower is preferred\n+ *\n+ * Records the priority and, when the source is in the PLL's table,\n+ * rebuilds the table from the configured priorities. A source that is\n+ * not in the table keeps the priority for when it is connected: that is\n+ * the pin's state, and it belongs to the state setter. Other sources\n+ * keep theirs either way, so no sibling's priority moves.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ *\n+ * Return: 0 on success, -EINVAL for a bad PLL or source, \u003c0 on error\n+ */\n+int sit9531x_input_prio_set(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t u8 input_idx, u8 prio)\n+{\n+\tstruct sit9531x_chan *chan;\n+\tu8 old_prio;\n+\tu16 old_known;\n+\tint rc;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (pll_idx \u003e= SIT9531X_NUM_PLLS)\n+\t\treturn -EINVAL;\n+\tinput_idx = sit9531x_prio_src_canon(sitdev, input_idx);\n+\tif (input_idx \u003e= SIT9531X_PRIO_NUM_SRC)\n+\t\treturn -EINVAL;\n+\n+\tchan = \u0026sitdev-\u003echan[pll_idx];\n+\told_prio = chan-\u003ecfg_prio[input_idx];\n+\told_known = chan-\u003ecfg_known;\n+\tchan-\u003ecfg_prio[input_idx] = prio;\n+\tchan-\u003ecfg_known |= BIT(input_idx);\n+\n+\tif (!(chan-\u003eprio_mask \u0026 BIT(input_idx)))\n+\t\treturn 0;\n+\n+\t/*\n+\t * The table is built from the configured priority, so it has to be\n+\t * recorded before the rebuild; a rebuild that failed takes the\n+\t * record back with it. Left in place, a refused priority would be\n+\t * reported by the getter, notified by the poll, and written by the\n+\t * next rebuild for any other reason.\n+\t */\n+\trc = sit9531x_prio_table_apply(sitdev, pll_idx, chan-\u003eprio_mask);\n+\tif (rc) {\n+\t\tchan-\u003ecfg_prio[input_idx] = old_prio;\n+\t\tchan-\u003ecfg_known = old_known;\n+\t}\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * sit9531x_input_prio_remove - drop an input from a PLL's priority table\n+ * @input_idx:\tinput source in hardware encoding\n+ *\n+ * Rebuilds the table without the source, which makes a disconnected\n+ * input ineligible for automatic reference selection, not just gated at\n+ * the input buffer. The source keeps its configured priority for when it\n+ * comes back. Removing a source that is absent succeeds without touching\n+ * the table. Removing the last one leaves a table that names no source;\n+ * the commit then keeps the PLL in holdover, which is what disconnecting\n+ * every input asks for.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ *\n+ * Return: 0 on success, \u003c0 on error\n+ */\n+int sit9531x_input_prio_remove(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t u8 input_idx)\n+{\n+\tstruct sit9531x_chan *chan;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (pll_idx \u003e= SIT9531X_NUM_PLLS)\n+\t\treturn -EINVAL;\n+\tinput_idx = sit9531x_prio_src_canon(sitdev, input_idx);\n+\tif (input_idx \u003e= SIT9531X_PRIO_NUM_SRC)\n+\t\treturn -EINVAL;\n+\n+\tchan = \u0026sitdev-\u003echan[pll_idx];\n+\tif (!(chan-\u003eprio_mask \u0026 BIT(input_idx)))\n+\t\treturn 0;\n+\n+\treturn sit9531x_prio_table_apply(sitdev, pll_idx,\n+\t\t\t\t\t chan-\u003eprio_mask \u0026 ~BIT(input_idx));\n+}\n+\n+/*\n+ * sit9531x_input_prio_add - make an input eligible in a PLL's table\n+ * @input_idx:\tinput source in hardware encoding\n+ *\n+ * Puts the source back into the table at its configured priority; one the\n+ * PLL never listed has the lowest slot (see sit9531x_prio_cfg_seed()). A\n+ * source that is already listed leaves the table untouched.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ *\n+ * Return: 0 on success, -ENOSPC when the table cannot hold another\n+ * source, \u003c0 on error\n+ */\n+int sit9531x_input_prio_add(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t u8 input_idx)\n+{\n+\tstruct sit9531x_chan *chan;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (pll_idx \u003e= SIT9531X_NUM_PLLS)\n+\t\treturn -EINVAL;\n+\tinput_idx = sit9531x_prio_src_canon(sitdev, input_idx);\n+\tif (input_idx \u003e= SIT9531X_PRIO_NUM_SRC)\n+\t\treturn -EINVAL;\n+\n+\tchan = \u0026sitdev-\u003echan[pll_idx];\n+\tif (chan-\u003eprio_mask \u0026 BIT(input_idx))\n+\t\treturn 0;\n+\n+\treturn sit9531x_prio_table_apply(sitdev, pll_idx,\n+\t\t\t\t\t chan-\u003eprio_mask | BIT(input_idx));\n+}\n+\n+/* Per-slot DIVO base register offsets (6 slots per page) */\n+static const u8 clkout_odr_divn_base[] = {\n+\t0x14, 0x24, 0x34, 0x44, 0x54, 0x64\n+};\n+\n+/* XO doubler register */\n+#define SIT9531X_REG_XO2_GENERIC\t\tSIT9531X_REG(0x00, 0x2D)\n+#define SIT9531X_XO_DOUBLER_ENB_BIT\t\t7 /* inverted: 0 = enabled */\n+\n+/* VCO frequency bands (Hz) */\n+#define SIT9531X_FVCO_LOWBAND_MIN\t\t4915200000ULL\n+#define SIT9531X_FVCO_LOWBAND_MAX\t\t5898240000ULL\n+#define SIT9531X_FVCO_HIGHBAND_MIN\t\t6875000000ULL\n+#define SIT9531X_FVCO_HIGHBAND_MAX\t\t7812500000ULL\n+\n+/* The output divider is a 34-bit field */\n+#define SIT9531X_DIVO_MAX\t\t\tGENMASK_ULL(33, 0)\n+\n+/*\n+ * sit9531x_is_xo_doubler_enabled - check if Fref doubler is active\n+ *\n+ * Register 0x2D bit 7 is active-low: 0 = doubler enabled, 1 = disabled.\n+ *\n+ * Return: 1 if enabled, 0 if disabled, \u003c0 on error\n+ */\n+static int sit9531x_is_xo_doubler_enabled(struct sit9531x_dev *sitdev)\n+{\n+\tu8 val;\n+\tint rc;\n+\n+\trc = sit9531x_read_u8(sitdev, SIT9531X_REG_XO2_GENERIC, \u0026val);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\treturn (~val \u003e\u003e SIT9531X_XO_DOUBLER_ENB_BIT) \u0026 1u;\n+}\n+\n+/*\n+ * DIVN as a fixed-point value: int_part plus fracn/fracd, carried with\n+ * SIT9531X_DIVN_SCALE steps per unit. The scale keeps a whole DIVN\n+ * well inside s64 while resolving far below the parts-per-trillion the\n+ * frequency offset is reported in.\n+ */\n+static s64 sit9531x_divn_fixed(u32 int_part, s64 fracn, u64 fracd)\n+{\n+\ts64 whole = (s64)int_part * SIT9531X_DIVN_SCALE;\n+\tu64 frac;\n+\n+\tif (!fracd)\n+\t\treturn whole;\n+\n+\tfrac = mul_u64_u64_div_u64(abs(fracn), SIT9531X_DIVN_SCALE, fracd);\n+\n+\treturn fracn \u003c 0 ? whole - (s64)frac : whole + (s64)frac;\n+}\n+\n+/*\n+ * sit9531x_divn_static - read the configured DIVN of a PLL\n+ * @sitdev:\tdevice pointer\n+ * @pll_idx:\tPLL index (0-3)\n+ * @divn:\tresult, fixed point as per sit9531x_divn_fixed()\n+ *\n+ * Reads PLL page regs 0x30 (integer part), 0x32-0x35 (numerator) and\n+ * 0x38-0x3B (denominator). The numerator is a two's complement 32-bit\n+ * value, so DIVN can sit below the integer part, and the denominator\n+ * register holds the divisor minus one.\n+ *\n+ * Return: 0 on success, \u003c0 on error\n+ */\n+static int sit9531x_divn_static(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t\ts64 *divn)\n+{\n+\tu32 int_part, fracn_raw = 0, fracd_raw = 0;\n+\tu64 fracd;\n+\ts64 fracn;\n+\tint rc, i;\n+\tu8 v;\n+\n+\trc = sit9531x_read_pll_u8(sitdev, pll_idx,\n+\t\t\t\t SIT9531X_PLL_REG_DIVN_INT, \u0026v);\n+\tif (rc)\n+\t\treturn rc;\n+\tint_part = v;\n+\n+\tfor (i = 3; i \u003e= 0; i--) {\n+\t\trc = sit9531x_read_pll_u8(sitdev, pll_idx,\n+\t\t\t\t\t SIT9531X_PLL_REG_DIVN_NUM + i, \u0026v);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\t\tfracn_raw = (fracn_raw \u003c\u003c 8) | v;\n+\t}\n+\n+\tfor (i = 3; i \u003e= 0; i--) {\n+\t\trc = sit9531x_read_pll_u8(sitdev, pll_idx,\n+\t\t\t\t\t SIT9531X_PLL_REG_DIVN_DEN + i, \u0026v);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\t\tfracd_raw = (fracd_raw \u003c\u003c 8) | v;\n+\t}\n+\n+\t/*\n+\t * NUM/DEN is the fractional part of DIVN, so |NUM| is below DEN by\n+\t * construction. A pair that says otherwise did not come from a\n+\t * programmed divider, and handing it on would divide by a\n+\t * denominator small enough for the quotient to leave u64 -- which\n+\t * is a divide-error exception on x86, not a value a caller could\n+\t * reject.\n+\t */\n+\tfracn = (s32)fracn_raw;\n+\tfracd = (u64)fracd_raw + 1;\n+\tif ((u64)abs(fracn) \u003e= fracd)\n+\t\treturn -ENODATA;\n+\n+\t*divn = sit9531x_divn_fixed(int_part, fracn, fracd);\n+\n+\treturn 0;\n+}\n+\n+/* PLLB and PLLD run their VCO in the high band, PLLA and PLLC in the low. */\n+static bool sit9531x_fvco_in_band(u8 pll_idx, u64 fvco)\n+{\n+\tif (pll_idx == 1 || pll_idx == 3)\n+\t\treturn fvco \u003e= SIT9531X_FVCO_HIGHBAND_MIN \u0026\u0026\n+\t\t fvco \u003c= SIT9531X_FVCO_HIGHBAND_MAX;\n+\treturn fvco \u003e= SIT9531X_FVCO_LOWBAND_MIN \u0026\u0026\n+\t fvco \u003c= SIT9531X_FVCO_LOWBAND_MAX;\n+}\n+\n+/*\n+ * sit9531x_get_fvco - read VCO frequency from chip's DIVN registers\n+ *\n+ * Fvco = Fref * DIVN, where DIVN comes from sit9531x_divn_static() and\n+ * Fref = xtal_freq \u003c\u003c doubler. DIVN is the steady-state Fvco/Fref\n+ * target programmed by the NVM blob and is authoritative in both\n+ * free-run and sync modes.\n+ *\n+ * Return: 0 with *fvco set on success, -ENODATA when DIVN is not\n+ * programmed (dormant PLL), or the register access error. A bus\n+ * failure is never folded into the -ENODATA case, so callers can fail\n+ * a request instead of acting on a guessed rate.\n+ */\n+static int sit9531x_get_fvco(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t u64 *fvco)\n+{\n+\tint doubler, rc;\n+\tu64 fref;\n+\ts64 divn;\n+\n+\trc = sit9531x_divn_static(sitdev, pll_idx, \u0026divn);\n+\tif (rc)\n+\t\treturn rc;\n+\tif (divn \u003c= 0)\n+\t\treturn -ENODATA;\n+\n+\tdoubler = sit9531x_is_xo_doubler_enabled(sitdev);\n+\tif (doubler \u003c 0)\n+\t\treturn doubler;\n+\n+\tfref = (u64)sitdev-\u003extal_freq \u003c\u003c doubler;\n+\n+\t/*\n+\t * Round to the nearest hertz: DIVN is carried in fixed point, so a\n+\t * fraction such as 1/6 is already floored once, and flooring the\n+\t * product again reads an exact 5 GHz VCO as 4999999999 Hz -- which\n+\t * then refuses every rate the VCO divides exactly.\n+\t */\n+\t*fvco = mul_u64_u64_div_u64(2 * fref, (u64)divn, SIT9531X_DIVN_SCALE);\n+\t*fvco = (*fvco + 1) / 2;\n+\n+\t/*\n+\t * No VCO of this family runs below the low band, so a product under\n+\t * it is not a rate: a DIVN of a cycle or less, say, which the check\n+\t * above lets through and which a profile that never programmed the\n+\t * divider can leave behind. Callers scale 34-bit register fields by\n+\t * 1e12 / Fvco or more, and for a rate far enough below the band that\n+\t * quotient leaves u64 -- a divide-error exception on x86, not a\n+\t * value they could reject. Report the unprogrammed divider instead.\n+\t */\n+\tif (*fvco \u003c SIT9531X_FVCO_LOWBAND_MIN)\n+\t\treturn -ENODATA;\n+\n+\t/*\n+\t * A rate outside the band the PLL's VCO runs in usually means\n+\t * Fref * DIVN is not what this PLL runs at -- a PLL fed from another\n+\t * PLL rather than from the XO, for one. The rate derived from the\n+\t * registers is still the only estimate there is, so a read reports\n+\t * what it derives; a set refuses to program a divider against it\n+\t * (sit9531x_output_divo_calc()), since substituting the band edge\n+\t * would program against a rate nothing supports and report the\n+\t * result as exact. Say so once per PLL.\n+\t */\n+\tif (!sit9531x_fvco_in_band(pll_idx, *fvco) \u0026\u0026\n+\t !(sitdev-\u003efvco_band_warned \u0026 BIT(pll_idx))) {\n+\t\tsitdev-\u003efvco_band_warned |= BIT(pll_idx);\n+\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t \"PLL%c: Fref * DIVN = %llu Hz is outside its VCO band\\n\",\n+\t\t\t 'A' + pll_idx, *fvco);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/* Latch a change to a PLL page with a small update. */\n+static int sit9531x_pll_small_update(struct sit9531x_dev *sitdev, u8 pll_idx)\n+{\n+\treturn sit9531x_write_pll_u8(sitdev, pll_idx,\n+\t\t\t\t SIT9531X_PLL_REG_SMALL_UPDATE,\n+\t\t\t\t SIT9531X_SMALL_UPDATE_CMD);\n+}\n+\n+/*\n+ * sit9531x_output_phase_flush - flush the output phase of a PLL\n+ *\n+ * Fires the chip's on-demand phase-flush (PHFL) so every output divider\n+ * of @pll_idx restarts aligned to the PLL phase. Without it a rewritten\n+ * DIVO keeps counting from an arbitrary point and the output edge lands\n+ * with a persistent offset against the tracked reference (only a power\n+ * cycle realigned it).\n+ *\n+ * The sequence mirrors the documented procedure: arm the on-demand PHFL and\n+ * latch it with the PLL-page small-change update, then select the\n+ * in-register phase trigger on Page 0 and pulse it. The Page 0 trigger\n+ * register is touched read-modify-write so the unrelated OEb bits are\n+ * preserved.\n+ *\n+ * A PLL the loaded configuration builds without the phase-flush feature\n+ * (PLL page 0x47 bit 7 clear) has nothing to fire; it is restarted\n+ * instead, which restarts its output dividers from the PLL phase. That\n+ * is what SiTime's Dely_program_output.py does for such a PLL.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+static int sit9531x_output_phase_flush(struct sit9531x_dev *sitdev, u8 pll_idx)\n+{\n+\tu8 ctrl, orig, phfl, armed_mask = 0, parked = 0, i;\n+\tint rc, ret;\n+\n+\trc = sit9531x_read_pll_u8(sitdev, pll_idx, SIT9531X_PLL_REG_CONFIG47,\n+\t\t\t\t \u0026phfl);\n+\tif (rc)\n+\t\treturn rc;\n+\tif (!(phfl \u0026 SIT9531X_PLL_CONFIG47_PHFL_EN))\n+\t\treturn sit9531x_write_pll_u8(sitdev, pll_idx,\n+\t\t\t\t\t SIT9531X_PLL_REG_DIRECTIVES,\n+\t\t\t\t\t SIT9531X_PLL_DIRECTIVE_RESTART);\n+\n+\t/*\n+\t * The trigger below is chip-global: every PLL whose on-demand flush\n+\t * is armed answers it. Note which ones the loaded profile arms --\n+\t * this one so its setting can be put back, the others so they can\n+\t * be parked for the pulse and do not realign outputs nobody asked\n+\t * to move.\n+\t */\n+\tfor (i = 0; i \u003c SIT9531X_NUM_PLLS; i++) {\n+\t\trc = sit9531x_read_pll_u8(sitdev, i, SIT9531X_PLL_REG_PHFL_CTRL,\n+\t\t\t\t\t \u0026phfl);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\t\tif (phfl \u0026 SIT9531X_PLL_PHFL_ON_DEMAND_EN)\n+\t\t\tarmed_mask |= BIT(i);\n+\t}\n+\n+\tfor (i = 0; i \u003c SIT9531X_NUM_PLLS; i++) {\n+\t\tif (i == pll_idx || !(armed_mask \u0026 BIT(i)))\n+\t\t\tcontinue;\n+\t\t/*\n+\t\t * A clear whose write reported an error may still have\n+\t\t * landed, latched or not: put it back either way.\n+\t\t */\n+\t\tparked |= BIT(i);\n+\t\trc = sit9531x_update_pll_u8(sitdev, i,\n+\t\t\t\t\t SIT9531X_PLL_REG_PHFL_CTRL,\n+\t\t\t\t\t SIT9531X_PLL_PHFL_ON_DEMAND_EN, 0);\n+\t\tif (rc)\n+\t\t\tgoto unpark;\n+\t\trc = sit9531x_pll_small_update(sitdev, i);\n+\t\tif (rc)\n+\t\t\tgoto unpark;\n+\t}\n+\n+\t/*\n+\t * Arm the on-demand phase-flush on the PLL page. An arm whose write\n+\t * failed may have landed too, so it goes through the disarm below.\n+\t */\n+\trc = sit9531x_update_pll_u8(sitdev, pll_idx,\n+\t\t\t\t SIT9531X_PLL_REG_PHFL_CTRL,\n+\t\t\t\t SIT9531X_PLL_PHFL_ON_DEMAND_EN,\n+\t\t\t\t SIT9531X_PLL_PHFL_ON_DEMAND_EN);\n+\tif (rc)\n+\t\tgoto disarm;\n+\n+\t/*\n+\t * Latch it with the PLL small-change update. Written whole, like\n+\t * every other issue of this directive: the register is a command\n+\t * register, and a read-modify-write skips the write entirely when\n+\t * the bit still reads back set from the previous command.\n+\t */\n+\trc = sit9531x_pll_small_update(sitdev, pll_idx);\n+\tif (rc)\n+\t\tgoto disarm;\n+\n+\t/*\n+\t * Select the in-register phase trigger, preserving the OEb bits.\n+\t * Remember the original register value (with the trigger de-asserted)\n+\t * so the trigger-source select can be restored once the pulse has\n+\t * fired.\n+\t */\n+\trc = sit9531x_read_u8(sitdev, SIT9531X_REG_GPIO_FUNC_CTRL1, \u0026ctrl);\n+\tif (rc)\n+\t\tgoto disarm;\n+\n+\torig = ctrl \u0026 ~SIT9531X_DIVO_PHASE_TRIG;\n+\tctrl = orig | SIT9531X_DIVO_PHASE_SEL_REG;\n+\trc = sit9531x_write_u8(sitdev, SIT9531X_REG_GPIO_FUNC_CTRL1, ctrl);\n+\tif (rc)\n+\t\tgoto restore;\n+\n+\t/*\n+\t * Pulse the phase trigger. No explicit delay is needed between the\n+\t * set and clear writes: each I2C transaction takes far longer than\n+\t * any minimum pulse width.\n+\t */\n+\trc = sit9531x_write_u8(sitdev, SIT9531X_REG_GPIO_FUNC_CTRL1,\n+\t\t\t ctrl | SIT9531X_DIVO_PHASE_TRIG);\n+\n+restore:\n+\t/*\n+\t * Restore the original trigger-source select. The pulse above has\n+\t * already latched the flush, so a one-shot flush must not leave the\n+\t * phase trigger permanently pinned to the in-register source. This\n+\t * runs even when the pulse write failed, and when the select write\n+\t * itself reported a failure, since the transfer may have reached\n+\t * the part all the same; otherwise a failed flush would keep a\n+\t * hardware trigger source hijacked. The restore error is only\n+\t * surfaced when it would not mask the earlier failure.\n+\t */\n+\tret = sit9531x_write_u8(sitdev, SIT9531X_REG_GPIO_FUNC_CTRL1, orig);\n+\tif (ret \u0026\u0026 !rc)\n+\t\trc = ret;\n+\n+disarm:\n+\t/*\n+\t * Put the on-demand flush enable back as the profile had it. One the\n+\t * profile left clear must not stay armed: a later assertion of the\n+\t * restored trigger source would re-flush every output divider of this\n+\t * PLL. One the profile armed -- a board realigning from a GPIO, say\n+\t * -- must not be switched off by an unrelated rate change.\n+\t */\n+\tif (!(armed_mask \u0026 BIT(pll_idx))) {\n+\t\tret = sit9531x_update_pll_u8(sitdev, pll_idx,\n+\t\t\t\t\t SIT9531X_PLL_REG_PHFL_CTRL,\n+\t\t\t\t\t SIT9531X_PLL_PHFL_ON_DEMAND_EN, 0);\n+\t\tif (!ret)\n+\t\t\tret = sit9531x_pll_small_update(sitdev, pll_idx);\n+\t\tif (ret \u0026\u0026 !rc)\n+\t\t\trc = ret;\n+\t}\n+\n+unpark:\n+\tfor (i = 0; i \u003c SIT9531X_NUM_PLLS; i++) {\n+\t\tif (!(parked \u0026 BIT(i)))\n+\t\t\tcontinue;\n+\t\tret = sit9531x_update_pll_u8(sitdev, i,\n+\t\t\t\t\t SIT9531X_PLL_REG_PHFL_CTRL,\n+\t\t\t\t\t SIT9531X_PLL_PHFL_ON_DEMAND_EN,\n+\t\t\t\t\t SIT9531X_PLL_PHFL_ON_DEMAND_EN);\n+\t\tif (!ret)\n+\t\t\tret = sit9531x_pll_small_update(sitdev, i);\n+\t\tif (ret \u0026\u0026 !rc)\n+\t\t\trc = ret;\n+\t}\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * sit9531x_output_divo_calc - work out an output's divider and its VCO\n+ *\n+ * Separated from the write so a caller that programs more than the\n+ * divider in one sequence can compute the value before it enters the\n+ * programming state.\n+ */\n+static int sit9531x_output_divo_calc(struct sit9531x_dev *sitdev, u8 out_idx,\n+\t\t\t\t u8 pll_idx, u64 frequency, u64 *fvco_out,\n+\t\t\t\t u64 *divo_out)\n+{\n+\tconst struct sit9531x_chip_info *info = sitdev-\u003einfo;\n+\tu64 fvco, divo;\n+\tint rc;\n+\n+\tif (out_idx \u003e= info-\u003enum_outputs || pll_idx \u003e= SIT9531X_NUM_PLLS)\n+\t\treturn -EINVAL;\n+\n+\tif (!frequency)\n+\t\treturn -EINVAL;\n+\n+\t/*\n+\t * The core validates the request against the supported ranges with\n+\t * the value narrowed to u32 but hands the full u64 down, so a value\n+\t * like U32_MAX + 1 Hz validates as 1 Hz. Reject anything that does\n+\t * not fit the narrowed width the validation actually covered.\n+\t */\n+\tif (frequency \u003e U32_MAX)\n+\t\treturn -EINVAL;\n+\n+\t/*\n+\t * sit9531x_get_fvco() returns the register-derived rate, so a\n+\t * frequency get and a frequency set divide the same number. A VCO\n+\t * that cannot be read fails the request: programming a divider from a\n+\t * guessed rate would put the output far from what was asked for\n+\t * while reporting success.\n+\t */\n+\trc = sit9531x_get_fvco(sitdev, pll_idx, \u0026fvco);\n+\tif (rc)\n+\t\treturn rc == -ENODATA ? -ENODEV : rc;\n+\n+\t/*\n+\t * Fref * DIVN outside the VCO band is not what the PLL runs at, and\n+\t * sit9531x_get_fvco() has said so. A divider computed against it\n+\t * would put the output at some other rate while the exactness check\n+\t * below passes and the read-back, dividing the same number, confirms\n+\t * the request. Refuse the set instead.\n+\t */\n+\tif (!sit9531x_fvco_in_band(pll_idx, fvco)) {\n+\t\tdev_dbg(sitdev-\u003edev,\n+\t\t\t\"out%u: PLL%c Fvco %llu Hz is outside its VCO band, rate not settable\\n\",\n+\t\t\tout_idx, 'A' + pll_idx, fvco);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/*\n+\t * Round to nearest rather than down: flooring picks the worse of the\n+\t * two adjacent dividers whenever the remainder is above half the\n+\t * request.\n+\t */\n+\tdivo = div64_u64(fvco + frequency / 2, frequency);\n+\tif (!divo)\n+\t\treturn -EINVAL;\n+\n+\t/* DIVO is a 34-bit field; refuse a wider divider rather than truncate it. */\n+\tif (divo \u003e SIT9531X_DIVO_MAX)\n+\t\treturn -EINVAL;\n+\n+\t/*\n+\t * The output divider is an integer divider of the VCO, so the only\n+\t * rates the part can make are Fvco/N. An output pin that lists no\n+\t * supported frequencies advertises a continuous range, because the\n+\t * divisors cannot be enumerated ahead of a known Fvco, so a request\n+\t * for a rate between two of them arrives here. Refuse it: running\n+\t * the output at the nearest divider instead and reporting success\n+\t * would leave the pin several percent off what was asked for with\n+\t * nothing saying so.\n+\t */\n+\tif (div64_u64(fvco, divo) != frequency) {\n+\t\tdev_dbg(sitdev-\u003edev,\n+\t\t\t\"out%u: %llu Hz is not Fvco/N (Fvco=%llu, nearest %llu Hz)\\n\",\n+\t\t\tout_idx, frequency, fvco, div64_u64(fvco, divo));\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tdev_dbg(sitdev-\u003edev,\n+\t\t\"out%u: Fvco=%llu freq=%llu DIVO=%llu (effective %llu Hz)\\n\",\n+\t\tout_idx, fvco, frequency, divo, div64_u64(fvco, divo));\n+\n+\t*fvco_out = fvco;\n+\t*divo_out = divo;\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * sit9531x_output_divo_write - write the five DIVO bytes of an output\n+ *\n+ * The caller must already be in the programming state. Bytes written\n+ * before a failure are put back, so the output keeps the divider it had\n+ * rather than a mixture of the two.\n+ */\n+static int sit9531x_output_divo_write(struct sit9531x_dev *sitdev, u8 out_idx,\n+\t\t\t\t u64 divo)\n+{\n+\tconst struct sit9531x_chip_info *info = sitdev-\u003einfo;\n+\tu8 slot, page, base_reg, divo_bytes[5], old_bytes[5], msb_old;\n+\tint rc, j, rb_rc;\n+\tu8 written = 0;\n+\n+\t/* Map output index to physical slot */\n+\tslot = info-\u003eclkout_map[out_idx];\n+\n+\t/* Determine page and per-page slot register */\n+\tif (slot \u003e SIT9531X_PAGE_OUTSYS0_SLOT_MAX)\n+\t\tpage = SIT9531X_PAGE_OUTSYS1;\n+\telse\n+\t\tpage = SIT9531X_PAGE_OUTSYS0;\n+\tbase_reg = clkout_odr_divn_base[slot % 6];\n+\n+\tdivo_bytes[0] = (divo \u003e\u003e 0) \u0026 0xFF;\n+\tdivo_bytes[1] = (divo \u003e\u003e 8) \u0026 0xFF;\n+\tdivo_bytes[2] = (divo \u003e\u003e 16) \u0026 0xFF;\n+\tdivo_bytes[3] = (divo \u003e\u003e 24) \u0026 0xFF;\n+\tdivo_bytes[4] = (divo \u003e\u003e 32) \u0026 0x03; /* only bits [1:0] */\n+\n+\tfor (j = 0; j \u003c 5; j++) {\n+\t\trc = sit9531x_read_u8(sitdev,\n+\t\t\t\t SIT9531X_REG(page, base_reg - j),\n+\t\t\t\t \u0026old_bytes[j]);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\t}\n+\n+\tmsb_old = old_bytes[4];\n+\tdivo_bytes[4] |= msb_old \u0026 0xFC;\n+\n+\tfor (j = 0; j \u003c 5; j++) {\n+\t\trc = sit9531x_write_u8(sitdev,\n+\t\t\t\t SIT9531X_REG(page, base_reg - j),\n+\t\t\t\t divo_bytes[j]);\n+\t\tif (rc)\n+\t\t\tgoto rollback;\n+\t\twritten++;\n+\t}\n+\n+\treturn 0;\n+\n+rollback:\n+\t/*\n+\t * The byte whose write reported the error may still have reached\n+\t * the part, so it is put back along with the ones that did.\n+\t */\n+\tfor (j = 0; j \u003c= written \u0026\u0026 j \u003c 5; j++) {\n+\t\trb_rc = sit9531x_write_u8(sitdev,\n+\t\t\t\t\t SIT9531X_REG(page, base_reg - j),\n+\t\t\t\t\t old_bytes[j]);\n+\t\tif (rb_rc) {\n+\t\t\tdev_err(sitdev-\u003edev,\n+\t\t\t\t\"out%u: DIVO rollback failed (%d), the divider is part old and part new\\n\",\n+\t\t\t\tout_idx, rb_rc);\n+\t\t\tif (!rc)\n+\t\t\t\trc = rb_rc;\n+\t\t}\n+\t}\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * Read an output's divider back from the device.\n+ *\n+ * Return: 0 with *divo set, -ENODATA when the divider is not programmed,\n+ * or the register access error\n+ */\n+static int sit9531x_output_divo_read(struct sit9531x_dev *sitdev, u8 out_idx,\n+\t\t\t\t u64 *divo)\n+{\n+\tu8 slot, page, base_reg, v;\n+\tint rc, j;\n+\n+\tslot = sitdev-\u003einfo-\u003eclkout_map[out_idx];\n+\tif (slot \u003e SIT9531X_PAGE_OUTSYS0_SLOT_MAX)\n+\t\tpage = SIT9531X_PAGE_OUTSYS1;\n+\telse\n+\t\tpage = SIT9531X_PAGE_OUTSYS0;\n+\tbase_reg = clkout_odr_divn_base[slot % 6];\n+\n+\t*divo = 0;\n+\tfor (j = 4; j \u003e= 0; j--) {\n+\t\trc = sit9531x_read_u8(sitdev,\n+\t\t\t\t SIT9531X_REG(page, base_reg - j), \u0026v);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\t\tif (j == 4)\n+\t\t\tv \u0026= 0x03;\n+\t\t*divo = (*divo \u003c\u003c 8) | v;\n+\t}\n+\n+\treturn *divo ? 0 : -ENODATA;\n+}\n+\n+/*\n+ * Phase adjust (PRG_RST_DELAY register-based).\n+ *\n+ * The chip exposes a per-output 34-bit coarse delay measured in VCO\n+ * clock periods plus a 3-bit fine delay in fixed 30 ps steps. The\n+ * five bytes PROG6..PROG2 hold the field across registers:\n+ * base + 0 PROG6 [7:5] OPSTG_VCASC_BUMP (preserved via RMW)\n+ * [4:2] PRG_RST_FINE_DELAY\n+ * [1:0] PRG_RST_DELAY[33:32]\n+ * base + 1 PROG5 PRG_RST_DELAY[31:24]\n+ * base + 2 PROG4 PRG_RST_DELAY[23:16]\n+ * base + 3 PROG3 PRG_RST_DELAY[15:8]\n+ * base + 4 PROG2 PRG_RST_DELAY[7:0]\n+ *\n+ * Slots 0-5 live on Page 3, slots 6-11 on Page 4, with each slot's\n+ * block at base = 0x15 + 16 * (slot % 6); the slot is the physical\n+ * output position from clkout_map[], not the logical output index.\n+ *\n+ * The chip only supports unsigned positive delay. Requests are folded\n+ * modulo one output period: positive delays wrap naturally and a negative\n+ * phase adjustment (advance) is rendered as (T_out - |phase|).\n+ */\n+\n+/*\n+ * Register of byte @i (PROG6 first) of an output's PRG_RST_DELAY block.\n+ *\n+ * The logical output index maps to the chip's physical output slot. On\n+ * SiT95317 the eight logical outputs land on chip slots {0, 3, 4, 5, 7,\n+ * 8, 9, 11}; on SiT95316 the map is identity. Page and base address the\n+ * slot, not the logical index.\n+ */\n+static unsigned int sit9531x_output_prg_reg(struct sit9531x_dev *sitdev,\n+\t\t\t\t\t u8 out_idx, u8 i)\n+{\n+\tu8 slot = sitdev-\u003einfo-\u003eclkout_map[out_idx];\n+\tu8 page = (slot \u003e SIT9531X_PAGE_OUTSYS0_SLOT_MAX) ?\n+\t\t SIT9531X_PAGE_OUTSYS1 : SIT9531X_PAGE_OUTSYS0;\n+\tu8 base = SIT9531X_OUT_PRG_DELAY_BASE +\n+\t\t SIT9531X_OUT_PRG_SLOT_STRIDE * (slot % 6);\n+\n+\treturn SIT9531X_REG(page, base + i);\n+}\n+\n+/* Read the PRG_RST_DELAY bytes of an output, PROG6 first. */\n+static int sit9531x_output_phase_bytes_read(struct sit9531x_dev *sitdev,\n+\t\t\t\t\t u8 out_idx, u8 *bytes)\n+{\n+\tint rc;\n+\tu8 i;\n+\n+\tfor (i = 0; i \u003c SIT9531X_OUT_PRG_BYTES; i++) {\n+\t\trc = sit9531x_read_u8(sitdev,\n+\t\t\t\t sit9531x_output_prg_reg(sitdev, out_idx, i),\n+\t\t\t\t \u0026bytes[i]);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Encode a quantized delay into the register bytes. @old_bytes supplies\n+ * the PROG6 bits that are not the delay's, which are preserved.\n+ *\n+ * The register value carries the divider's own settling time: the\n+ * quantizer works in the delay the caller asked for, the register wants\n+ * that plus the two VCO cycles the divider spends acting on it, and a\n+ * request of zero still waits those two. Only the register value carries\n+ * them; @coarse stays the requested delay, which is what gets cached.\n+ */\n+static void sit9531x_output_phase_bytes_build(const u8 *old_bytes, u64 coarse,\n+\t\t\t\t\t u8 fine, u8 *new_bytes)\n+{\n+\tu64 prg_coarse = coarse + SIT9531X_OUT_PRG_DIVO_CYCLES;\n+\n+\t/* PROG6 RMW: preserve OPSTG_VCASC_BUMP in [7:5] */\n+\tnew_bytes[0] = old_bytes[0] \u0026 SIT9531X_OUT_PRG_OPSTG_MASK;\n+\tnew_bytes[0] |= (fine \u003c\u003c SIT9531X_OUT_PRG_FINE_SHIFT) \u0026\n+\t\t\tSIT9531X_OUT_PRG_FINE_MASK;\n+\tnew_bytes[0] |= (u8)((prg_coarse \u003e\u003e 32) \u0026\n+\t\t\t SIT9531X_OUT_PRG_COARSE_HI_MASK);\n+\tnew_bytes[1] = (u8)((prg_coarse \u003e\u003e 24) \u0026 0xFF);\n+\tnew_bytes[2] = (u8)((prg_coarse \u003e\u003e 16) \u0026 0xFF);\n+\tnew_bytes[3] = (u8)((prg_coarse \u003e\u003e 8) \u0026 0xFF);\n+\tnew_bytes[4] = (u8)(prg_coarse \u0026 0xFF);\n+}\n+\n+/*\n+ * Write an output's delay bytes. The caller must already be in the\n+ * programming state. On a failure every byte is put back, the one whose\n+ * write reported the error included, since it may have reached the part.\n+ */\n+static int sit9531x_output_phase_bytes_write(struct sit9531x_dev *sitdev,\n+\t\t\t\t\t u8 out_idx, const u8 *new_bytes,\n+\t\t\t\t\t const u8 *old_bytes)\n+{\n+\tint rc, ret, rb_rc = 0;\n+\tu8 i;\n+\n+\tfor (i = 0; i \u003c SIT9531X_OUT_PRG_BYTES; i++) {\n+\t\trc = sit9531x_write_u8(sitdev,\n+\t\t\t\t sit9531x_output_prg_reg(sitdev, out_idx, i),\n+\t\t\t\t new_bytes[i]);\n+\t\tif (rc)\n+\t\t\tgoto rollback;\n+\t}\n+\n+\treturn 0;\n+\n+rollback:\n+\tfor (i = 0; i \u003c SIT9531X_OUT_PRG_BYTES; i++) {\n+\t\tret = sit9531x_write_u8(sitdev,\n+\t\t\t\t\tsit9531x_output_prg_reg(sitdev, out_idx, i),\n+\t\t\t\t\told_bytes[i]);\n+\t\tif (ret \u0026\u0026 !rb_rc)\n+\t\t\trb_rc = ret;\n+\t}\n+\tif (rb_rc) {\n+\t\tdev_err(sitdev-\u003edev,\n+\t\t\t\"out%u: phase-adjust rollback failed (%d), the delay registers are part old and part new\\n\",\n+\t\t\tout_idx, rb_rc);\n+\t\tif (!rc)\n+\t\t\trc = rb_rc;\n+\t}\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * Error of realizing @abs_ps as @cycles whole VCO cycles plus the fine\n+ * steps that come nearest, which are returned through @fine.\n+ */\n+static u64 sit9531x_output_phase_quant_err(u64 abs_ps, u64 fvco, u64 cycles,\n+\t\t\t\t\t u8 *fine)\n+{\n+\tu64 cycles_ps, steps = 0;\n+\n+\tcycles_ps = mul_u64_u64_div_u64(cycles, 1000000000000ULL, fvco);\n+\tif (abs_ps \u003e cycles_ps)\n+\t\tsteps = div64_u64(abs_ps - cycles_ps +\n+\t\t\t\t SIT9531X_OUT_PRG_FINE_STEP_PS / 2,\n+\t\t\t\t SIT9531X_OUT_PRG_FINE_STEP_PS);\n+\t*fine = min_t(u64, steps, SIT9531X_OUT_PRG_FINE_MAX);\n+\n+\treturn abs_diff(abs_ps, cycles_ps +\n+\t\t\t(u64)*fine * SIT9531X_OUT_PRG_FINE_STEP_PS);\n+}\n+\n+/*\n+ * Quantize a phase request: fold @phase_ps into one output period and\n+ * split it into whole VCO cycles (@coarse_out, without the divider's\n+ * settling cycles) and fine steps (@fine_out). @phase_adj gets the delay\n+ * the two realize, in the request's sign and bounded to the advertised\n+ * range, which is what the cache holds. Pure arithmetic against @fvco\n+ * and the divider @divo the output runs on, so a caller can encode\n+ * before it enters the programming state.\n+ */\n+static int sit9531x_output_phase_encode(u64 fvco, u64 divo, s32 phase_ps,\n+\t\t\t\t\tu64 *coarse_out, u8 *fine_out,\n+\t\t\t\t\ts32 *phase_adj)\n+{\n+\tu64 abs_ps, cycles, frac_ps, coarse = 0, coarse_ps, t_out_ps;\n+\ts64 phase_norm_ps = 0;\n+\tu8 fine = 0;\n+\n+\tt_out_ps = mul_u64_u64_div_u64(divo, 1000000000000ULL, fvco);\n+\tif (!t_out_ps)\n+\t\treturn -EINVAL;\n+\n+\t/*\n+\t * Convert to unsigned absolute delay. Both signs are folded\n+\t * modulo one period: positive delays wrap naturally, negative\n+\t * delays are rendered as T_out - |phase|. abs() is safe here\n+\t * because the core rejects anything outside the advertised phase\n+\t * range, which is +/-1 ms.\n+\t *\n+\t * The period is exactly DIVO VCO cycles, whereas in picoseconds it\n+\t * is a fraction more often than not: 7812.5 ps at 128 MHz from a\n+\t * 5.12 GHz VCO, and a fold on the truncated 7812 drops the half\n+\t * picosecond once per period folded away, so 100 us -- 12800\n+\t * periods exactly -- would come out as 6400 ps rather than nothing.\n+\t * Fold the whole VCO cycles of the request modulo DIVO instead, and\n+\t * carry the sub-cycle part across as it is. div64_u64_rem() rather\n+\t * than the % operator: a 64-bit modulo has no compiler helper on\n+\t * 32-bit targets and leaves the module with an undefined __umoddi3.\n+\t */\n+\tabs_ps = abs(phase_ps);\n+\tcycles = mul_u64_u64_div_u64(abs_ps, fvco, 1000000000000ULL);\n+\tfrac_ps = abs_ps - mul_u64_u64_div_u64(cycles, 1000000000000ULL, fvco);\n+\tdiv64_u64_rem(cycles, divo, \u0026cycles);\n+\tabs_ps = mul_u64_u64_div_u64(cycles, 1000000000000ULL, fvco) + frac_ps;\n+\t/*\n+\t * The truncations above can put a request within a picosecond of a\n+\t * whole period a picosecond past it; that is the same edge as none.\n+\t */\n+\tif (abs_ps \u003e= t_out_ps)\n+\t\tabs_ps = 0;\n+\tphase_norm_ps = phase_ps \u003c 0 ? -(s64)abs_ps : (s64)abs_ps;\n+\tabs_ps = (phase_ps \u003c 0 \u0026\u0026 abs_ps) ? t_out_ps - abs_ps : abs_ps;\n+\n+\tif (abs_ps) {\n+\t\tu64 floor_cycles, err, alt_err;\n+\t\tu8 alt_fine;\n+\n+\t\t/*\n+\t\t * coarse_cycles = abs_ps * Fvco / 1e12 ps/s.\n+\t\t * mul_u64_u64_div_u64() avoids overflow when abs_ps approaches\n+\t\t * one second of 1 PPS wrap-around.\n+\t\t */\n+\t\tfloor_cycles = mul_u64_u64_div_u64(abs_ps, fvco,\n+\t\t\t\t\t\t 1000000000000ULL);\n+\n+\t\t/*\n+\t\t * Fine = round((abs_ps - coarse * vco_period_ps) / 30 ps).\n+\t\t * The fine field spans 210 ps, more than one VCO cycle in\n+\t\t * every band, so the cycle count that floors the request is\n+\t\t * not always the nearest encoding: one cycle more can land\n+\t\t * closer than any fine code when the remainder sits just\n+\t\t * under a cycle, and one cycle fewer with a larger fine code\n+\t\t * can land closer or exact -- 210 ps at 5 GHz is seven fine\n+\t\t * steps, not a cycle and a ten picosecond remainder. Take\n+\t\t * the nearest of the three, the floor on a tie.\n+\t\t */\n+\t\tcoarse = floor_cycles;\n+\t\terr = sit9531x_output_phase_quant_err(abs_ps, fvco, coarse,\n+\t\t\t\t\t\t \u0026fine);\n+\t\talt_err = sit9531x_output_phase_quant_err(abs_ps, fvco,\n+\t\t\t\t\t\t\t floor_cycles + 1,\n+\t\t\t\t\t\t\t \u0026alt_fine);\n+\t\tif (alt_err \u003c err) {\n+\t\t\tcoarse = floor_cycles + 1;\n+\t\t\tfine = alt_fine;\n+\t\t\terr = alt_err;\n+\t\t}\n+\t\tif (floor_cycles) {\n+\t\t\talt_err = sit9531x_output_phase_quant_err(abs_ps, fvco,\n+\t\t\t\t\t\t\t\t floor_cycles - 1,\n+\t\t\t\t\t\t\t\t \u0026alt_fine);\n+\t\t\tif (alt_err \u003c err) {\n+\t\t\t\tcoarse = floor_cycles - 1;\n+\t\t\t\tfine = alt_fine;\n+\t\t\t}\n+\t\t}\n+\n+\t\t/*\n+\t\t * A delay that quantizes to a whole output period or beyond\n+\t\t * is the same edge as no delay at all; program none, so the\n+\t\t * registers hold no residual past the period and the cache\n+\t\t * below describes exactly what they realize.\n+\t\t */\n+\t\tcoarse_ps = mul_u64_u64_div_u64(coarse, 1000000000000ULL, fvco);\n+\t\tif (coarse_ps + (u64)fine * SIT9531X_OUT_PRG_FINE_STEP_PS \u003e=\n+\t\t t_out_ps) {\n+\t\t\tcoarse = 0;\n+\t\t\tfine = 0;\n+\t\t}\n+\n+\t\tif (coarse + SIT9531X_OUT_PRG_DIVO_CYCLES \u003e=\n+\t\t (1ULL \u003c\u003c SIT9531X_OUT_PRG_COARSE_BITS))\n+\t\t\treturn -ERANGE;\n+\t}\n+\n+\tcoarse_ps = mul_u64_u64_div_u64(coarse, 1000000000000ULL, fvco);\n+\tabs_ps = coarse_ps + (u64)fine * SIT9531X_OUT_PRG_FINE_STEP_PS;\n+\t/*\n+\t * Quantization can also land a few picoseconds past the end of the\n+\t * advertised range, which the getter must not report. Bound both\n+\t * signs to the range; the positive one is also what keeps the cast\n+\t * to the s32 the ABI carries safe.\n+\t */\n+\tif (phase_norm_ps \u003c 0)\n+\t\t*phase_adj = abs_ps ?\n+\t\t\t -(s32)min_t(u64, t_out_ps - abs_ps,\n+\t\t\t\t\t SIT9531X_OUT_PHASE_ADJ_MAX_PS) : 0;\n+\telse\n+\t\t*phase_adj = (s32)min_t(u64, abs_ps,\n+\t\t\t\t\t SIT9531X_OUT_PHASE_ADJ_MAX_PS);\n+\n+\t*coarse_out = coarse;\n+\t*fine_out = fine;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * sit9531x_output_phase_read - read an output's programmed delay back\n+ * @sitdev:\tdevice pointer\n+ * @out_idx:\tlogical output index\n+ * @phase_ps:\tresult in picoseconds, in the advertised range\n+ * @clamped:\tset when the delay lies beyond the advertised range either\n+ *\t\tway and @phase_ps reports the end of the range in its place\n+ *\n+ * The delay the chip holds is part of the profile it loads before probe,\n+ * and a rate or phase request that failed after its writes reached the\n+ * device leaves the cache describing something else. Decoding the five\n+ * PRG_RST_DELAY bytes is the only way to say what the output is really\n+ * doing. The registers carry an unsigned delay, and an advance is held\n+ * as its complement to the output period. The two can only be told\n+ * apart on an output whose period is longer than the advertised range:\n+ * there a delay beyond the range whose complement is within it reads\n+ * back as that advance. On a faster output every delay is within the\n+ * range, and an advance that was set reads back as the delay to the same\n+ * edge, which is the same phase.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ *\n+ * Return: 0 on success, -ENODATA for an output no PLL drives or whose VCO\n+ * rate is unknown, \u003c0 on register access error\n+ */\n+int sit9531x_output_phase_read(struct sit9531x_dev *sitdev, u8 out_idx,\n+\t\t\t s32 *phase_ps, bool *clamped)\n+{\n+\tconst struct sit9531x_chip_info *info = sitdev-\u003einfo;\n+\tu8 bytes[SIT9531X_OUT_PRG_BYTES], fine;\n+\tu64 coarse = 0, fvco, ps, divo;\n+\tint rc;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (out_idx \u003e= info-\u003enum_outputs)\n+\t\treturn -EINVAL;\n+\n+\t*clamped = false;\n+\n+\t/*\n+\t * An output no PLL drives has no Fvco to decode its delay against;\n+\t * pll_idx then holds a placeholder, not a routing.\n+\t */\n+\tif (!sitdev-\u003eout[out_idx].routed)\n+\t\treturn -ENODATA;\n+\n+\trc = sit9531x_get_fvco(sitdev, sitdev-\u003eout[out_idx].pll_idx, \u0026fvco);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = sit9531x_output_phase_bytes_read(sitdev, out_idx, bytes);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tfine = (bytes[0] \u0026 SIT9531X_OUT_PRG_FINE_MASK) \u003e\u003e\n+\t SIT9531X_OUT_PRG_FINE_SHIFT;\n+\tcoarse = (u64)(bytes[0] \u0026 SIT9531X_OUT_PRG_COARSE_HI_MASK) \u003c\u003c 32;\n+\tcoarse |= (u64)bytes[1] \u003c\u003c 24;\n+\tcoarse |= (u64)bytes[2] \u003c\u003c 16;\n+\tcoarse |= (u64)bytes[3] \u003c\u003c 8;\n+\tcoarse |= bytes[4];\n+\n+\t/*\n+\t * The register carries the divider's settling time on top of the\n+\t * delay that was asked for, so take it back off. A profile can\n+\t * leave a value below it, which describes no delay at all.\n+\t */\n+\tcoarse = (coarse \u003e SIT9531X_OUT_PRG_DIVO_CYCLES) ?\n+\t\t coarse - SIT9531X_OUT_PRG_DIVO_CYCLES : 0;\n+\n+\t/*\n+\t * The setter holds an advance as T_out - |advance|, so a delay\n+\t * beyond the advertised range whose complement is within it is that\n+\t * advance and reads back as one. Anything else a profile left\n+\t * beyond the range -- wider than an s32 on a slow output -- reports\n+\t * the end of the range rather than a value the setter would refuse,\n+\t * and says so: the end of the range is not the delay, and a rate\n+\t * change must not re-time it into the device as though it were.\n+\t *\n+\t * The period comes from the divider rather than from the cached rate,\n+\t * which is still unset at probe and whole hertz at best. An output\n+\t * without a programmed divider has no period to fold against; a\n+\t * divider that could not be read is an error, not a missing one,\n+\t * since the unfolded value would be cached as the realized delay.\n+\t */\n+\trc = sit9531x_output_divo_read(sitdev, out_idx, \u0026divo);\n+\tif (rc \u0026\u0026 rc != -ENODATA)\n+\t\treturn rc;\n+\n+\t/*\n+\t * The period is exactly DIVO VCO cycles, so fold the cycle count\n+\t * rather than the picoseconds it converts to, which would drop the\n+\t * fraction of a picosecond of the period once per period folded\n+\t * away (see sit9531x_output_phase_encode()). The fine steps can\n+\t * still carry the sum a fraction of a cycle past the period; one\n+\t * subtraction folds that without anything to accumulate.\n+\t */\n+\tif (!rc)\n+\t\tdiv64_u64_rem(coarse, divo, \u0026coarse);\n+\n+\tps = mul_u64_u64_div_u64(coarse, 1000000000000ULL, fvco);\n+\tps += (u64)fine * SIT9531X_OUT_PRG_FINE_STEP_PS;\n+\n+\tif (!rc) {\n+\t\tu64 t_out_ps = mul_u64_u64_div_u64(divo, 1000000000000ULL,\n+\t\t\t\t\t\t fvco);\n+\n+\t\tif (ps \u003e= t_out_ps)\n+\t\t\tps -= t_out_ps;\n+\t\tif (ps \u003e SIT9531X_OUT_PHASE_ADJ_MAX_PS \u0026\u0026\n+\t\t t_out_ps - ps \u003c= SIT9531X_OUT_PHASE_ADJ_MAX_PS) {\n+\t\t\t*phase_ps = -(s32)(t_out_ps - ps);\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\t*clamped = ps \u003e SIT9531X_OUT_PHASE_ADJ_MAX_PS;\n+\t*phase_ps = (s32)min_t(u64, ps, SIT9531X_OUT_PHASE_ADJ_MAX_PS);\n+\n+\treturn 0;\n+}\n+\n+int sit9531x_output_freq_set(struct sit9531x_dev *sitdev, u8 out_idx,\n+\t\t\t u8 pll_idx, u64 frequency)\n+{\n+\tu8 old_bytes[SIT9531X_OUT_PRG_BYTES], new_bytes[SIT9531X_OUT_PRG_BYTES];\n+\tu64 fvco, divo, coarse;\n+\tbool retime = false;\n+\ts32 phase_adj = 0;\n+\tint rc, ret;\n+\tu8 fine;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\trc = sit9531x_output_divo_calc(sitdev, out_idx, pll_idx, frequency,\n+\t\t\t\t \u0026fvco, \u0026divo);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\t/*\n+\t * The programmed reset delay counts VCO cycles, and a rate change\n+\t * moves only the divider, so a positive delay keeps its timing; an\n+\t * advance, though, is held as T_out - |advance| and has to be\n+\t * re-encoded against the new period. Re-encode the delay the output\n+\t * realizes, which is what the cache holds, against the new divider,\n+\t * and write it in the same programming window as the divider: the\n+\t * rate change then costs one commit and one flush. A second window\n+\t * would open the output loops and wait out their settling again, and\n+\t * a second flush would restart the PLL's dividers again -- or, on a\n+\t * PLL the profile builds without the phase-flush feature, the PLL\n+\t * itself. For a positive delay the bytes come out the same and\n+\t * nothing is written. A re-time that cannot be worked out fails\n+\t * the request before anything is written.\n+\t */\n+\tif (sitdev-\u003eout[out_idx].phase_armed) {\n+\t\trc = sit9531x_output_phase_encode(fvco, divo,\n+\t\t\t\t\t\t sitdev-\u003eout[out_idx].phase_adj,\n+\t\t\t\t\t\t \u0026coarse, \u0026fine, \u0026phase_adj);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\t\trc = sit9531x_output_phase_bytes_read(sitdev, out_idx,\n+\t\t\t\t\t\t old_bytes);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\t\tsit9531x_output_phase_bytes_build(old_bytes, coarse, fine,\n+\t\t\t\t\t\t new_bytes);\n+\t\tretime = memcmp(old_bytes, new_bytes, sizeof(new_bytes)) != 0;\n+\t}\n+\n+\trc = sit9531x_prg_enter(sitdev);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = sit9531x_output_divo_write(sitdev, out_idx, divo);\n+\tif (!rc \u0026\u0026 retime)\n+\t\trc = sit9531x_output_phase_bytes_write(sitdev, out_idx,\n+\t\t\t\t\t\t new_bytes, old_bytes);\n+\t/*\n+\t * Step 4: NVM update + loop lock. Always run prg_commit() so the chip\n+\t * leaves the PRG_CMD state with the output loops re-locked, even when a\n+\t * write above failed; keep the first error to return. It also carries\n+\t * the required post-lock settling sleep.\n+\t */\n+\tret = sit9531x_prg_commit(sitdev);\n+\tif (ret \u0026\u0026 !rc)\n+\t\trc = ret;\n+\tif (rc) {\n+\t\t/*\n+\t\t * The divider may have changed all the same: a failed update\n+\t\t * can have reached the part, a loop lock can fail after it\n+\t\t * took effect, and a failed rollback leaves whatever landed.\n+\t\t * An advance is held as T_out - |advance| against the period\n+\t\t * that was, and the re-timed bytes may or may not be in, so\n+\t\t * the cache no longer says what the output does. Mark it for\n+\t\t * a read-back: the core drops a retry of either request,\n+\t\t * the rate because it reads the new one first and the phase\n+\t\t * because it would match the stale cache.\n+\t\t */\n+\t\tif (sitdev-\u003eout[out_idx].phase_armed)\n+\t\t\tsitdev-\u003eout[out_idx].phase_stale = true;\n+\t\treturn rc;\n+\t}\n+\n+\t/*\n+\t * Step 5: flush the PLL's output phase so the new DIVO starts\n+\t * aligned instead of keeping the arbitrary phase the divider\n+\t * happened to be at.\n+\t */\n+\t/*\n+\t * The divider is committed by this point, so the part is already\n+\t * running at the new rate. A flush that fails leaves the output\n+\t * divider on its old phase, which is a realignment that did not\n+\t * happen rather than a rate that did not change -- and reporting a\n+\t * failure would be doubly wrong, because the core asks for the\n+\t * current rate first and would drop an identical retry. A re-timed\n+\t * delay is then committed but not applied either; mark it for a\n+\t * read-back rather than cache a value the flush did not realize.\n+\t */\n+\trc = sit9531x_output_phase_flush(sitdev, pll_idx);\n+\tif (rc) {\n+\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t \"out%u: rate changed but the divider phase was not realigned (%d)\\n\",\n+\t\t\t out_idx, rc);\n+\t\tif (retime)\n+\t\t\tsitdev-\u003eout[out_idx].phase_stale = true;\n+\t\trc = 0;\n+\t} else {\n+\t\t/* Whatever flush was owed from before has now run. */\n+\t\tsitdev-\u003eout[out_idx].flush_pending = false;\n+\t\tif (retime) {\n+\t\t\tsitdev-\u003eout[out_idx].phase_adj = phase_adj;\n+\t\t\tsitdev-\u003eout[out_idx].phase_armed = phase_adj != 0;\n+\t\t\tsitdev-\u003eout[out_idx].phase_stale = false;\n+\t\t}\n+\t}\n+\n+\tsitdev-\u003eout[out_idx].freq = div64_u64(fvco, divo);\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * sit9531x_output_freq_get - read output clock frequency from hardware\n+ * @out_idx:\toutput index (0-N for this chip variant)\n+ * @frequency:\toutput frequency in Hz\n+ *\n+ * Reads the 34-bit DIVO divider back from the output system registers\n+ * and computes the live output frequency as Fvco / DIVO. This stays\n+ * correct even when the divider was reprogrammed behind the driver's\n+ * back (e.g. by a direct-I2C userspace tool), where the cached value\n+ * would be stale.\n+ *\n+ * The cached output state is refreshed with the computed value.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ *\n+ * Return: 0 on success, -ENODATA when the output divider or VCO rate\n+ *\t is not resolvable, \u003c0 on register access error. -ENODATA and not\n+ *\t -ENODEV, which the I2C layer returns for an adapter that is gone\n+ *\t and which must not read as \"no rate to report\".\n+ */\n+int sit9531x_output_freq_get(struct sit9531x_dev *sitdev, u8 out_idx,\n+\t\t\t u64 *frequency)\n+{\n+\tconst struct sit9531x_chip_info *info = sitdev-\u003einfo;\n+\tu64 fvco, divo;\n+\tu8 pll_idx;\n+\tint rc;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (out_idx \u003e= info-\u003enum_outputs)\n+\t\treturn -EINVAL;\n+\n+\tpll_idx = sitdev-\u003eout[out_idx].pll_idx;\n+\tif (pll_idx \u003e= SIT9531X_NUM_PLLS || !sitdev-\u003eout[out_idx].routed)\n+\t\treturn -ENODATA;\n+\n+\trc = sit9531x_get_fvco(sitdev, pll_idx, \u0026fvco);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = sit9531x_output_divo_read(sitdev, out_idx, \u0026divo);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\t*frequency = div64_u64(fvco, divo);\n+\tsitdev-\u003eout[out_idx].freq = *frequency;\n+\n+\treturn 0;\n+}\n+\n+int sit9531x_output_phase_adjust_set(struct sit9531x_dev *sitdev,\n+\t\t\t\t u8 out_idx, s32 phase_ps)\n+{\n+\tu8 old_bytes[SIT9531X_OUT_PRG_BYTES], new_bytes[SIT9531X_OUT_PRG_BYTES];\n+\tconst struct sit9531x_chip_info *info = sitdev-\u003einfo;\n+\tu64 fvco, divo, coarse;\n+\tu8 pll_idx, fine;\n+\ts32 phase_adj;\n+\tint rc, ret;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (out_idx \u003e= info-\u003enum_outputs)\n+\t\treturn -EINVAL;\n+\n+\tpll_idx = sitdev-\u003eout[out_idx].pll_idx;\n+\tif (pll_idx \u003e= SIT9531X_NUM_PLLS)\n+\t\treturn -EINVAL;\n+\n+\trc = sit9531x_get_fvco(sitdev, pll_idx, \u0026fvco);\n+\tif (rc)\n+\t\treturn rc == -ENODATA ? -ENODEV : rc;\n+\n+\t/*\n+\t * The output period comes from the divider the output runs on, not\n+\t * from the cached rate: that is whole hertz, so a profile's output at\n+\t * a fractional rate would fold an advance against the wrong period.\n+\t */\n+\trc = sit9531x_output_divo_read(sitdev, out_idx, \u0026divo);\n+\tif (rc)\n+\t\treturn rc == -ENODATA ? -ENODEV : rc;\n+\n+\trc = sit9531x_output_phase_encode(fvco, divo, phase_ps, \u0026coarse, \u0026fine,\n+\t\t\t\t\t \u0026phase_adj);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = sit9531x_output_phase_bytes_read(sitdev, out_idx, old_bytes);\n+\tif (rc)\n+\t\treturn rc;\n+\tsit9531x_output_phase_bytes_build(old_bytes, coarse, fine, new_bytes);\n+\n+\t/*\n+\t * The pin advertises 1 ps granularity but caches the quantized\n+\t * value, so a repeated off-grid request reaches here with the\n+\t * registers already holding it. Rewriting them would still restart\n+\t * the divider phase of every output on the PLL; skip it unless an\n+\t * earlier failure left the delay unconfirmed. A delay whose flush\n+\t * failed is committed and only owes that flush, which matching\n+\t * bytes would otherwise skip as well.\n+\t */\n+\tif (!memcmp(old_bytes, new_bytes, sizeof(new_bytes)) \u0026\u0026\n+\t !sitdev-\u003eout[out_idx].phase_stale) {\n+\t\tif (!sitdev-\u003eout[out_idx].flush_pending)\n+\t\t\tgoto cache;\n+\t\tgoto flush;\n+\t}\n+\n+\t/*\n+\t * The PRG_RST_DELAY bytes live in the output system, so the writes\n+\t * only take effect when made inside the PRG_CMD programming state and\n+\t * committed to the NVM shadow, exactly like sit9531x_output_freq_set().\n+\t */\n+\trc = sit9531x_prg_enter(sitdev);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = sit9531x_output_phase_bytes_write(sitdev, out_idx, new_bytes,\n+\t\t\t\t\t old_bytes);\n+\n+\t/*\n+\t * Always leave the PRG_CMD state via prg_commit(), even on a\n+\t * mid-sequence write failure, so the output loops are re-locked rather\n+\t * than stranded unlocked; keep the first error.\n+\t */\n+\tret = sit9531x_prg_commit(sitdev);\n+\tif (ret \u0026\u0026 !rc)\n+\t\trc = ret;\n+\tif (rc) {\n+\t\t/*\n+\t\t * The delay registers were written and the rollback may not\n+\t\t * have put all of them back, so what the output realizes is\n+\t\t * no longer what the cache says. Mark it so the getter reads\n+\t\t * the registers instead of reporting the value that was\n+\t\t * cached before this call. No flush ran either way.\n+\t\t */\n+\t\tsitdev-\u003eout[out_idx].phase_stale = true;\n+\t\tsitdev-\u003eout[out_idx].flush_pending = true;\n+\t\treturn rc;\n+\t}\n+\n+flush:\n+\t/*\n+\t * Restart the output divider phase so the freshly programmed delay is\n+\t * applied against a known edge instead of the divider's arbitrary\n+\t * running phase.\n+\t */\n+\trc = sit9531x_output_phase_flush(sitdev, pll_idx);\n+\tif (rc) {\n+\t\t/*\n+\t\t * The delay is committed but the output keeps the phase it\n+\t\t * had, so the cache is left as it is: it still says what the\n+\t\t * output realizes. Record the flush that is owed instead.\n+\t\t * Marking the cache stale would have the getter publish the\n+\t\t * new delay off the registers, the core would then drop a\n+\t\t * retry as a request for the value already reported, and the\n+\t\t * realignment would never run.\n+\t\t */\n+\t\tsitdev-\u003eout[out_idx].flush_pending = true;\n+\t\treturn rc;\n+\t}\n+\tsitdev-\u003eout[out_idx].flush_pending = false;\n+\tsitdev-\u003eout[out_idx].phase_stale = false;\n+\n+cache:\n+\t/*\n+\t * Cache what the registers realize, and only once every step has\n+\t * succeeded: the core drops a repeated request with the same value,\n+\t * so a cache updated by a failed call would make the retry a no-op.\n+\t */\n+\tsitdev-\u003eout[out_idx].phase_adj = phase_adj;\n+\n+\t/*\n+\t * Arm the re-time a rate change owes only for a delay that is\n+\t * actually programmed: an advance is held as T_out - |advance|,\n+\t * which has to be re-encoded against the new period. A request\n+\t * that quantized to nothing stays nothing at any rate, since the\n+\t * quantum is the VCO cycle and not the output period, so there is\n+\t * nothing to re-time for it.\n+\t */\n+\tsitdev-\u003eout[out_idx].phase_armed = phase_adj != 0;\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * sit9531x_clear_notifications - clear all notification registers\n+ *\n+ * Clears all write-1-to-clear notification registers:\n+ * - PLL outer LOL notification (Page 0, reg 0x07)\n+ * - PLL holdover freeze notification (Page 0, reg 0x0B)\n+ * - PLL inner LOL notification (Page 0, reg 0x93)\n+ * - Clock monitor XO/PLL notification (Page 0, reg 0x9E)\n+ * - Clock input notifications (Page 6, regs 0x03/0x07/0x93/0x97)\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+int sit9531x_clear_notifications(struct sit9531x_dev *sitdev)\n+{\n+\tint rc;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\t/* Page 0x00 W1C notification registers */\n+\trc = sit9531x_write_u8(sitdev, SIT9531X_REG_OUTER_LOL_NOTIF, 0xFF);\n+\tif (rc)\n+\t\treturn rc;\n+\trc = sit9531x_write_u8(sitdev, SIT9531X_REG_HO_FREEZE_NOTIF, 0xFF);\n+\tif (rc)\n+\t\treturn rc;\n+\trc = sit9531x_write_u8(sitdev, SIT9531X_REG_PLL_INNER_LOL_NOTIF, 0xFF);\n+\tif (rc)\n+\t\treturn rc;\n+\trc = sit9531x_write_u8(sitdev, SIT9531X_REG_CMON_NOTIF, 0xFF);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\t/* Page 0x06 clock input monitor notifications */\n+\trc = sit9531x_write_u8(sitdev, SIT9531X_CLKMON_P_NOTIF_01, 0xFF);\n+\tif (rc)\n+\t\treturn rc;\n+\trc = sit9531x_write_u8(sitdev, SIT9531X_CLKMON_P_NOTIF_23, 0xFF);\n+\tif (rc)\n+\t\treturn rc;\n+\trc = sit9531x_write_u8(sitdev, SIT9531X_CLKMON_N_NOTIF_01, 0xFF);\n+\tif (rc)\n+\t\treturn rc;\n+\trc = sit9531x_write_u8(sitdev, SIT9531X_CLKMON_N_NOTIF_23, 0xFF);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tdev_dbg(sitdev-\u003edev, \"All notification registers cleared\\n\");\n+\treturn 0;\n+}\n+\n+/*\n+ * Close the debug window on a PLL's EXT page. The key register opens\n+ * every debug register on that page while it holds the unlock value.\n+ */\n+static int sit9531x_intsync_debug_lock(struct sit9531x_dev *sitdev, u8 ext_page)\n+{\n+\treturn sit9531x_write_u8(sitdev,\n+\t\t\t\t SIT9531X_REG(ext_page, SIT9531X_PLL_REG_DEBUG),\n+\t\t\t\t SIT9531X_PLL_DEBUG_LOCK);\n+}\n+\n+/*\n+ * INTSYNC configuration register values.\n+ * These are written to the source PLL's EXT page to enable/disable\n+ * inter-PLL synchronization (lock frequency PLL to phase PLL).\n+ */\n+struct sit9531x_intsync_reg {\n+\tu8 offset;\n+\tu8 en_val;\n+\tu8 dis_val;\n+};\n+\n+static const struct sit9531x_intsync_reg intsync_config[] = {\n+\t{ 0x2D, 0x02, 0x00 },\n+\t{ 0x50, 0x08, 0x00 },\n+\t{ 0x51, 0x04, 0x00 },\n+\t{ 0x54, 0x02, 0x00 },\n+\t{ 0x55, 0x28, 0x20 },\n+\t{ 0x5C, 0x0F, 0x00 },\n+\t{ 0x5D, 0xFF, 0x00 },\n+\t{ 0x6C, 0xDD, 0x00 },\n+};\n+\n+int sit9531x_intsync_src_detect(struct sit9531x_dev *sitdev)\n+{\n+\ts8 src = -1, partial = -1;\n+\tu8 global;\n+\tu8 pll, ext_page;\n+\tint rc, ret, n_en, n_dis;\n+\tunsigned int i;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\trc = sit9531x_read_u8(sitdev, SIT9531X_REG_INTSYNC_GLOBAL, \u0026global);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tif (!(global \u0026 BIT(SIT9531X_INTSYNC_EN_BIT))) {\n+\t\tsitdev-\u003eintsync_src = -1;\n+\t\tsitdev-\u003eintsync_partial = false;\n+\t\treturn 0;\n+\t}\n+\n+\tfor (pll = 0; pll \u003c SIT9531X_NUM_PLLS; pll++) {\n+\t\text_page = SIT9531X_PLL_EXT_PAGE(pll);\n+\n+\t\t/*\n+\t\t * These are debug registers, which the enable and disable\n+\t\t * sequences only touch with the EXT page's debug window\n+\t\t * open; read them the same way.\n+\t\t */\n+\t\trc = sit9531x_write_u8(sitdev,\n+\t\t\t\t SIT9531X_REG(ext_page,\n+\t\t\t\t\t\t SIT9531X_PLL_REG_DEBUG),\n+\t\t\t\t SIT9531X_PLL_DEBUG_UNLOCK);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\n+\t\tn_en = 0;\n+\t\tn_dis = 0;\n+\t\tfor (i = 0; i \u003c ARRAY_SIZE(intsync_config); i++) {\n+\t\t\tu16 reg;\n+\t\t\tu8 val;\n+\n+\t\t\treg = SIT9531X_REG(ext_page, intsync_config[i].offset);\n+\n+\t\t\trc = sit9531x_read_u8(sitdev, reg, \u0026val);\n+\t\t\tif (rc)\n+\t\t\t\tbreak;\n+\t\t\tif (val == intsync_config[i].en_val)\n+\t\t\t\tn_en++;\n+\t\t\telse if (val == intsync_config[i].dis_val)\n+\t\t\t\tn_dis++;\n+\t\t}\n+\n+\t\tret = sit9531x_intsync_debug_lock(sitdev, ext_page);\n+\t\tif (!rc)\n+\t\t\trc = ret;\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\n+\t\tif (n_en == ARRAY_SIZE(intsync_config)) {\n+\t\t\t/*\n+\t\t\t * Only one PLL can drive the net. If a second\n+\t\t\t * one matches, the registers are not describing\n+\t\t\t * a state this driver put the device in, so say\n+\t\t\t * so rather than pick silently.\n+\t\t\t */\n+\t\t\tif (src \u003c 0)\n+\t\t\t\tsrc = pll;\n+\t\t\telse\n+\t\t\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t\t\t \"PLL%c also matches the INTSYNC source pattern; keeping PLL%c\\n\",\n+\t\t\t\t\t 'A' + pll, 'A' + src);\n+\t\t} else if (n_en \u0026\u0026 n_dis \u0026\u0026\n+\t\t\t n_en + n_dis == ARRAY_SIZE(intsync_config) \u0026\u0026\n+\t\t\t partial \u003c 0) {\n+\t\t\t/*\n+\t\t\t * Every register holds one of the two patterns and both\n+\t\t\t * occur: the residue of an enable or a disable that\n+\t\t\t * stopped part way, and nothing a profile writes.\n+\t\t\t */\n+\t\t\tpartial = pll;\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * An enable or a disable that stopped part way leaves a PLL holding\n+\t * a mix of the two patterns. Treat it as the owner, so that another\n+\t * PLL cannot be enabled on top of the leftover configuration, but\n+\t * record that it is partial: a repeated connect on that PLL must run\n+\t * the enable again rather than take the PLL for configured, and a\n+\t * repeated disconnect runs the disable again.\n+\t */\n+\tsitdev-\u003eintsync_partial = src \u003c 0 \u0026\u0026 partial \u003e= 0;\n+\tif (sitdev-\u003eintsync_partial) {\n+\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t \"PLL%c holds a partial INTSYNC source configuration\\n\",\n+\t\t\t 'A' + partial);\n+\t\tsrc = partial;\n+\t}\n+\n+\tsitdev-\u003eintsync_src = src;\n+\n+\treturn 0;\n+}\n+\n+static int __sit9531x_intsync_disable(struct sit9531x_dev *sitdev,\n+\t\t\t\t u8 src_pll_idx, bool restore);\n+\n+/*\n+ * sit9531x_intsync_enable - enable inter-PLL synchronization\n+ * @src_pll_idx: source (frequency) PLL index (0-3)\n+ *\n+ * Enables INTSYNC global bit, unlocks the source PLL's EXT page\n+ * debug registers, writes configuration, and triggers a small\n+ * update on the source PLL.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+int sit9531x_intsync_enable(struct sit9531x_dev *sitdev, u8 src_pll_idx)\n+{\n+\tu8 ext_page, val;\n+\tint rc, lock_rc;\n+\tunsigned int i;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (src_pll_idx \u003e= SIT9531X_NUM_PLLS)\n+\t\treturn -EINVAL;\n+\n+\text_page = SIT9531X_PLL_EXT_PAGE(src_pll_idx);\n+\n+\trc = sit9531x_read_u8(sitdev, SIT9531X_REG_INTSYNC_GLOBAL, \u0026val);\n+\tif (rc)\n+\t\treturn rc;\n+\trc = sit9531x_write_u8(sitdev, SIT9531X_REG_INTSYNC_GLOBAL,\n+\t\t\t val | BIT(SIT9531X_INTSYNC_EN_BIT));\n+\tif (rc)\n+\t\treturn rc;\n+\n+\t/* Small update on Page 0 */\n+\trc = sit9531x_write_u8(sitdev, SIT9531X_REG_GLOBAL_UPDATE,\n+\t\t\t SIT9531X_SMALL_UPDATE_CMD);\n+\tusleep_range(1000, 2000);\n+\tif (rc)\n+\t\tgoto relock_err;\n+\n+\t/* Unlock debug on EXT page */\n+\trc = sit9531x_write_u8(sitdev,\n+\t\t\t SIT9531X_REG(ext_page,\n+\t\t\t\t\t SIT9531X_PLL_REG_DEBUG),\n+\t\t\t SIT9531X_PLL_DEBUG_UNLOCK);\n+\tif (rc)\n+\t\tgoto relock_err;\n+\n+\tfor (i = 0; i \u003c ARRAY_SIZE(intsync_config); i++) {\n+\t\trc = sit9531x_write_u8(sitdev,\n+\t\t\t\t SIT9531X_REG(ext_page,\n+\t\t\t\t\t\t intsync_config[i].offset),\n+\t\t\t\t intsync_config[i].en_val);\n+\t\tif (rc)\n+\t\t\tgoto relock_err;\n+\t}\n+\n+\t/* Small update on source PLL */\n+\trc = sit9531x_pll_small_update(sitdev, src_pll_idx);\n+\tif (rc)\n+\t\tgoto relock_err;\n+\n+\trc = 0;\n+\tgoto relock;\n+\n+relock_err:\n+\tsit9531x_intsync_debug_lock(sitdev, ext_page);\n+\tgoto err_disable;\n+\n+relock:\n+\t/*\n+\t * Close the EXT page debug window the sequence opened. Nothing\n+\t * else writes the key back, so leaving it open would keep the block\n+\t * unlocked for as long as the device runs. The net is driven by\n+\t * now, though, so a failure here must not read as a failed enable:\n+\t * the caller would then not record the owner of a net that is\n+\t * driven.\n+\t */\n+\tlock_rc = sit9531x_intsync_debug_lock(sitdev, ext_page);\n+\tif (lock_rc)\n+\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t \"PLL%c: INTSYNC enabled but the debug window was left open: %d\\n\",\n+\t\t\t 'A' + src_pll_idx, lock_rc);\n+\n+\treturn rc;\n+\n+err_disable:\n+\t/*\n+\t * The global enable is already set at this point. The caller only\n+\t * records the source PLL when this function succeeds, so nothing\n+\t * else will ever clear the bit: undo it here rather than leave the\n+\t * net asserted with a half-written EXT page. The disable must not\n+\t * put the bit back when it fails part way, as it does for a\n+\t * disconnect: here the bit is what the rollback set out to clear,\n+\t * and restoring it would leave the net asserted with no PLL\n+\t * matching the source pattern, which no owner on record would ever\n+\t * clear.\n+\t */\n+\t{\n+\t\tint rollback_rc;\n+\n+\t\trollback_rc = __sit9531x_intsync_disable(sitdev, src_pll_idx,\n+\t\t\t\t\t\t\t false);\n+\t\tif (rollback_rc)\n+\t\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t\t \"INTSYNC rollback failed after enable error: %d (original %d)\\n\",\n+\t\t\t\t rollback_rc, rc);\n+\t}\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * The disable sequence. @restore says whether a failure before the EXT\n+ * page holds the disable pattern puts the global enable back, which a\n+ * disconnect wants (see restore_global below) and the rollback of a\n+ * failed enable does not.\n+ */\n+static int __sit9531x_intsync_disable(struct sit9531x_dev *sitdev,\n+\t\t\t\t u8 src_pll_idx, bool restore)\n+{\n+\tu8 ext_page, val;\n+\tint rc, lock_rc;\n+\tunsigned int i;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (src_pll_idx \u003e= SIT9531X_NUM_PLLS)\n+\t\treturn -EINVAL;\n+\n+\text_page = SIT9531X_PLL_EXT_PAGE(src_pll_idx);\n+\n+\trc = sit9531x_read_u8(sitdev, SIT9531X_REG_INTSYNC_GLOBAL, \u0026val);\n+\tif (rc)\n+\t\treturn rc;\n+\trc = sit9531x_write_u8(sitdev, SIT9531X_REG_INTSYNC_GLOBAL,\n+\t\t\t val \u0026 ~BIT(SIT9531X_INTSYNC_EN_BIT));\n+\tif (rc)\n+\t\tgoto restore_global;\n+\n+\t/* Small update on Page 0 */\n+\trc = sit9531x_write_u8(sitdev, SIT9531X_REG_GLOBAL_UPDATE,\n+\t\t\t SIT9531X_SMALL_UPDATE_CMD);\n+\tusleep_range(1000, 2000);\n+\tif (rc)\n+\t\tgoto restore_global;\n+\n+\t/* Unlock debug on EXT page */\n+\trc = sit9531x_write_u8(sitdev,\n+\t\t\t SIT9531X_REG(ext_page,\n+\t\t\t\t\t SIT9531X_PLL_REG_DEBUG),\n+\t\t\t SIT9531X_PLL_DEBUG_UNLOCK);\n+\tif (rc)\n+\t\tgoto restore_global;\n+\n+\tfor (i = 0; i \u003c ARRAY_SIZE(intsync_config); i++) {\n+\t\trc = sit9531x_write_u8(sitdev,\n+\t\t\t\t SIT9531X_REG(ext_page,\n+\t\t\t\t\t\t intsync_config[i].offset),\n+\t\t\t\t intsync_config[i].dis_val);\n+\t\tif (rc)\n+\t\t\tgoto restore_global;\n+\t}\n+\n+\t/*\n+\t * Small update on source PLL. The global enable is latched off and\n+\t * every register holds the disable value by now, so the net is not\n+\t * driven whether or not this latch went through. Putting the bit\n+\t * back for a failure here would describe the net as driven with no\n+\t * PLL matching the source pattern: the detector would record no\n+\t * owner, and nothing would ever clear the bit.\n+\t */\n+\trc = sit9531x_pll_small_update(sitdev, src_pll_idx);\n+\n+\tgoto relock;\n+\n+restore_global:\n+\t/*\n+\t * The global enable was cleared first, so a failure here leaves the\n+\t * EXT page still holding the enable pattern with nothing pointing\n+\t * at it: the source detector keys on the global bit, would report\n+\t * the net as unowned, and a retry of the disable would then\n+\t * short-circuit. Put the bit back so the state stays one the\n+\t * driver can describe and the request can be repeated.\n+\t */\n+\tif (restore \u0026\u0026\n+\t !sit9531x_read_u8(sitdev, SIT9531X_REG_INTSYNC_GLOBAL, \u0026val) \u0026\u0026\n+\t !sit9531x_write_u8(sitdev, SIT9531X_REG_INTSYNC_GLOBAL,\n+\t\t\t val | BIT(SIT9531X_INTSYNC_EN_BIT))) {\n+\t\t/*\n+\t\t * Every write of the global enable, here and in SiTime's\n+\t\t * sequences, is followed by the page-0 small update that\n+\t\t * applies it. Without it the bit would sit pending until\n+\t\t * an unrelated commit through the same update register\n+\t\t * applied it, re-asserting the net over a half-written\n+\t\t * EXT page with nobody having asked.\n+\t\t */\n+\t\tsit9531x_write_u8(sitdev, SIT9531X_REG_GLOBAL_UPDATE,\n+\t\t\t\t SIT9531X_SMALL_UPDATE_CMD);\n+\t\tusleep_range(1000, 2000);\n+\t}\n+\n+relock:\n+\t/*\n+\t * Close the EXT page debug window the sequence opened. After a\n+\t * disable that took effect, a failure here is a warning, for the\n+\t * same reason as in sit9531x_intsync_enable().\n+\t */\n+\tlock_rc = sit9531x_intsync_debug_lock(sitdev, ext_page);\n+\tif (lock_rc) {\n+\t\tif (rc)\n+\t\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t\t \"PLL%c: debug window left open: %d\\n\",\n+\t\t\t\t 'A' + src_pll_idx, lock_rc);\n+\t\telse\n+\t\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t\t \"PLL%c: INTSYNC disabled but the debug window was left open: %d\\n\",\n+\t\t\t\t 'A' + src_pll_idx, lock_rc);\n+\t}\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * sit9531x_intsync_disable - disable inter-PLL synchronization\n+ * @src_pll_idx: source (frequency) PLL index (0-3)\n+ *\n+ * Clears INTSYNC global bit, writes disable values to the source\n+ * PLL's EXT page, and triggers a small update.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+int sit9531x_intsync_disable(struct sit9531x_dev *sitdev, u8 src_pll_idx)\n+{\n+\treturn __sit9531x_intsync_disable(sitdev, src_pll_idx, true);\n+}\n+\n+/**\n+ * sit9531x_phase_offset_read - read phase difference via TDC\n+ * @sitdev:\tdevice pointer\n+ * @pll_idx:\tPLL index (0-3)\n+ * @phase_offset:\tphase difference in units of 1/DPLL_PHASE_OFFSET_DIVIDER\n+ *\t\tof a picosecond, as the DPLL subsystem carries it\n+ *\n+ * Reads the Time-to-Digital Converter (TDC) code from the PLL page\n+ * registers -- a 35-bit magnitude with a separate sign bit -- then\n+ * converts it using the VCO frequency: phase_diff = tdc_code / fvco.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ *\n+ * Return: 0 on success, -ENODATA when the PLL has no known VCO rate\n+ * (nothing is sampled then), or the register access error\n+ */\n+int sit9531x_phase_offset_read(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t s64 *phase_offset)\n+{\n+\tu8 v, old_write_code, old_read_code;\n+\tbool have_old = false;\n+\tint rc, ret, i;\n+\ts64 tdc_signed;\n+\tu64 fvco, mag;\n+\tu64 tdc_raw;\n+\tbool sign;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (pll_idx \u003e= SIT9531X_NUM_PLLS)\n+\t\treturn -EINVAL;\n+\n+\t/*\n+\t * Get the VCO rate first. -ENODATA means the PLL has no rate to\n+\t * convert against (a divider this board leaves unprogrammed), and\n+\t * there is then no reason to open the debug window at all -- which\n+\t * also keeps a bus error while it is open from ever being reported\n+\t * as that benign case.\n+\t */\n+\trc = sit9531x_get_fvco(sitdev, pll_idx, \u0026fvco);\n+\tif (rc) {\n+\t\tif (rc == -ENODATA)\n+\t\t\tdev_dbg(sitdev-\u003edev,\n+\t\t\t\t\"PLL%c: Fvco unknown, skip TDC\\n\",\n+\t\t\t\t'A' + pll_idx);\n+\t\treturn rc;\n+\t}\n+\n+\t/* Unlock the debug page so the TDC registers are accessible. */\n+\trc = sit9531x_write_pll_u8(sitdev, pll_idx,\n+\t\t\t\t SIT9531X_PLL_REG_DEBUG,\n+\t\t\t\t SIT9531X_PLL_DEBUG_UNLOCK);\n+\tif (rc)\n+\t\tgoto relock;\n+\n+\t/*\n+\t * Remember the tap selection so it can be put back. The key\n+\t * register is re-locked below, but the mux is not part of the key:\n+\t * leaving it parked on the TDC with a slow sampling clock selected\n+\t * is a state change the caller did not ask for, and the next reader\n+\t * of a different tap would have to know to undo it.\n+\t */\n+\trc = sit9531x_read_pll_u8(sitdev, pll_idx,\n+\t\t\t\t SIT9531X_PLL_REG_DBG_WRITE_CODE,\n+\t\t\t\t \u0026old_write_code);\n+\tif (!rc)\n+\t\trc = sit9531x_read_pll_u8(sitdev, pll_idx,\n+\t\t\t\t\t SIT9531X_PLL_REG_DBG_READ_CODE,\n+\t\t\t\t\t \u0026old_read_code);\n+\tif (rc)\n+\t\tgoto relock;\n+\n+\thave_old = true;\n+\n+\t/*\n+\t * Select the debug clock for taps below 200 kHz, then point the\n+\t * readback at the TDC. Only the one bit is touched: writing the\n+\t * modifier register whole would clear the fields belonging to\n+\t * other taps.\n+\t */\n+\trc = sit9531x_update_pll_u8(sitdev, pll_idx,\n+\t\t\t\t SIT9531X_PLL_REG_DBG_WRITE_CODE,\n+\t\t\t\t SIT9531X_DBG_LOW_FREQ_CLK_BIT,\n+\t\t\t\t SIT9531X_DBG_LOW_FREQ_CLK_BIT);\n+\tif (rc)\n+\t\tgoto relock;\n+\trc = sit9531x_write_pll_u8(sitdev, pll_idx,\n+\t\t\t\t SIT9531X_PLL_REG_DBG_READ_CODE,\n+\t\t\t\t SIT9531X_DBG_READ_CODE_TDC);\n+\tif (rc)\n+\t\tgoto relock;\n+\n+\t/*\n+\t * Latch a sample by reading the debug status data register. A\n+\t * single read returns the previous latch, so read it three times\n+\t * as the documented phase-difference procedure does.\n+\t */\n+\tfor (i = 0; i \u003c SIT9531X_DBG_STATUS_READS; i++) {\n+\t\trc = sit9531x_read_pll_u8(sitdev, pll_idx,\n+\t\t\t\t\t SIT9531X_PLL_REG_DBG_STATUS_DATA,\n+\t\t\t\t\t \u0026v);\n+\t\tif (rc)\n+\t\t\tgoto relock;\n+\t}\n+\n+\ttdc_raw = 0;\n+\n+\trc = sit9531x_read_pll_u8(sitdev, pll_idx,\n+\t\t\t\t SIT9531X_PLL_REG_DBG_DATA_4, \u0026v);\n+\tif (rc)\n+\t\tgoto relock;\n+\tsign = !!(v \u0026 BIT(SIT9531X_TDC_SIGN_BIT));\n+\ttdc_raw = (u64)(v \u0026 SIT9531X_TDC_MAG_HI_MASK) \u003c\u003c 32;\n+\n+\trc = sit9531x_read_pll_u8(sitdev, pll_idx,\n+\t\t\t\t SIT9531X_PLL_REG_DBG_DATA_3, \u0026v);\n+\tif (rc)\n+\t\tgoto relock;\n+\ttdc_raw |= (u64)v \u003c\u003c 24;\n+\n+\trc = sit9531x_read_pll_u8(sitdev, pll_idx,\n+\t\t\t\t SIT9531X_PLL_REG_DBG_DATA_2, \u0026v);\n+\tif (rc)\n+\t\tgoto relock;\n+\ttdc_raw |= (u64)v \u003c\u003c 16;\n+\n+\trc = sit9531x_read_pll_u8(sitdev, pll_idx,\n+\t\t\t\t SIT9531X_PLL_REG_DBG_DATA_1, \u0026v);\n+\tif (rc)\n+\t\tgoto relock;\n+\ttdc_raw |= (u64)v \u003c\u003c 8;\n+\n+\trc = sit9531x_read_pll_u8(sitdev, pll_idx,\n+\t\t\t\t SIT9531X_PLL_REG_DBG_DATA_0, \u0026v);\n+\tif (rc)\n+\t\tgoto relock;\n+\ttdc_raw |= v;\n+\n+\t/*\n+\t * Apply sign. Per the register map the sign bit is active-high\n+\t * for a positive offset: bit set -\u003e +code, bit clear -\u003e -code.\n+\t */\n+\ttdc_signed = sign ? (s64)tdc_raw : -(s64)tdc_raw;\n+\n+\t/*\n+\t * phase_diff (seconds) = tdc_code / fvco\n+\t * phase_diff (ps) = tdc_code * 1e12 / fvco\n+\t *\n+\t * Scaled by the subsystem's divider before the division rather than\n+\t * after it: one TDC code is 1e12 / fvco ps, a fraction of a\n+\t * picosecond more often than not (203.45 ps at 4.9152 GHz), and\n+\t * truncating to whole picoseconds first would leave the three\n+\t * fractional digits the attribute carries always zero. The\n+\t * quotient fits u64 for any rate sit9531x_get_fvco() returns: it\n+\t * refuses one below the VCO band.\n+\t *\n+\t * mul_u64_u64_div_u64() keeps the exact Hz denominator; dividing\n+\t * by whole MHz instead would lose up to ~40 ppm of scale on a\n+\t * fractional-DIVN Fvco.\n+\t */\n+\tmag = mul_u64_u64_div_u64(tdc_signed \u003c 0 ? -tdc_signed : tdc_signed,\n+\t\t\t\t 1000000000000ULL * DPLL_PHASE_OFFSET_DIVIDER,\n+\t\t\t\t fvco);\n+\t*phase_offset = tdc_signed \u003c 0 ? -(s64)mag : (s64)mag;\n+\n+\trc = 0;\n+\n+relock:\n+\tif (have_old) {\n+\t\tret = sit9531x_write_pll_u8(sitdev, pll_idx,\n+\t\t\t\t\t SIT9531X_PLL_REG_DBG_READ_CODE,\n+\t\t\t\t\t old_read_code);\n+\t\tif (ret \u0026\u0026 !rc)\n+\t\t\trc = ret;\n+\t\tret = sit9531x_write_pll_u8(sitdev, pll_idx,\n+\t\t\t\t\t SIT9531X_PLL_REG_DBG_WRITE_CODE,\n+\t\t\t\t\t old_write_code);\n+\t\tif (ret \u0026\u0026 !rc)\n+\t\t\trc = ret;\n+\t}\n+\n+\t/*\n+\t * Close the debug window again. The key register opens every debug\n+\t * register on this PLL while it holds the unlock value, and this read\n+\t * runs on every pin-get of a connected input, so leaving it open\n+\t * would mean normal monitoring permanently unlocks the block.\n+\t */\n+\tret = sit9531x_write_pll_u8(sitdev, pll_idx,\n+\t\t\t\t SIT9531X_PLL_REG_DEBUG,\n+\t\t\t\t SIT9531X_PLL_DEBUG_LOCK);\n+\tif (ret \u0026\u0026 !rc)\n+\t\trc = ret;\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * sit9531x_ref_state_fetch - read input reference status from hardware\n+ * @index:\tlogical input index\n+ *\n+ * Reads whether the lane's receiver is on, from the Page 0x02 force and\n+ * state bits. Signal status comes from sit9531x_input_mon_fetch(),\n+ * which reads every lane's clock monitor in one pass.\n+ */\n+static int sit9531x_ref_state_fetch(struct sit9531x_dev *sitdev, u8 index)\n+{\n+\tunsigned int force_reg, state_reg;\n+\tu8 pair, force, state;\n+\tstruct sit9531x_ref *ref;\n+\tint rc;\n+\n+\t/*\n+\t * The XTAL/XO reference (index SIT9531X_MAX_INPUTS) is the on-chip\n+\t * oscillator that feeds every PLL. It cannot be routed or deselected,\n+\t * so its pin is modeled as permanently connected (see\n+\t * sit9531x_dpll_xo_pin_ops) and has no receiver to gate. Only the\n+\t * routable per-lane inputs (0..num_inputs-1) are polled here.\n+\t */\n+\tif (index \u003e= SIT9531X_MAX_INPUTS)\n+\t\treturn -EINVAL;\n+\n+\tref = \u0026sitdev-\u003eref[index];\n+\tpair = sit9531x_input_pair(index);\n+\n+\t/*\n+\t * Whether the receiver is on. This has to come from the chip: it\n+\t * is the loaded configuration that decides, and without reading it\n+\t * back every input would look disabled until something called\n+\t * sit9531x_input_enable(). A lane counts as disabled only while\n+\t * the force bit overrides it to the off state; with the force bit\n+\t * clear it follows the configuration, which is the enabled case.\n+\t */\n+\tsit9531x_input_get_regs(sitdev, index, \u0026force_reg, \u0026state_reg);\n+\n+\trc = sit9531x_read_u8(sitdev, force_reg, \u0026force);\n+\tif (rc)\n+\t\treturn rc;\n+\trc = sit9531x_read_u8(sitdev, state_reg, \u0026state);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tref-\u003eenabled = !((force \u0026 BIT(pair)) \u0026\u0026 !(state \u0026 BIT(pair)));\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * sit9531x_input_mode_fetch - detect SE/DE configuration of an input pair\n+ * @pair:\tinput pair number (0-3)\n+ *\n+ * Reads CLKINx_INPUT_MODE and stores the detected signal mode on both\n+ * lanes of the pair. A pair with neither SE lane enabled is running\n+ * differential.\n+ */\n+static int sit9531x_input_mode_fetch(struct sit9531x_dev *sitdev, u8 pair)\n+{\n+\tenum sit9531x_signal_mode sig_mode;\n+\tu8 mode;\n+\tint rc;\n+\n+\trc = sit9531x_read_u8(sitdev, SIT9531X_REG_IN_MODE(pair), \u0026mode);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tif (mode \u0026 (SIT9531X_IN_MODE_SE_P_EN | SIT9531X_IN_MODE_SE_N_EN))\n+\t\tsig_mode = SIT9531X_MODE_SE;\n+\telse\n+\t\tsig_mode = SIT9531X_MODE_DE;\n+\n+\tsitdev-\u003eref[pair * 2].sig_mode = sig_mode;\n+\tsitdev-\u003eref[pair * 2 + 1].sig_mode = sig_mode;\n+\n+\tdev_dbg(sitdev-\u003edev, \"CLKIN%u mode reg 0x%02x -\u003e %s\\n\", pair, mode,\n+\t\tsig_mode == SIT9531X_MODE_DE ? \"differential\" : \"single-ended\");\n+\n+\treturn 0;\n+}\n+\n+/* Read the PLL active-state bit (PLL page reg 0x02 bit 0). */\n+static int sit9531x_pll_is_active(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t\t bool *active)\n+{\n+\tu8 v;\n+\tint rc;\n+\n+\trc = sit9531x_read_pll_u8(sitdev, pll_idx, SIT9531X_PLL_REG_ACTIVE, \u0026v);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\t*active = !!(v \u0026 SIT9531X_PLL_ACTIVE_BIT);\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * sit9531x_chan_routed_ref_read - read the reference the device feeds a PLL\n+ * @sitdev:\tdevice pointer\n+ * @pll_idx:\tPLL index (0-3)\n+ * @ref:\tresult: logical input index, SIT9531X_INTSYNC_PIN_ID for the\n+ *\t\tinter-PLL sync net, or SIT9531X_REF_INVALID when the code\n+ *\t\tnames no registered input\n+ *\n+ * The active selection names the source the PLL was told to follow. The\n+ * input subsystem feeds the PLL that source while it has signal and falls\n+ * back to another listed source on its own when it does not, without\n+ * touching the selection, so after such a fallback the selection no\n+ * longer says which reference the PLL runs on. The debug status bus\n+ * does: addressed at the routed reference, it returns the source code\n+ * the input subsystem currently feeds the PLL.\n+ *\n+ * This names the source and nothing more. Whether the PLL is locked to\n+ * it is read from the outer-loop lock and the lane's clock monitor; in\n+ * free-run the PLL is fed nothing and the bus is not consulted, so the\n+ * caller checks the mode first.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ *\n+ * Return: 0 on success, \u003c0 on error\n+ */\n+int sit9531x_chan_routed_ref_read(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t\t u8 *ref)\n+{\n+\tint rc, lock_rc;\n+\tu8 v, i;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\trc = sit9531x_write_pll_u8(sitdev, pll_idx, SIT9531X_PLL_REG_DEBUG,\n+\t\t\t\t SIT9531X_PLL_DEBUG_UNLOCK);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = sit9531x_write_pll_u8(sitdev, pll_idx,\n+\t\t\t\t SIT9531X_PLL_REG_DBG_STATUS_ADDR,\n+\t\t\t\t SIT9531X_DBG_STATUS_ROUTED_SRC);\n+\tif (rc)\n+\t\tgoto relock;\n+\n+\t/* The first reads return earlier latches; the last is current. */\n+\tfor (i = 0; i \u003c SIT9531X_DBG_STATUS_READS; i++) {\n+\t\trc = sit9531x_read_pll_u8(sitdev, pll_idx,\n+\t\t\t\t\t SIT9531X_PLL_REG_DBG_STATUS_DATA,\n+\t\t\t\t\t \u0026v);\n+\t\tif (rc)\n+\t\t\tgoto relock;\n+\t}\n+\n+\t/*\n+\t * Canonicalise like the table entries: a differential pair fed\n+\t * through its N-lane code is the P-lane pin.\n+\t */\n+\tv = sit9531x_prio_src_canon(sitdev, v \u0026 SIT9531X_DBG_ROUTED_SRC_MASK);\n+\t*ref = sit9531x_hw_src_input(v);\n+\n+relock:\n+\t/*\n+\t * Lock the debug registers again. A read that failed is reported\n+\t * ahead of a lock that did; a lock that failed on its own leaves the\n+\t * page open to a stray write and is reported too.\n+\t */\n+\tlock_rc = sit9531x_write_pll_u8(sitdev, pll_idx, SIT9531X_PLL_REG_DEBUG,\n+\t\t\t\t\tSIT9531X_PLL_DEBUG_LOCK);\n+\n+\treturn rc ? rc : lock_rc;\n+}\n+\n+/*\n+ * sit9531x_chan_state_fetch - read PLL channel status from hardware\n+ *\n+ * Reads whether the PLL runs, its lock and holdover state, its mode, the\n+ * active selection and its priority table.\n+ */\n+static int sit9531x_chan_state_fetch(struct sit9531x_dev *sitdev, u8 pll_idx)\n+{\n+\tstruct sit9531x_chan *chan = \u0026sitdev-\u003echan[pll_idx];\n+\tu8 status, outer_lol, inner_lol, ho_freeze, pll_status_1;\n+\tu8 srcs[SIT9531X_PRIO_MAX_SLOTS];\n+\tu8 routed = SIT9531X_REF_INVALID;\n+\tbool active, tracking;\n+\tint rc;\n+\n+\t/*\n+\t * Whether the PLL is running at all. The loss-of-lock bit read\n+\t * below is driven by the PLL itself, so on one the loaded\n+\t * configuration leaves unused it simply stays clear and would\n+\t * otherwise read as a lock.\n+\t */\n+\trc = sit9531x_pll_is_active(sitdev, pll_idx, \u0026active);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = sit9531x_read_pll_u8(sitdev, pll_idx,\n+\t\t\t\t SIT9531X_PLL_REG_STATUS, \u0026status);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = sit9531x_read_u8(sitdev, SIT9531X_REG_OUTER_LOL_STATUS,\n+\t\t\t \u0026outer_lol);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = sit9531x_read_u8(sitdev, SIT9531X_REG_PLL_INNER_LOL_STATUS,\n+\t\t\t \u0026inner_lol);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = sit9531x_read_u8(sitdev, SIT9531X_REG_HO_FREEZE_STATUS,\n+\t\t\t \u0026ho_freeze);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\t/*\n+\t * The reference the device feeds the PLL, read only while the PLL\n+\t * tracks one -- running, locked, outer loop on, not frozen -- which\n+\t * is the only case the pin state reports it. In free-run the PLL\n+\t * is fed nothing and the status bus is not consulted.\n+\t */\n+\ttracking = active \u0026\u0026 !(outer_lol \u0026 BIT(pll_idx)) \u0026\u0026\n+\t\t !(status \u0026 SIT9531X_PLL_STATUS_OUTER_DIS) \u0026\u0026\n+\t\t !(ho_freeze \u0026 BIT(pll_idx));\n+\tif (tracking) {\n+\t\trc = sit9531x_chan_routed_ref_read(sitdev, pll_idx, \u0026routed);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\t}\n+\n+\trc = sit9531x_read_pll_u8(sitdev, pll_idx, SIT9531X_PLL_REG_STATUS_1,\n+\t\t\t\t \u0026pll_status_1);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\t/*\n+\t * Which sources this PLL may select. The table is configuration and\n+\t * changes only through the driver, but reading it back keeps the\n+\t * membership the pin state getters report tied to the hardware\n+\t * instead of to a value the driver maintains on the side.\n+\t */\n+\trc = sit9531x_prio_table_read(sitdev, pll_idx, srcs);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tsit9531x_prio_mask_build(sitdev, pll_idx, srcs);\n+\n+\t/*\n+\t * The configured priorities come from the table the first time it\n+\t * is read, and again whenever it no longer matches what the driver\n+\t * last wrote: something else -- a profile reload, a direct I2C\n+\t * tool -- rewrote it, and that table is now the configuration.\n+\t */\n+\tif (!chan-\u003eseen_valid ||\n+\t memcmp(srcs, chan-\u003eseen_srcs, sizeof(chan-\u003eseen_srcs)))\n+\t\tsit9531x_prio_cfg_seed(sitdev, pll_idx, srcs);\n+\n+\t/* STATUS_1_GENERIC reports loss of lock, so invert it. */\n+\tchan-\u003eactive = active;\n+\tchan-\u003elocked = active \u0026\u0026 !(outer_lol \u0026 BIT(pll_idx));\n+\tchan-\u003emode = !!(status \u0026 SIT9531X_PLL_STATUS_OUTER_DIS);\n+\tchan-\u003erouted_ref = routed;\n+\tchan-\u003einner_lol = !!(inner_lol \u0026 BIT(pll_idx));\n+\tchan-\u003eho_freeze = !!(ho_freeze \u0026 BIT(pll_idx));\n+\tchan-\u003eho_valid = !!(pll_status_1 \u0026 SIT9531X_PLL_STATUS_1_HO_VALID);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * sit9531x_out_state_fetch - read output status from hardware\n+ *\n+ * Takes the output PLL association from the PLL page output map\n+ * registers into out-\u003erouted / out-\u003epll_idx, and the current drive state\n+ * from the Hi-Z force bits into out-\u003eenabled. The two are separate:\n+ * routing decides whether the output gets a DPLL pin at all,\n+ * while a muted but routed output keeps its pin and reports\n+ * DPLL_PIN_STATE_DISCONNECTED until it is un-muted.\n+ */\n+static int sit9531x_out_state_fetch(struct sit9531x_dev *sitdev, u8 index)\n+{\n+\tstruct sit9531x_out *out = \u0026sitdev-\u003eout[index];\n+\tu8 map_lo, map_hi, slot, bit, claimed;\n+\tint pll_idx;\n+\tu16 mask;\n+\tbool muted;\n+\tint rc;\n+\n+\tslot = sitdev-\u003einfo-\u003eclkout_map[index];\n+\n+\trc = sit9531x_output_mode_fetch(sitdev, index);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = sit9531x_output_forced_hiz(sitdev, index, \u0026muted);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tsitdev-\u003eout[index].state_stale = false;\n+\n+\t/*\n+\t * Each PLL page holds the PLL's output-enable mask, twelve bits:\n+\t * bits 0-7 in OUT_MAP_LO and bits 8-11 in OUT_MAP_HI[3:0]. PLLA and\n+\t * PLLB keep OUTn at bit n; PLLC and PLLD keep the outputs in mirrored\n+\t * order, OUTn at bit 11 - n, as SiTime's procedure scripts read\n+\t * them. The index is the physical output the pin drives, not\n+\t * the driver's logical one (translated above via the chip-info\n+\t * clkout_map[]: identity on SiT95316, non-contiguous on SiT95317).\n+\t *\n+\t * An output belongs to one PLL. Take the first that claims it, and\n+\t * say so if another does too.\n+\t */\n+\tclaimed = 0;\n+\tfor (pll_idx = 0; pll_idx \u003c SIT9531X_NUM_PLLS; pll_idx++) {\n+\t\trc = sit9531x_read_pll_u8(sitdev, pll_idx,\n+\t\t\t\t\t SIT9531X_PLL_REG_OUT_MAP_LO, \u0026map_lo);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\n+\t\trc = sit9531x_read_pll_u8(sitdev, pll_idx,\n+\t\t\t\t\t SIT9531X_PLL_REG_OUT_MAP_HI, \u0026map_hi);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\n+\t\tmask = map_lo | (map_hi \u0026 GENMASK(3, 0)) \u003c\u003c 8;\n+\t\tbit = pll_idx \u003c 2 ? slot : SIT9531X_MAX_OUTPUTS - 1 - slot;\n+\t\tif (mask \u0026 BIT(bit))\n+\t\t\tclaimed |= BIT(pll_idx);\n+\t}\n+\n+\tif (!claimed) {\n+\t\t/* Output not mapped to any PLL */\n+\t\tout-\u003epll_idx = 0;\n+\t\tout-\u003erouted = false;\n+\t\tout-\u003eenabled = false;\n+\t\treturn 0;\n+\t}\n+\n+\tout-\u003epll_idx = __ffs(claimed);\n+\tout-\u003erouted = true;\n+\tout-\u003eenabled = !muted;\n+\n+\tif (claimed \u0026 (claimed - 1))\n+\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t \"OUT%u is in the output-enable mask of more than one PLL (0x%x); taking PLL%c\\n\",\n+\t\t\t slot, claimed, 'A' + out-\u003epll_idx);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * sit9531x_ref_pll_mask_fetch - seed the input-to-PLL usage masks\n+ *\n+ * ref-\u003epll_mask is the refcount the disconnect path uses to decide when\n+ * an input receiver may be powered down: the physical input is only\n+ * disabled once the last DPLL has released it. It therefore has to\n+ * start out matching the hardware. Without this pass every mask starts\n+ * at zero, and disconnecting an input from one DPLL drops the mask to\n+ * zero and disables a receiver the other DPLLs are still locked to.\n+ *\n+ * An input is counted for a PLL when it appears in that PLL's Page-1\n+ * priority table, which is exactly the condition the connect and\n+ * disconnect callbacks maintain. Sources that are not physical inputs\n+ * (OCXO, INTSYNC) and reserved codes are skipped.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+static int sit9531x_ref_pll_mask_fetch(struct sit9531x_dev *sitdev)\n+{\n+\tu8 srcs[SIT9531X_PRIO_MAX_SLOTS];\n+\tu8 pll_idx, slot, index;\n+\tint rc;\n+\n+\tfor (pll_idx = 0; pll_idx \u003c SIT9531X_NUM_PLLS; pll_idx++) {\n+\t\trc = sit9531x_prio_table_read(sitdev, pll_idx, srcs);\n+\t\tif (rc)\n+\t\t\treturn rc;\n+\n+\t\tfor (slot = 0; slot \u003c SIT9531X_PRIO_MAX_SLOTS; slot++) {\n+\t\t\tindex = sit9531x_hw_src_input(srcs[slot]);\n+\t\t\tif (index \u003e= sitdev-\u003einfo-\u003enum_inputs)\n+\t\t\t\tcontinue;\n+\n+\t\t\t/*\n+\t\t\t * On a differentially configured pair only the P lane\n+\t\t\t * has a DPLL pin, so that is the lane the connect and\n+\t\t\t * disconnect callbacks account for. Fold an N-lane\n+\t\t\t * table entry onto its P lane, otherwise the count\n+\t\t\t * would land on a lane nothing ever releases. The\n+\t\t\t * signaling mode is already known here:\n+\t\t\t * sit9531x_input_mode_fetch() runs first.\n+\t\t\t */\n+\t\t\tif (sit9531x_input_is_n(index) \u0026\u0026\n+\t\t\t sitdev-\u003eref[index].sig_mode == SIT9531X_MODE_DE)\n+\t\t\t\tindex--;\n+\n+\t\t\tsitdev-\u003eref[index].pll_mask |= BIT(pll_idx);\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * sit9531x_dev_state_fetch - read all hardware state at startup\n+ *\n+ * Called once during probe to populate the initial state cache.\n+ */\n+static int sit9531x_dev_state_fetch(struct sit9531x_dev *sitdev)\n+{\n+\tint rc;\n+\tu8 i;\n+\n+\t/* Detect SE/DE configuration before any per-lane access */\n+\tfor (i = 0; i \u003c sitdev-\u003einfo-\u003enum_inputs / 2; i++) {\n+\t\trc = sit9531x_input_mode_fetch(sitdev, i);\n+\t\tif (rc) {\n+\t\t\tdev_err(sitdev-\u003edev,\n+\t\t\t\t\"Failed to fetch CLKIN%u mode: %d\\n\", i, rc);\n+\t\t\treturn rc;\n+\t\t}\n+\t}\n+\n+\tfor (i = 0; i \u003c sitdev-\u003einfo-\u003enum_inputs; i++) {\n+\t\trc = sit9531x_ref_state_fetch(sitdev, i);\n+\t\tif (rc) {\n+\t\t\tdev_err(sitdev-\u003edev,\n+\t\t\t\t\"Failed to fetch input %u state: %d\\n\", i, rc);\n+\t\t\treturn rc;\n+\t\t}\n+\t}\n+\n+\trc = sit9531x_input_mon_fetch(sitdev);\n+\tif (rc) {\n+\t\tdev_err(sitdev-\u003edev,\n+\t\t\t\"Failed to read the input clock monitors: %d\\n\", rc);\n+\t\treturn rc;\n+\t}\n+\n+\t/*\n+\t * The priority-table read walks the Page-1 registers, so it runs\n+\t * with multiop_lock held like every other multi-register sequence.\n+\t * Nothing can race with it here -- the DPLLs are not registered and\n+\t * the monitor is not running yet -- but the page handling stays\n+\t * serialized the same way as at runtime.\n+\t */\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\trc = sit9531x_ref_pll_mask_fetch(sitdev);\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\tif (rc) {\n+\t\tdev_err(sitdev-\u003edev,\n+\t\t\t\"Failed to fetch input priority tables: %d\\n\", rc);\n+\t\treturn rc;\n+\t}\n+\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\trc = sit9531x_intsync_src_detect(sitdev);\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\tif (rc) {\n+\t\tdev_err(sitdev-\u003edev,\n+\t\t\t\"Failed to detect INTSYNC source: %d\\n\", rc);\n+\t\treturn rc;\n+\t}\n+\n+\tfor (i = 0; i \u003c sitdev-\u003einfo-\u003enum_outputs; i++) {\n+\t\tbool clamped;\n+\t\ts32 phase_ps;\n+\n+\t\trc = sit9531x_out_state_fetch(sitdev, i);\n+\t\tif (rc) {\n+\t\t\tdev_err(sitdev-\u003edev,\n+\t\t\t\t\"Failed to fetch output %u state: %d\\n\", i, rc);\n+\t\t\treturn rc;\n+\t\t}\n+\n+\t\t/*\n+\t\t * The delay registers are part of the profile the chip loads\n+\t\t * before probe, so an output can already carry one. Seeding\n+\t\t * the cache from the device is what lets a request of 0 ps\n+\t\t * clear it: the core drops a request equal to what the\n+\t\t * getter reports, and a cache that started at zero would\n+\t\t * make clearing a programmed delay impossible. An output\n+\t\t * the configuration does not route has no Fvco to decode\n+\t\t * against, which is not an error here. A delay beyond the\n+\t\t * range is reported as its end but not armed: re-timing it\n+\t\t * on a rate change would write that end over the profile's\n+\t\t * delay.\n+\t\t */\n+\t\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\t\trc = sit9531x_output_phase_read(sitdev, i, \u0026phase_ps, \u0026clamped);\n+\t\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\t\tif (!rc) {\n+\t\t\tsitdev-\u003eout[i].phase_adj = phase_ps;\n+\t\t\tsitdev-\u003eout[i].phase_armed = !clamped \u0026\u0026 phase_ps;\n+\t\t} else if (rc != -ENODATA) {\n+\t\t\tdev_err(sitdev-\u003edev,\n+\t\t\t\t\"Failed to read output %u delay: %d\\n\",\n+\t\t\t\ti, rc);\n+\t\t\treturn rc;\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * The channel fetch walks the priority table and the debug status\n+\t * bus, multi-register sequences that expect the lock; nothing races\n+\t * with them yet, but they are serialized the same way as at runtime.\n+\t */\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\tfor (i = 0; i \u003c SIT9531X_NUM_PLLS; i++) {\n+\t\trc = sit9531x_chan_state_fetch(sitdev, i);\n+\t\tif (rc)\n+\t\t\tbreak;\n+\t}\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\tif (rc) {\n+\t\tdev_err(sitdev-\u003edev, \"Failed to fetch PLL%c state: %d\\n\",\n+\t\t\t'A' + i, rc);\n+\t\treturn rc;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void sit9531x_dev_ref_states_update(struct sit9531x_dev *sitdev)\n+{\n+\tint i, rc;\n+\n+\tfor (i = 0; i \u003c sitdev-\u003einfo-\u003enum_inputs; i++) {\n+\t\trc = sit9531x_ref_state_fetch(sitdev, i);\n+\t\tif (rc)\n+\t\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t\t \"Failed to get REF%u status: %d\\n\", i, rc);\n+\t}\n+\n+\trc = sit9531x_input_mon_fetch(sitdev);\n+\tif (rc)\n+\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t \"Failed to read the input clock monitors: %d\\n\", rc);\n+}\n+\n+/*\n+ * sit9531x_ref_pll_mask_rebuild - re-derive the input receiver refcounts\n+ *\n+ * ref-\u003epll_mask decides when an input receiver may be powered down, and\n+ * the connect and disconnect paths maintain it by hand. A request that\n+ * failed part way through leaves it describing a table the device does\n+ * not hold, and nothing else corrected it: a later disconnect could then\n+ * drop the count to zero and gate an input another PLL is still locked\n+ * to. Re-derive every mask from the tables the poll has just read. No\n+ * extra bus traffic -- sit9531x_chan_state_fetch() refreshed the masks\n+ * this reads immediately before.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+static void sit9531x_ref_pll_mask_rebuild(struct sit9531x_dev *sitdev)\n+{\n+\tu8 pll_idx, src, index;\n+\n+\tfor (index = 0; index \u003c sitdev-\u003einfo-\u003enum_inputs; index++)\n+\t\tsitdev-\u003eref[index].pll_mask = 0;\n+\n+\tfor (pll_idx = 0; pll_idx \u003c SIT9531X_NUM_PLLS; pll_idx++) {\n+\t\tu16 mask = sitdev-\u003echan[pll_idx].prio_mask;\n+\n+\t\tfor (src = 0; src \u003c SIT9531X_PRIO_NUM_SRC; src++) {\n+\t\t\tif (!(mask \u0026 BIT(src)))\n+\t\t\t\tcontinue;\n+\n+\t\t\tindex = sit9531x_hw_src_input(src);\n+\t\t\tif (index \u003c sitdev-\u003einfo-\u003enum_inputs)\n+\t\t\t\tsitdev-\u003eref[index].pll_mask |= BIT(pll_idx);\n+\t\t}\n+\t}\n+}\n+\n+/*\n+ * Release a forced holdover that a table write owes. The write forces\n+ * holdover and releases it at the end; when the release failed, nothing\n+ * else in the driver clears the bit -- a retry of the same request finds\n+ * the table already as asked and does not run the sequence again -- so\n+ * the poll retries it each tick until it lands.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+static void sit9531x_ho_release_retry(struct sit9531x_dev *sitdev, u8 pll_idx)\n+{\n+\tstruct sit9531x_chan *chan = \u0026sitdev-\u003echan[pll_idx];\n+\n+\tif (!chan-\u003eho_release_pending)\n+\t\treturn;\n+\n+\tif (sit9531x_update_pll_u8(sitdev, pll_idx, SIT9531X_PLL_REG_HO_CTRL,\n+\t\t\t\t BIT(SIT9531X_PLL_HO_FORCE_BIT), 0))\n+\t\treturn;\n+\n+\tchan-\u003eho_release_pending = false;\n+\tdev_info(sitdev-\u003edev, \"PLL%c: forced holdover released\\n\",\n+\t\t 'A' + pll_idx);\n+}\n+\n+static void sit9531x_dev_chan_states_update(struct sit9531x_dev *sitdev)\n+{\n+\tint i, rc;\n+\n+\tfor (i = 0; i \u003c SIT9531X_NUM_PLLS; i++) {\n+\t\tsit9531x_ho_release_retry(sitdev, i);\n+\t\trc = sit9531x_chan_state_fetch(sitdev, i);\n+\t\tif (rc)\n+\t\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t\t \"Failed to get PLL%c state: %d\\n\",\n+\t\t\t\t 'A' + i, rc);\n+\t}\n+\n+\tsit9531x_ref_pll_mask_rebuild(sitdev);\n+}\n+\n+/*\n+ * sit9531x_dev_periodic_work - periodic hardware state polling\n+ * @work:\tkthread_work pointer\n+ *\n+ * Polls hardware state at SIT9531X_STATUS_POLL_MS intervals.\n+ * Updates reference and channel states, then delegates change\n+ * detection to sit9531x_dpll_changes_check() for each registered DPLL.\n+ */\n+static void sit9531x_dev_periodic_work(struct kthread_work *work)\n+{\n+\tstruct sit9531x_dev *sitdev = container_of(work, struct sit9531x_dev,\n+\t\t\t\t\t\t work.work);\n+\tstruct sit9531x_dpll *sitdpll;\n+\tint rc;\n+\n+\t/*\n+\t * Update the cached ref[]/chan[] arrays under multiop_lock so the\n+\t * fetches are serialized against the DPLL callbacks that read\n+\t * these fields and against the chip's page selector.\n+\t *\n+\t * The lock is then dropped before sit9531x_dpll_changes_check(),\n+\t * which calls dpll_pin_change_ntf() / dpll_device_change_ntf().\n+\t * Those notification helpers take DPLL-subsystem locks that are\n+\t * already held when our callbacks are invoked from netlink\n+\t * context, and nesting multiop_lock around them would invert the\n+\t * lock order. changes_check() reads the cache published above,\n+\t * which is already consistent.\n+\t */\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\tsit9531x_dev_ref_states_update(sitdev);\n+\tsit9531x_dev_chan_states_update(sitdev);\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\n+\tlist_for_each_entry(sitdpll, \u0026sitdev-\u003edplls, list)\n+\t\tsit9531x_dpll_changes_check(sitdpll);\n+\n+\t/*\n+\t * Acknowledge the chip's notification latches after the tick has\n+\t * read and acted on them. Without this, the W1C bits remain set\n+\t * and -- on boards that wire INTRB -- the line stays asserted,\n+\t * re-firing the threaded handler back to back. The helper writes\n+\t * W1C bits across page 0 and page 6 and must run under\n+\t * multiop_lock to serialize the page selector against userspace\n+\t * dpll ops. Failure is non-fatal: status was already consumed\n+\t * for this tick and the next tick re-processes whatever stayed\n+\t * latched.\n+\t */\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\trc = sit9531x_clear_notifications(sitdev);\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\tif (rc)\n+\t\tdev_warn_ratelimited(sitdev-\u003edev,\n+\t\t\t\t \"Failed to clear notifications: %d\\n\",\n+\t\t\t\t rc);\n+\n+\t/* Run twice a second */\n+\tkthread_queue_delayed_work(sitdev-\u003ekworker, \u0026sitdev-\u003ework,\n+\t\t\t\t msecs_to_jiffies(SIT9531X_STATUS_POLL_MS));\n+}\n+\n+/*\n+ * sit9531x_irq_thread_fn - threaded IRQ handler for the chip's INTRB line\n+ *\n+ * Triggered when the chip asserts INTRB (and only when DT wires up the\n+ * client interrupt; absent property == handler never installed). The\n+ * action mirrors a periodic-work tick: queue an immediate run so status\n+ * registers are read and DPLL changes_check fires without waiting for\n+ * the next poll deadline. Polling continues to run as a fallback.\n+ */\n+static irqreturn_t sit9531x_irq_thread_fn(int irq, void *data)\n+{\n+\tstruct sit9531x_dev *sitdev = data;\n+\tint rc;\n+\n+\t/*\n+\t * Acknowledge the chip's notification latches from the threaded\n+\t * handler itself. With IRQF_ONESHOT the line is unmasked on\n+\t * return, so deferring the W1C clear to the async kworker would\n+\t * let a still-asserted INTRB re-fire immediately (interrupt storm).\n+\t * Clear here, then kick the poll worker to read state and run\n+\t * changes_check.\n+\t */\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\trc = sit9531x_clear_notifications(sitdev);\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\tif (rc) {\n+\t\tdev_warn_ratelimited(sitdev-\u003edev,\n+\t\t\t\t \"IRQ: failed to clear notifications: %d\\n\",\n+\t\t\t\t rc);\n+\t\t/*\n+\t\t * The latch was not acknowledged, so with IRQF_ONESHOT the\n+\t\t * still-asserted line re-enters this handler as soon as it\n+\t\t * returns. Returning IRQ_NONE leaves that to the spurious\n+\t\t * detector, which needs roughly 100000 interrupts and resets\n+\t\t * its count every tenth of a second -- unreachable when each\n+\t\t * pass costs an I2C timeout. Give up on the line instead:\n+\t\t * the periodic poll reads the same state without it, so the\n+\t\t * driver keeps working on a board whose INTRB cannot be\n+\t\t * acknowledged.\n+\t\t */\n+\t\tif (++sitdev-\u003eirq_ack_fails \u003c SIT9531X_IRQ_ACK_TRIES)\n+\t\t\treturn IRQ_NONE;\n+\n+\t\tdev_err(sitdev-\u003edev,\n+\t\t\t\"IRQ %d disabled: notifications cannot be cleared, polling only\\n\",\n+\t\t\tirq);\n+\t\tdisable_irq_nosync(irq);\n+\t\treturn IRQ_NONE;\n+\t}\n+\n+\tsitdev-\u003eirq_ack_fails = 0;\n+\n+\tkthread_mod_delayed_work(sitdev-\u003ekworker, \u0026sitdev-\u003ework, 0);\n+\treturn IRQ_HANDLED;\n+}\n+\n+/*\n+ * The driver reports every PLL as selecting by priority. A profile can\n+ * put a PLL in manual active select instead (PLL_CONFIG1F_PLL bit 6), and\n+ * the mode userspace sees would then be wrong; say so rather than\n+ * reconfigure what the profile chose. The warning also says what the PLL\n+ * then follows: its active selection, or with MISCINNER_PLL bit 5 the\n+ * manual input select -- the input-select pins, or the input code that\n+ * GPIO_INPUT_FUNC_CTRL5..8 holds when its bit 4 is set.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+static void sit9531x_manual_sel_report(struct sit9531x_dev *sitdev)\n+{\n+\tu8 cfg, misc, sel, i;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tfor (i = 0; i \u003c SIT9531X_NUM_PLLS; i++) {\n+\t\tif (sit9531x_read_pll_u8(sitdev, i, SIT9531X_PLL_REG_CONFIG1F,\n+\t\t\t\t\t \u0026cfg))\n+\t\t\treturn;\n+\t\tif (!(cfg \u0026 SIT9531X_PLL_CONFIG1F_MANUAL_SEL))\n+\t\t\tcontinue;\n+\n+\t\tif (sit9531x_read_pll_u8(sitdev, i, SIT9531X_PLL_REG_MISCINNER,\n+\t\t\t\t\t \u0026misc))\n+\t\t\treturn;\n+\t\tif (!(misc \u0026 SIT9531X_PLL_MISCINNER_MAN_IN_SEL)) {\n+\t\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t\t \"PLL%c: the profile selects its reference manually, following its active selection; it is reported as automatic\\n\",\n+\t\t\t\t 'A' + i);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (sit9531x_read_u8(sitdev, SIT9531X_REG_MAN_IN_SEL(i), \u0026sel))\n+\t\t\treturn;\n+\t\tif (sel \u0026 SIT9531X_MAN_IN_SEL_FROM_REG) {\n+\t\t\tsel = FIELD_GET(SIT9531X_MAN_IN_SEL_MASK, sel);\n+\t\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t\t \"PLL%c: the profile selects its reference manually, input code %u from the register; it is reported as automatic\\n\",\n+\t\t\t\t 'A' + i, sel);\n+\t\t} else {\n+\t\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t\t \"PLL%c: the profile selects its reference manually, from the input-select pins; it is reported as automatic\\n\",\n+\t\t\t\t 'A' + i);\n+\t\t}\n+\t}\n+}\n+\n+/*\n+ * Report what the device loaded from its EEPROM, and warn if it does not\n+ * look like a healthy load.\n+ *\n+ * A profile that failed to load leaves the part running something other\n+ * than what the board was designed around -- dividers, output routing\n+ * and priority tables all differ -- while every register still reads\n+ * back a plausible value. Naming the profile and saying whether the\n+ * load was clean turns that into something visible at startup instead of\n+ * something inferred from measurements later.\n+ *\n+ * This only reports. Boards in this family may have their\n+ * configuration pushed over I2C rather than held in an EEPROM, and there\n+ * the CRC pair means nothing, so a mismatch is not grounds for refusing\n+ * to drive the device.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+static void sit9531x_eeprom_state_report(struct sit9531x_dev *sitdev)\n+{\n+\tu32 rec_crc = 0, cal_crc = 0, prof_id = 0;\n+\tu8 notif, v;\n+\tint rc, i;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\t/* Profile id: three bytes, least significant first. */\n+\tfor (i = 2; i \u003e= 0; i--) {\n+\t\trc = sit9531x_read_u8(sitdev, SIT9531X_REG_PROFILE_ID + i, \u0026v);\n+\t\tif (rc)\n+\t\t\treturn;\n+\t\tprof_id = prof_id \u003c\u003c 8 | v;\n+\t}\n+\n+\tdev_info(sitdev-\u003edev, \"profile id %u\\n\", prof_id);\n+\n+\t/* Both CRCs: four bytes, most significant first. */\n+\tfor (i = 0; i \u003c 4; i++) {\n+\t\trc = sit9531x_read_u8(sitdev, SIT9531X_REG_REC_CRC + i, \u0026v);\n+\t\tif (rc)\n+\t\t\treturn;\n+\t\trec_crc = rec_crc \u003c\u003c 8 | v;\n+\n+\t\trc = sit9531x_read_u8(sitdev, SIT9531X_REG_CAL_CRC + i, \u0026v);\n+\t\tif (rc)\n+\t\t\treturn;\n+\t\tcal_crc = cal_crc \u003c\u003c 8 | v;\n+\t}\n+\n+\trc = sit9531x_read_u8(sitdev, SIT9531X_REG_EEPROM_NOTIF, \u0026notif);\n+\tif (rc)\n+\t\treturn;\n+\n+\t/*\n+\t * A clean load leaves the read-done bit set and every defect bit\n+\t * clear. Both CRCs zero means no EEPROM read happened at all:\n+\t * boards in this family may take their configuration over I2C\n+\t * instead of an EEPROM, and there the CRC pair means nothing, so\n+\t * that case is not a mismatch worth warning about.\n+\t */\n+\tif (!rec_crc \u0026\u0026 !cal_crc)\n+\t\tdev_dbg(sitdev-\u003edev,\n+\t\t\t\"no EEPROM profile (configuration pushed over I2C)\\n\");\n+\telse if (rec_crc != cal_crc)\n+\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t \"EEPROM CRC mismatch: stored %08x, computed %08x\\n\",\n+\t\t\t rec_crc, cal_crc);\n+\telse if (notif != SIT9531X_EEPROM_READ_DONE)\n+\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t \"EEPROM read reported defects (notify %02x)\\n\",\n+\t\t\t notif);\n+\telse\n+\t\tdev_dbg(sitdev-\u003edev, \"EEPROM profile loaded, CRC %08x\\n\",\n+\t\t\tcal_crc);\n+}\n+\n+/*\n+ * Report which PLLs came up, and flag the one case that is a real\n+ * inconsistency rather than a configuration choice.\n+ *\n+ * A PLL the loaded configuration leaves unused never reaches its active\n+ * state, which is normal and not worth a warning. A PLL that has\n+ * outputs routed to it and is still not active is different: something\n+ * that is meant to be generating clocks is not running, and every value\n+ * read from it -- lock state, phase, frequency offset -- describes a\n+ * stopped loop. Say so once at startup rather than leaving it to be\n+ * discovered through measurements that quietly read as zero.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+static void sit9531x_pll_states_report(struct sit9531x_dev *sitdev)\n+{\n+\tunsigned int idx, i;\n+\tbool routed;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tfor (i = 0; i \u003c SIT9531X_NUM_PLLS; i++) {\n+\t\tif (sitdev-\u003echan[i].active) {\n+\t\t\tdev_dbg(sitdev-\u003edev, \"PLL%c active\\n\", 'A' + i);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\trouted = false;\n+\t\tfor (idx = 0; idx \u003c sitdev-\u003einfo-\u003enum_outputs; idx++) {\n+\t\t\tconst struct sit9531x_out *out;\n+\n+\t\t\tout = sit9531x_out_state_get(sitdev, idx);\n+\t\t\tif (out-\u003erouted \u0026\u0026 out-\u003epll_idx == i) {\n+\t\t\t\trouted = true;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (routed)\n+\t\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t\t \"PLL%c drives outputs but is not in its active state\\n\",\n+\t\t\t\t 'A' + i);\n+\t\telse\n+\t\t\tdev_dbg(sitdev-\u003edev, \"PLL%c unused by the loaded configuration\\n\",\n+\t\t\t\t'A' + i);\n+\t}\n+}\n+\n+/*\n+ * sit9531x_input_pin_is_registrable - check if an input pin is registrable\n+ *\n+ * Split out so input-model changes stay local to this helper.\n+ *\n+ * Return: true if the input pin should be registered, false otherwise\n+ */\n+static bool sit9531x_input_pin_is_registrable(struct sit9531x_dev *sitdev,\n+\t\t\t\t\t u8 index)\n+{\n+\tif (index \u003e= sitdev-\u003einfo-\u003enum_inputs)\n+\t\treturn false;\n+\n+\t/*\n+\t * The N lane of a differentially-configured pair is not a\n+\t * standalone input and is skipped (zl3073x model).\n+\t */\n+\tif (sit9531x_input_is_n(index) \u0026\u0026\n+\t sitdev-\u003eref[index].sig_mode == SIT9531X_MODE_DE)\n+\t\treturn false;\n+\n+\treturn true;\n+}\n+\n+/*\n+ * Warn about a pin node in the firmware description whose reg names no\n+ * pin this device can have: an input lane or an output the variant does\n+ * not have, or the N lane of a pair the configuration runs differential.\n+ * Nodes are looked up from the pins (sit9531x_pin_props_get()), so such a\n+ * node would otherwise have its label and frequencies dropped without a\n+ * word. The binding bounds reg per variant; this catches what reaches\n+ * the driver unvalidated, and the pair mode, which only the loaded\n+ * configuration decides. An output that exists but that no PLL drives\n+ * gets no pin either and is not reported: which outputs a configuration\n+ * uses is not a fault in the description.\n+ */\n+static void sit9531x_pin_nodes_check(struct sit9531x_dev *sitdev)\n+{\n+\tstruct fwnode_handle *pins, *node;\n+\tbool found;\n+\tu32 reg;\n+\tu8 i;\n+\n+\tpins = device_get_named_child_node(sitdev-\u003edev, \"input-pins\");\n+\tfwnode_for_each_child_node(pins, node) {\n+\t\tif (fwnode_property_read_u32(node, \"reg\", \u0026reg))\n+\t\t\tcontinue;\n+\t\tif (reg \u003c sitdev-\u003einfo-\u003enum_inputs \u0026\u0026\n+\t\t sit9531x_input_pin_is_registrable(sitdev, reg))\n+\t\t\tcontinue;\n+\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t \"input-pins/%pfwP: reg %u is %s, node ignored\\n\",\n+\t\t\t node, reg,\n+\t\t\t reg \u003c sitdev-\u003einfo-\u003enum_inputs ?\n+\t\t\t \"the N lane of a differential pair\" :\n+\t\t\t \"not an input lane\");\n+\t}\n+\tfwnode_handle_put(pins);\n+\n+\tpins = device_get_named_child_node(sitdev-\u003edev, \"output-pins\");\n+\tfwnode_for_each_child_node(pins, node) {\n+\t\tif (fwnode_property_read_u32(node, \"reg\", \u0026reg))\n+\t\t\tcontinue;\n+\t\tfound = false;\n+\t\tfor (i = 0; i \u003c sitdev-\u003einfo-\u003enum_outputs; i++) {\n+\t\t\tif (sitdev-\u003einfo-\u003eclkout_map[i] == reg) {\n+\t\t\t\tfound = true;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tif (!found)\n+\t\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t\t \"output-pins/%pfwP: reg %u is not an output of %s, node ignored\\n\",\n+\t\t\t\t node, reg, sitdev-\u003einfo-\u003ename);\n+\t}\n+\tfwnode_handle_put(pins);\n+}\n+\n+/*\n+ * sit9531x_dev_start - start normal operation\n+ *\n+ * Fetches initial hardware state, registers all DPLL devices and\n+ * their pins, and starts the periodic monitoring thread.\n+ */\n+int sit9531x_dev_start(struct sit9531x_dev *sitdev)\n+{\n+\tstruct sit9531x_dpll *sitdpll;\n+\tint rc;\n+\n+\t/* Fetch device state */\n+\trc = sit9531x_dev_state_fetch(sitdev);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\tsit9531x_eeprom_state_report(sitdev);\n+\tsit9531x_manual_sel_report(sitdev);\n+\tsit9531x_pll_states_report(sitdev);\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\n+\tsit9531x_pin_nodes_check(sitdev);\n+\n+\tlist_for_each_entry(sitdpll, \u0026sitdev-\u003edplls, list) {\n+\t\trc = sit9531x_dpll_register(sitdpll);\n+\t\tif (rc) {\n+\t\t\tdev_err_probe(sitdev-\u003edev, rc,\n+\t\t\t\t \"Failed to register DPLL%u\\n\",\n+\t\t\t\t sitdpll-\u003eid);\n+\t\t\tgoto err_unregister;\n+\t\t}\n+\t}\n+\n+\tkthread_queue_delayed_work(sitdev-\u003ekworker, \u0026sitdev-\u003ework, 0);\n+\n+\treturn 0;\n+\n+err_unregister:\n+\t/*\n+\t * Unregister what did register. The caller frees the list on this\n+\t * path, so leaving a DPLL registered would hand the subsystem a\n+\t * pointer to freed memory.\n+\t */\n+\tlist_for_each_entry_continue_reverse(sitdpll, \u0026sitdev-\u003edplls, list)\n+\t\tsit9531x_dpll_unregister(sitdpll);\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * sit9531x_dev_stop - stop normal operation\n+ *\n+ * Cancels the monitoring thread and unregisters all DPLL devices\n+ * and their pins.\n+ */\n+void sit9531x_dev_stop(struct sit9531x_dev *sitdev)\n+{\n+\tstruct sit9531x_dpll *sitdpll;\n+\n+\tkthread_cancel_delayed_work_sync(\u0026sitdev-\u003ework);\n+\n+\tlist_for_each_entry(sitdpll, \u0026sitdev-\u003edplls, list) {\n+\t\tif (sitdpll-\u003edpll_dev)\n+\t\t\tsit9531x_dpll_unregister(sitdpll);\n+\t}\n+}\n+\n+static struct sit9531x_dpll_pin *\n+sit9531x_dpll_pin_alloc(struct sit9531x_dpll *sitdpll,\n+\t\t\tenum dpll_pin_direction dir, u8 id)\n+{\n+\tstruct sit9531x_dpll_pin *pin;\n+\n+\tpin = kzalloc_obj(*pin, GFP_KERNEL);\n+\tif (!pin)\n+\t\treturn ERR_PTR(-ENOMEM);\n+\n+\tpin-\u003edpll = sitdpll;\n+\tpin-\u003edir = dir;\n+\tpin-\u003eid = id;\n+\n+\treturn pin;\n+}\n+\n+/*\n+ * sit9531x_dpll_pin_register - register a DPLL pin with the subsystem\n+ * @index:\tabsolute pin index for clock_id namespace\n+ *\n+ * Gets pin properties from firmware, creates or gets a dpll_pin,\n+ * and registers it with the parent DPLL device.\n+ */\n+static int sit9531x_dpll_pin_register(struct sit9531x_dpll_pin *pin,\n+\t\t\t\t u32 index)\n+{\n+\tstruct sit9531x_dpll *sitdpll = pin-\u003edpll;\n+\tstruct sit9531x_pin_props *props;\n+\tconst struct dpll_pin_ops *ops;\n+\tint rc;\n+\n+\tprops = sit9531x_pin_props_get(sitdpll-\u003edev, pin-\u003edir, pin-\u003eid);\n+\tif (IS_ERR(props))\n+\t\treturn PTR_ERR(props);\n+\n+\tstrscpy(pin-\u003elabel, props-\u003epackage_label, sizeof(pin-\u003elabel));\n+\tpin-\u003efwnode = fwnode_handle_get(props-\u003efwnode);\n+\n+\tpin-\u003edpll_pin = dpll_pin_get(sitdpll-\u003edev-\u003eclock_id, index,\n+\t\t\t\t THIS_MODULE, \u0026props-\u003edpll_props,\n+\t\t\t\t \u0026pin-\u003etracker);\n+\tif (IS_ERR(pin-\u003edpll_pin)) {\n+\t\trc = PTR_ERR(pin-\u003edpll_pin);\n+\t\tgoto err_pin_get;\n+\t}\n+\tdpll_pin_fwnode_set(pin-\u003edpll_pin, props-\u003efwnode);\n+\n+\tops = sit9531x_dpll_pin_ops_get(pin);\n+\n+\trc = dpll_pin_register(sitdpll-\u003edpll_dev, pin-\u003edpll_pin, ops, pin);\n+\tif (rc)\n+\t\tgoto err_register;\n+\n+\tsit9531x_pin_props_put(props);\n+\n+\treturn 0;\n+\n+err_register:\n+\tdpll_pin_put(pin-\u003edpll_pin, \u0026pin-\u003etracker);\n+err_pin_get:\n+\t/* dpll_pin_get() left an ERR_PTR here. */\n+\tpin-\u003edpll_pin = NULL;\n+\tfwnode_handle_put(pin-\u003efwnode);\n+\tpin-\u003efwnode = NULL;\n+\tsit9531x_pin_props_put(props);\n+\n+\treturn rc;\n+}\n+\n+static void sit9531x_dpll_pin_unregister(struct sit9531x_dpll_pin *pin)\n+{\n+\tstruct sit9531x_dpll *sitdpll = pin-\u003edpll;\n+\tstruct dpll_pin *dpll_pin = pin-\u003edpll_pin;\n+\tconst struct dpll_pin_ops *ops;\n+\n+\tops = sit9531x_dpll_pin_ops_get(pin);\n+\n+\t/*\n+\t * Clear the pointer before the subsystem is told to drop the pin.\n+\t * A callback tests this field to tell a pin on its way out from a\n+\t * live one, and it runs under the subsystem's device lock, which\n+\t * dpll_pin_unregister() takes as well. Clearing first means such a\n+\t * callback either sees NULL and leaves the pin alone, or still sees\n+\t * the pin and finishes before the unregister can proceed. Clearing\n+\t * afterwards leaves a window where the field still reads as live\n+\t * and the pin behind it is already gone.\n+\t */\n+\tWRITE_ONCE(pin-\u003edpll_pin, NULL);\n+\n+\tdpll_pin_unregister(sitdpll-\u003edpll_dev, dpll_pin, ops, pin);\n+\tdpll_pin_put(dpll_pin, \u0026pin-\u003etracker);\n+\n+\tfwnode_handle_put(pin-\u003efwnode);\n+\tpin-\u003efwnode = NULL;\n+}\n+\n+static void sit9531x_dpll_pins_unregister(struct sit9531x_dpll *sitdpll)\n+{\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\tstruct sit9531x_dpll_pin *pin, *next;\n+\tLIST_HEAD(dead);\n+\n+\t/*\n+\t * Take the pins out of the subsystem first. That call takes the\n+\t * DPLL device lock, which the driver's own callbacks are already\n+\t * under when they take multiop_lock, so it must not run with\n+\t * multiop_lock held or the two orders cross.\n+\t */\n+\tlist_for_each_entry(pin, \u0026sitdpll-\u003epins, list)\n+\t\tsit9531x_dpll_pin_unregister(pin);\n+\n+\t/*\n+\t * Then detach the list under multiop_lock, which every callback that\n+\t * walks it holds, so once the splice returns no callback can reach\n+\t * these nodes. The poll walks the list as well without holding the\n+\t * lock throughout, but it is not running here: sit9531x_dev_stop()\n+\t * cancels it before unregistering, and the IRQ that could queue it\n+\t * again is released before that, its devm action having been added\n+\t * after the DPLLs were set up. On the registration error path the\n+\t * poll has not been queued yet. So the nodes are safe to free.\n+\t */\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\tlist_splice_init(\u0026sitdpll-\u003epins, \u0026dead);\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\n+\tlist_for_each_entry_safe(pin, next, \u0026dead, list) {\n+\t\tlist_del(\u0026pin-\u003elist);\n+\t\tkfree(pin);\n+\t}\n+}\n+\n+/*\n+ * sit9531x_dpll_pin_is_registrable - check if a pin should be registered\n+ * @dir:\tpin direction\n+ * @index:\tpin hardware index\n+ *\n+ * For input pins: delegate to sit9531x_input_pin_is_registrable().\n+ * For output pins: the pin is registrable if this DPLL is routed to it,\n+ * whether or not it is currently driving.\n+ *\n+ * Return: true if pin should be registered, false otherwise\n+ */\n+static bool sit9531x_dpll_pin_is_registrable(struct sit9531x_dpll *sitdpll,\n+\t\t\t\t\t enum dpll_pin_direction dir,\n+\t\t\t\t\t u8 index)\n+{\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\n+\tif (dir == DPLL_PIN_DIRECTION_INPUT) {\n+\t\tif (index == SIT9531X_MAX_INPUTS)\n+\t\t\treturn true;\n+\t\tif (index == SIT9531X_INTSYNC_PIN_ID)\n+\t\t\treturn true;\n+\n+\t\treturn sit9531x_input_pin_is_registrable(sitdev, index);\n+\t}\n+\n+\tif (index == SIT9531X_INTSYNC_OUT_PIN_ID)\n+\t\treturn true;\n+\n+\tif (index \u003e= sitdev-\u003einfo-\u003enum_outputs)\n+\t\treturn false;\n+\n+\treturn sitdev-\u003eout[index].pll_idx == sitdpll-\u003eid \u0026\u0026\n+\t sitdev-\u003eout[index].routed;\n+}\n+\n+/*\n+ * sit9531x_dpll_pins_register - register all registrable pins\n+ *\n+ * Enumerates all possible input and output pins, checks registrability,\n+ * and registers each one. Input pins come first, then output pins,\n+ * with input pins first, then output pins.\n+ */\n+static int sit9531x_dpll_pins_register(struct sit9531x_dpll *sitdpll)\n+{\n+\tstruct sit9531x_dpll_pin *pin;\n+\tenum dpll_pin_direction dir;\n+\tu8 id, index;\n+\tint rc;\n+\n+\tfor (index = 0; index \u003c SIT9531X_NUM_PINS_TOTAL; index++) {\n+\t\tif (index \u003c SIT9531X_NUM_INPUT_PINS) {\n+\t\t\tid = index;\n+\t\t\tdir = DPLL_PIN_DIRECTION_INPUT;\n+\t\t} else {\n+\t\t\tid = index - SIT9531X_NUM_INPUT_PINS;\n+\t\t\tdir = DPLL_PIN_DIRECTION_OUTPUT;\n+\t\t}\n+\n+\t\tif (!sit9531x_dpll_pin_is_registrable(sitdpll, dir, id))\n+\t\t\tcontinue;\n+\n+\t\tpin = sit9531x_dpll_pin_alloc(sitdpll, dir, id);\n+\t\tif (IS_ERR(pin)) {\n+\t\t\trc = PTR_ERR(pin);\n+\t\t\tgoto error;\n+\t\t}\n+\n+\t\trc = sit9531x_dpll_pin_register(pin, index);\n+\t\tif (rc) {\n+\t\t\tkfree(pin);\n+\t\t\tgoto error;\n+\t\t}\n+\n+\t\t/*\n+\t\t * Pins registered before this one are already reachable from\n+\t\t * netlink, and the walks over this list run under\n+\t\t * multiop_lock, so the insertion does too. The baseline the\n+\t\t * poll compares the pin against is taken under the same\n+\t\t * lock, from the cache the create notification just reported.\n+\t\t */\n+\t\tmutex_lock(\u0026sitdpll-\u003edev-\u003emultiop_lock);\n+\t\tsit9531x_dpll_pin_baseline(pin);\n+\t\tlist_add(\u0026pin-\u003elist, \u0026sitdpll-\u003epins);\n+\t\tmutex_unlock(\u0026sitdpll-\u003edev-\u003emultiop_lock);\n+\t}\n+\n+\treturn 0;\n+\n+error:\n+\tsit9531x_dpll_pins_unregister(sitdpll);\n+\treturn rc;\n+}\n+\n+static int sit9531x_dpll_device_register(struct sit9531x_dpll *sitdpll)\n+{\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\tint rc;\n+\n+\tsitdpll-\u003eops = sit9531x_dpll_device_ops;\n+\n+\tsitdpll-\u003edpll_dev = dpll_device_get(sitdev-\u003eclock_id, sitdpll-\u003eid,\n+\t\t\t\t\t THIS_MODULE, \u0026sitdpll-\u003etracker);\n+\tif (IS_ERR(sitdpll-\u003edpll_dev)) {\n+\t\trc = PTR_ERR(sitdpll-\u003edpll_dev);\n+\t\tsitdpll-\u003edpll_dev = NULL;\n+\t\treturn rc;\n+\t}\n+\n+\trc = dpll_device_register(sitdpll-\u003edpll_dev,\n+\t\t\t\t sit9531x_prop_dpll_type_get(sitdev,\n+\t\t\t\t\t\t\t sitdpll-\u003eid),\n+\t\t\t\t \u0026sitdpll-\u003eops, sitdpll);\n+\tif (rc) {\n+\t\tdpll_device_put(sitdpll-\u003edpll_dev, \u0026sitdpll-\u003etracker);\n+\t\tsitdpll-\u003edpll_dev = NULL;\n+\t}\n+\n+\treturn rc;\n+}\n+\n+static void sit9531x_dpll_device_unregister(struct sit9531x_dpll *sitdpll)\n+{\n+\tdpll_device_unregister(sitdpll-\u003edpll_dev, \u0026sitdpll-\u003eops, sitdpll);\n+\tdpll_device_put(sitdpll-\u003edpll_dev, \u0026sitdpll-\u003etracker);\n+\tsitdpll-\u003edpll_dev = NULL;\n+}\n+\n+/*\n+ * sit9531x_dpll_alloc - allocate a DPLL device structure\n+ * @sitdev:\tparent device\n+ * @ch:\t\tPLL channel number (0-3)\n+ *\n+ * Return: pointer to allocated DPLL on success, error pointer on error\n+ */\n+struct sit9531x_dpll *sit9531x_dpll_alloc(struct sit9531x_dev *sitdev, u8 ch)\n+{\n+\tstruct sit9531x_dpll *sitdpll;\n+\n+\tsitdpll = kzalloc_obj(*sitdpll, GFP_KERNEL);\n+\tif (!sitdpll)\n+\t\treturn ERR_PTR(-ENOMEM);\n+\n+\tsitdpll-\u003edev = sitdev;\n+\tsitdpll-\u003eid = ch;\n+\tsitdpll-\u003elock_status = DPLL_LOCK_STATUS_UNLOCKED;\n+\tsitdpll-\u003elock_status_error = DPLL_LOCK_STATUS_ERROR_NONE;\n+\tINIT_LIST_HEAD(\u0026sitdpll-\u003epins);\n+\n+\treturn sitdpll;\n+}\n+\n+/*\n+ * sit9531x_dpll_free - deallocate a DPLL device structure\n+ * @sitdpll:\tDPLL to free\n+ */\n+void sit9531x_dpll_free(struct sit9531x_dpll *sitdpll)\n+{\n+\tkfree(sitdpll);\n+}\n+\n+/*\n+ * sit9531x_dpll_register - register DPLL device and all its pins\n+ *\n+ * Registers the DPLL device with the subsystem and then registers\n+ * all input and output pins that are connected to this PLL.\n+ */\n+int sit9531x_dpll_register(struct sit9531x_dpll *sitdpll)\n+{\n+\tint rc;\n+\n+\trc = sit9531x_dpll_device_register(sitdpll);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tsit9531x_dpll_lock_status_baseline(sitdpll);\n+\n+\trc = sit9531x_dpll_pins_register(sitdpll);\n+\tif (rc) {\n+\t\tsit9531x_dpll_device_unregister(sitdpll);\n+\t\treturn rc;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/* sit9531x_dpll_unregister - unregister DPLL device and its pins */\n+void sit9531x_dpll_unregister(struct sit9531x_dpll *sitdpll)\n+{\n+\tsit9531x_dpll_pins_unregister(sitdpll);\n+\tsit9531x_dpll_device_unregister(sitdpll);\n+}\n+\n+static void sit9531x_dpll_list_free(struct sit9531x_dev *sitdev)\n+{\n+\tstruct sit9531x_dpll *sitdpll, *next;\n+\n+\tlist_for_each_entry_safe(sitdpll, next, \u0026sitdev-\u003edplls, list) {\n+\t\tlist_del(\u0026sitdpll-\u003elist);\n+\t\tsit9531x_dpll_free(sitdpll);\n+\t}\n+}\n+\n+/* Runs only once the device is fully started, see the caller. */\n+static void sit9531x_dev_dpll_fini(void *ptr)\n+{\n+\tstruct sit9531x_dev *sitdev = ptr;\n+\n+\tsit9531x_dev_stop(sitdev);\n+\tkthread_destroy_worker(sitdev-\u003ekworker);\n+\tsit9531x_dpll_list_free(sitdev);\n+}\n+\n+static int sit9531x_devm_dpll_init(struct sit9531x_dev *sitdev)\n+{\n+\tstruct kthread_worker *kworker;\n+\tstruct sit9531x_dpll *sitdpll;\n+\tunsigned int i;\n+\tint rc;\n+\n+\tINIT_LIST_HEAD(\u0026sitdev-\u003edplls);\n+\tkthread_init_delayed_work(\u0026sitdev-\u003ework, sit9531x_dev_periodic_work);\n+\n+\tfor (i = 0; i \u003c SIT9531X_NUM_PLLS; i++) {\n+\t\tsitdpll = sit9531x_dpll_alloc(sitdev, i);\n+\t\tif (IS_ERR(sitdpll)) {\n+\t\t\trc = dev_err_probe(sitdev-\u003edev, PTR_ERR(sitdpll),\n+\t\t\t\t\t \"Failed to alloc DPLL%u\\n\", i);\n+\t\t\tgoto err_free_dplls;\n+\t\t}\n+\n+\t\tlist_add_tail(\u0026sitdpll-\u003elist, \u0026sitdev-\u003edplls);\n+\t}\n+\n+\tkworker = kthread_run_worker(0, \"sit9531x-%s\", dev_name(sitdev-\u003edev));\n+\tif (IS_ERR(kworker)) {\n+\t\trc = PTR_ERR(kworker);\n+\t\tgoto err_free_dplls;\n+\t}\n+\tsitdev-\u003ekworker = kworker;\n+\n+\trc = sit9531x_dev_start(sitdev);\n+\tif (rc) {\n+\t\trc = dev_err_probe(sitdev-\u003edev, rc, \"Failed to start device\\n\");\n+\t\tgoto err_destroy_worker;\n+\t}\n+\n+\t/*\n+\t * Only now is every field the cleanup touches valid, so this is the\n+\t * first point at which the action may be registered. On failure it\n+\t * runs the action itself, which is correct here and only here.\n+\t */\n+\treturn devm_add_action_or_reset(sitdev-\u003edev, sit9531x_dev_dpll_fini,\n+\t\t\t\t\tsitdev);\n+\n+err_destroy_worker:\n+\tkthread_destroy_worker(sitdev-\u003ekworker);\n+err_free_dplls:\n+\tsit9531x_dpll_list_free(sitdev);\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * sit9531x_read_variant_id - read chip variant ID byte from hardware\n+ * @id:\t\toutput variant ID byte\n+ *\n+ * Reads the single-byte variant identification register from Page 0\n+ * reg 0x02 (95317 = 0x17, 95316 = 0x31). Reg 0x03 holds a separate\n+ * revision byte and is intentionally not consumed here.\n+ */\n+static int sit9531x_read_variant_id(struct sit9531x_dev *sitdev, u8 *id)\n+{\n+\treturn sit9531x_read_u8(sitdev, SIT9531X_REG_VARIANT_ID, id);\n+}\n+\n+static const struct sit9531x_chip_info *sit9531x_match_variant(u8 id)\n+{\n+\tunsigned int i;\n+\n+\tfor (i = 0; i \u003c ARRAY_SIZE(sit9531x_chip_ids); i++) {\n+\t\tif (sit9531x_chip_ids[i].id == id)\n+\t\t\treturn \u0026sit9531x_chip_ids[i];\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+/*\n+ * sit9531x_derive_clock_id - build EUI-64 clock identifier\n+ *\n+ * Generates a deterministic 64-bit identifier from the SiTime OUI,\n+ * the I2C bus number and the I2C address. The bus number\n+ * disambiguates two same-variant parts at the same address on\n+ * different adapters (or behind a mux), which the DPLL core would\n+ * otherwise fold onto one set of objects; DT bus aliases keep the\n+ * numbering, and with it the clock_id, stable across reboots.\n+ *\n+ * Return: 64-bit clock identifier\n+ */\n+static u64 sit9531x_derive_clock_id(struct sit9531x_dev *sitdev)\n+{\n+\tu64 clkid;\n+\tint adap;\n+\n+\tadap = i2c_adapter_id(sitdev-\u003eclient-\u003eadapter);\n+\n+\t/*\n+\t * The OUI with its EUI-64 filler takes the top 40 bits. Bus numbers\n+\t * are not bounded by the number of adapters -- a DT alias or a mux\n+\t * child can number a bus 256 or above on a board with a handful of\n+\t * them -- so the bus gets 16 bits. The variant does not need a\n+\t * field: one address on one bus holds one part.\n+\t */\n+\tclkid = SIT9531X_OUI \u003c\u003c 24;\n+\tclkid |= (u64)(adap \u0026 0xffff) \u003c\u003c 8;\n+\tclkid |= (u64)sitdev-\u003eclient-\u003eaddr;\n+\n+\treturn clkid;\n+}\n+\n+int sit9531x_dev_probe(struct sit9531x_dev *sitdev)\n+{\n+\tstruct clk *xtal_clk;\n+\tu8 variant_id;\n+\tint rc;\n+\n+\t/*\n+\t * Fvco = Fref * (DIVN + frac/2^32) with Fref = xtal_freq \u003c\u003c doubler,\n+\t * so every freq_set and phase_adjust path divides by a rate derived\n+\t * from the XO feeding XIN/XO_CLK. The rate normally comes from a\n+\t * \"clocks\" phandle (clock-names = \"xtal\"). On platforms where the\n+\t * firmware does not expose the XO through the clock framework, fall\n+\t * back to a \"clock-frequency\" device property.\n+\t */\n+\txtal_clk = devm_clk_get_optional_enabled(sitdev-\u003edev, \"xtal\");\n+\tif (IS_ERR(xtal_clk))\n+\t\treturn dev_err_probe(sitdev-\u003edev, PTR_ERR(xtal_clk),\n+\t\t\t\t \"Failed to get xtal clock\\n\");\n+\tsitdev-\u003extal_freq = clk_get_rate(xtal_clk);\n+\tif (!sitdev-\u003extal_freq) {\n+\t\tu32 freq;\n+\n+\t\tif (!device_property_read_u32(sitdev-\u003edev, \"clock-frequency\",\n+\t\t\t\t\t \u0026freq))\n+\t\t\tsitdev-\u003extal_freq = freq;\n+\t}\n+\tif (!sitdev-\u003extal_freq)\n+\t\treturn dev_err_probe(sitdev-\u003edev, -EINVAL,\n+\t\t\t\t \"no xtal rate: provide clocks=\u003c\u0026xo\u003e + clock-names=\\\"xtal\\\", or a clock-frequency property\\n\");\n+\n+\t/*\n+\t * Held deasserted, never pulsed: the chip configuration comes from\n+\t * efuse or an NVM blob applied before probe, and a reset would\n+\t * discard it. Must precede the first I2C access, as a board that\n+\t * powers up asserted keeps the chip unreachable until released.\n+\t */\n+\tsitdev-\u003ereset_gpio = devm_gpiod_get_optional(sitdev-\u003edev, \"reset\",\n+\t\t\t\t\t\t GPIOD_OUT_LOW);\n+\tif (IS_ERR(sitdev-\u003ereset_gpio))\n+\t\treturn dev_err_probe(sitdev-\u003edev, PTR_ERR(sitdev-\u003ereset_gpio),\n+\t\t\t\t \"Failed to request reset gpio\\n\");\n+\tif (sitdev-\u003ereset_gpio)\n+\t\tfsleep(10000);\t/* internal boot after release */\n+\n+\trc = sit9531x_read_variant_id(sitdev, \u0026variant_id);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tsitdev-\u003einfo = sit9531x_match_variant(variant_id);\n+\tif (!sitdev-\u003einfo)\n+\t\treturn dev_err_probe(sitdev-\u003edev, -ENODEV,\n+\t\t\t\t \"Unknown variant ID: 0x%02x\\n\",\n+\t\t\t\t variant_id);\n+\n+\tsitdev-\u003eclock_id = sit9531x_derive_clock_id(sitdev);\n+\tsitdev-\u003eintsync_src = -1;\n+\n+\trc = devm_mutex_init(sitdev-\u003edev, \u0026sitdev-\u003emultiop_lock);\n+\tif (rc)\n+\t\treturn dev_err_probe(sitdev-\u003edev, rc,\n+\t\t\t\t \"Failed to initialize mutex\\n\");\n+\n+\tdev_info(sitdev-\u003edev, \"%s detected, %u inputs, %u outputs\\n\",\n+\t\t sitdev-\u003einfo-\u003ename, sitdev-\u003einfo-\u003enum_inputs,\n+\t\t sitdev-\u003einfo-\u003enum_outputs);\n+\n+\t/*\n+\t * Before the IRQ: the handler reaches sitdev-\u003ekworker through\n+\t * kthread_mod_delayed_work(), so the worker has to exist before an\n+\t * INTRB assertion can land.\n+\t */\n+\trc = sit9531x_devm_dpll_init(sitdev);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\t/* Absent \"interrupts\" leaves client-\u003eirq 0 and the poll in charge. */\n+\tsitdev-\u003eirq = sitdev-\u003eclient ? sitdev-\u003eclient-\u003eirq : 0;\n+\tif (sitdev-\u003eirq \u003e 0) {\n+\t\trc = devm_request_threaded_irq(sitdev-\u003edev, sitdev-\u003eirq,\n+\t\t\t\t\t NULL, sit9531x_irq_thread_fn,\n+\t\t\t\t\t IRQF_ONESHOT,\n+\t\t\t\t\t dev_name(sitdev-\u003edev), sitdev);\n+\t\tif (rc)\n+\t\t\treturn dev_err_probe(sitdev-\u003edev, rc,\n+\t\t\t\t\t \"Failed to request IRQ %d\\n\",\n+\t\t\t\t\t sitdev-\u003eirq);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int sit9531x_i2c_probe(struct i2c_client *client)\n+{\n+\tstruct sit9531x_dev *sitdev;\n+\tstruct regmap *regmap;\n+\n+\tregmap = devm_regmap_init_i2c(client, \u0026sit9531x_regmap_config);\n+\tif (IS_ERR(regmap))\n+\t\treturn dev_err_probe(\u0026client-\u003edev, PTR_ERR(regmap),\n+\t\t\t\t \"Failed to initialize regmap\\n\");\n+\n+\tsitdev = devm_kzalloc(\u0026client-\u003edev, sizeof(*sitdev), GFP_KERNEL);\n+\tif (!sitdev)\n+\t\treturn -ENOMEM;\n+\n+\tsitdev-\u003edev = \u0026client-\u003edev;\n+\tsitdev-\u003eclient = client;\n+\tsitdev-\u003eregmap = regmap;\n+\ti2c_set_clientdata(client, sitdev);\n+\n+\treturn sit9531x_dev_probe(sitdev);\n+}\n+\n+static const struct of_device_id sit9531x_of_match[] = {\n+\t{ .compatible = \"sitime,sit95316\" },\n+\t{ .compatible = \"sitime,sit95317\" },\n+\t{ }\n+};\n+MODULE_DEVICE_TABLE(of, sit9531x_of_match);\n+\n+/*\n+ * The poll worker is not freezable and would keep issuing paged I2C\n+ * transfers into a suspended adapter, where i2c_transfer() fails and a\n+ * tick landing mid-suspend could tear a paged sequence between the\n+ * page-selector write and the register access. Park the worker (and\n+ * the IRQ that kicks it) across suspend and take a fresh sample on\n+ * resume.\n+ */\n+static int sit9531x_suspend(struct device *dev)\n+{\n+\tstruct sit9531x_dev *sitdev = dev_get_drvdata(dev);\n+\n+\tif (sitdev-\u003eirq \u003e 0)\n+\t\tdisable_irq(sitdev-\u003eirq);\n+\tkthread_cancel_delayed_work_sync(\u0026sitdev-\u003ework);\n+\n+\treturn 0;\n+}\n+\n+static int sit9531x_resume(struct device *dev)\n+{\n+\tstruct sit9531x_dev *sitdev = dev_get_drvdata(dev);\n+\tint rc;\n+\tu8 i;\n+\n+\tsit9531x_page_cache_drop(sitdev);\n+\n+\t/*\n+\t * A part that lost power across suspend comes back with its\n+\t * profile's INTSYNC routing and output mutes, not with what the\n+\t * caches say, and the poll watches neither. Read them back here;\n+\t * a cache that cannot be read is left for the getter's read-through.\n+\t * Nothing races this: the poll is parked and the IRQ disabled.\n+\t */\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\trc = sit9531x_intsync_src_detect(sitdev);\n+\tif (rc)\n+\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t \"INTSYNC source not re-detected on resume: %d\\n\", rc);\n+\tfor (i = 0; i \u003c sitdev-\u003einfo-\u003enum_outputs; i++) {\n+\t\trc = sit9531x_output_state_refresh(sitdev, i);\n+\t\tif (rc) {\n+\t\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t\t \"Output %u mute state not read on resume: %d\\n\",\n+\t\t\t\t i, rc);\n+\t\t\tsitdev-\u003eout[i].state_stale = true;\n+\t\t}\n+\t}\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (sitdev-\u003eirq \u003e 0)\n+\t\tenable_irq(sitdev-\u003eirq);\n+\tkthread_queue_delayed_work(sitdev-\u003ekworker, \u0026sitdev-\u003ework, 0);\n+\n+\treturn 0;\n+}\n+\n+static DEFINE_SIMPLE_DEV_PM_OPS(sit9531x_pm_ops,\n+\t\t\t\tsit9531x_suspend, sit9531x_resume);\n+\n+static struct i2c_driver sit9531x_i2c_driver = {\n+\t.driver\t\t\t= {\n+\t\t.name\t\t= \"sit9531x\",\n+\t\t.of_match_table\t= sit9531x_of_match,\n+\t\t.pm\t\t= pm_sleep_ptr(\u0026sit9531x_pm_ops),\n+\t},\n+\t.probe\t\t= sit9531x_i2c_probe,\n+};\n+module_i2c_driver(sit9531x_i2c_driver);\n+\n+MODULE_AUTHOR(\"Ali Rouhi \u003carouhi@sitime.com\u003e\");\n+MODULE_AUTHOR(\"Oleg Zadorozhnyi \u003cOleg.Zadorozhnyi@devoxsoftware.com\u003e\");\n+MODULE_DESCRIPTION(\"SiTime SiT9531x DPLL subsystem driver\");\n+MODULE_LICENSE(\"GPL\");\ndiff --git a/drivers/dpll/sit9531x/core.h b/drivers/dpll/sit9531x/core.h\nnew file mode 100644\nindex 0000000000000..a5b343033a415\n--- /dev/null\n+++ b/drivers/dpll/sit9531x/core.h\n@@ -0,0 +1,453 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/*\n+ * SiTime SiT9531x DPLL core driver\n+ *\n+ * Copyright (C) 2026 SiTime Corp.\n+ * Author: Ali Rouhi \u003carouhi@sitime.com\u003e\n+ * Author: Oleg Zadorozhnyi \u003cOleg.Zadorozhnyi@devoxsoftware.com\u003e\n+ *\n+ * Device structure, register access helpers, and core function\n+ * declarations.\n+ */\n+\n+#ifndef _SIT9531X_CORE_H\n+#define _SIT9531X_CORE_H\n+\n+#include \u003clinux/gpio/consumer.h\u003e\n+#include \u003clinux/i2c.h\u003e\n+#include \u003clinux/kthread.h\u003e\n+#include \u003clinux/list.h\u003e\n+#include \u003clinux/mutex.h\u003e\n+#include \u003clinux/regmap.h\u003e\n+#include \u003clinux/types.h\u003e\n+\n+#include \"regs.h\"\n+\n+#define SIT9531X_NUM_PLLS\t\t4\n+#define SIT9531X_MAX_INPUTS\t\t8\n+#define SIT9531X_NUM_INPUT_PAIRS\t(SIT9531X_MAX_INPUTS / 2)\n+#define SIT9531X_MAX_OUTPUTS\t\t12\n+/* Output phase-adjust range advertised to the core, +/-1 ms in ps */\n+#define SIT9531X_OUT_PHASE_ADJ_MAX_PS\t1000000000\n+/*\n+ * INTSYNC (the inter-PLL sync net) is modeled as two pins. The\n+ * destination PLL that locks to INTSYNC sees an input pin\n+ * (SIT9531X_INTSYNC_PIN_ID, in the input id namespace after the physical\n+ * inputs and the xtal); the source PLL that drives INTSYNC sees an output\n+ * pin (SIT9531X_INTSYNC_OUT_PIN_ID, appended after the physical outputs).\n+ */\n+#define SIT9531X_INTSYNC_PIN_ID\t\t(SIT9531X_MAX_INPUTS + 1)\n+#define SIT9531X_INTSYNC_OUT_PIN_ID\tSIT9531X_MAX_OUTPUTS\n+#define SIT9531X_STATUS_POLL_MS\t\t500\n+\n+/* routed_ref value when the PLL is fed no registered input */\n+#define SIT9531X_REF_INVALID\t\t0xFF\n+\n+/* SiTime IEEE OUI for EUI-64 generation */\n+#define SIT9531X_OUI\t\t\t0x0090C2FFFEULL\n+\n+struct sit9531x_dpll;\n+\n+/*\n+ * struct sit9531x_chip_info - chip variant identification\n+ * @id:\t\tvariant ID byte read from register\n+ * @num_inputs:\tnumber of input clock pins\n+ * @num_outputs: number of output clock pins\n+ * @name:\thuman-readable variant name\n+ * @clkout_map:\tper-output slot mapping (output index -\u003e physical slot)\n+ */\n+struct sit9531x_chip_info {\n+\tu8\t\tid;\n+\tu8\t\tnum_inputs;\n+\tu8\t\tnum_outputs;\n+\tconst char\t*name;\n+\tconst u8\t*clkout_map;\n+};\n+\n+/*\n+ * enum sit9531x_signal_mode - input signal electrical mode\n+ * @SIT9531X_MODE_SE: single-ended\n+ * @SIT9531X_MODE_DE: differential\n+ */\n+enum sit9531x_signal_mode {\n+\tSIT9531X_MODE_SE = 0,\n+\tSIT9531X_MODE_DE,\n+};\n+\n+/*\n+ * struct sit9531x_ref - input reference state\n+ * @freq:\t\tconfigured frequency in Hz\n+ * @enabled:\t\tthe lane's receiver is on\n+ * @pll_mask:\t\tbitmask of PLLs this input feeds (bit 0 = PLLA)\n+ * @los:\t\tthe clock monitor reports loss of signal on the lane\n+ * @qual_fail:\t\tthe clock monitor reports a frequency drift on the\n+ *\t\t\tlane while it still has signal\n+ * @sig_mode:\t\tsignal mode of the pair this lane belongs to\n+ *\t\t\t(detected from CLKINx_INPUT_MODE at probe)\n+ */\n+struct sit9531x_ref {\n+\tu64\t\tfreq;\n+\tbool\t\tenabled;\n+\tu8\t\tpll_mask;\n+\tbool\t\tlos;\n+\tbool\t\tqual_fail;\n+\tenum sit9531x_signal_mode\tsig_mode;\n+};\n+\n+/*\n+ * struct sit9531x_out - output state\n+ * @freq:\t\trate the output is running at, in Hz\n+ * @enabled:\t\toutput is driving, i.e. not forced into Hi-Z\n+ * @cmos:\t\toutput is wired single-ended; the Hi-Z pairs that\n+ *\t\t\tspeak for it are the two CMOS pad ones, not the\n+ *\t\t\tdifferential\n+ * @state_stale:\tthe cached mute state could not be confirmed against\n+ *\t\t\thardware and has to be read back before it is reported\n+ * @routed:\t\toutput is mapped to @pll_idx by the initial\n+ *\t\t\tconfiguration; an unrouted output has no DPLL pin\n+ * @pll_idx:\t\tPLL driving this output (0-3)\n+ * @phase_stale:\tthe programmed delay may differ from @phase_adj\n+ * @flush_pending:\tthe programmed delay is committed but the flush\n+ *\t\t\tthat applies it failed, so the output still realizes\n+ *\t\t\t@phase_adj; the next request for that delay runs the\n+ *\t\t\tflush although the registers already hold it\n+ * @phase_armed:\ta non-zero delay is programmed, so a rate change\n+ *\t\t\thas to re-time it; a profile delay beyond the\n+ *\t\t\tadvertised range is not, since what the cache holds\n+ *\t\t\tfor it is the end of the range, not the delay\n+ * @phase_adj:\t\tphase adjust the delay registers actually realize,\n+ *\t\t\ti.e. the last request quantized to whole VCO cycles\n+ *\t\t\tplus 30 ps fine steps, in the request's sign\n+ */\n+struct sit9531x_out {\n+\tu64\t\tfreq;\n+\tbool\t\tenabled;\n+\tbool\t\tcmos;\n+\tbool\t\tstate_stale;\n+\tbool\t\trouted;\n+\tu8\t\tpll_idx;\n+\ts32\t\tphase_adj;\n+\tbool\t\tphase_armed;\n+\tbool\t\tphase_stale;\n+\tbool\t\tflush_pending;\n+};\n+\n+/*\n+ * struct sit9531x_chan - per-PLL channel state\n+ * @active:\t\tPLL has reached its active state; a PLL the loaded\n+ *\t\t\tconfiguration leaves unused never does, and its\n+ *\t\t\tloss-of-lock bit stays clear because nothing drives it\n+ * @locked:\t\tPLL is active and its outer loop reports lock\n+ * @mode:\t\t0 = sync (outer loop enabled), 1 = free-run\n+ * @routed_ref:\t\tlogical input index of the reference the device\n+ *\t\t\tfeeds the PLL while it tracks one (the INTSYNC net\n+ *\t\t\tmaps to SIT9531X_INTSYNC_PIN_ID); SIT9531X_REF_INVALID\n+ *\t\t\twhen the PLL tracks nothing -- free-run, frozen,\n+ *\t\t\tunlocked -- or the source code names no registered\n+ *\t\t\tinput\n+ * @inner_lol:\t\tPLL inner loop loss-of-lock detected\n+ * @ho_freeze:\t\tholdover freeze active\n+ * @ho_valid:\t\tholdover memory acquired, i.e. the holdover window\n+ *\t\t\tholds a valid estimate to fall back on\n+ * @ho_release_pending:\ta table write forced holdover and could not release\n+ *\t\t\tit; the poll retries the release until it lands\n+ * @prio_srcs:\t\tcached copy of the priority table, one source code\n+ *\t\t\tper slot; refreshed together with @prio_mask, it is\n+ *\t\t\twhat a rebuilt table is compared against\n+ * @prio_last:\t\tfirst slot each source occupies, plus one (0 = the\n+ *\t\t\tsource is not in the table); refreshed from the same\n+ *\t\t\tscan as @prio_mask, so the two never disagree\n+ * @prio_mask:\t\tbit per canonical source present in this PLL's\n+ *\t\t\tpriority table, i.e. the sources it may select. Read\n+ *\t\t\tback from the table by the periodic worker and\n+ *\t\t\trefreshed by every table write, so it tracks the\n+ *\t\t\thardware rather than the driver's intent\n+ * @cfg_prio:\t\tpriority configured for each canonical source,\n+ *\t\t\twhether or not it is in the table; the table is\n+ *\t\t\tbuilt from it, so taking a source out and putting it\n+ *\t\t\tback does not change anyone's priority\n+ * @cfg_known:\t\tbit per canonical source that has a @cfg_prio\n+ * @seen_srcs:\t\tthe table as the driver last wrote or seeded\n+ *\t\t\t@cfg_prio from; a read-back that differs means\n+ *\t\t\tsomething else rewrote it, and @cfg_prio is\n+ *\t\t\tre-seeded from the hardware\n+ * @seen_valid:\t\t@seen_srcs holds a table\n+ */\n+struct sit9531x_chan {\n+\tbool\t\tactive;\n+\tbool\t\tlocked;\n+\tu8\t\tmode;\n+\tu8\t\trouted_ref;\n+\tbool\t\tinner_lol;\n+\tbool\t\tho_freeze;\n+\tbool\t\tho_valid;\n+\tbool\t\tho_release_pending;\n+\tu8\t\tprio_srcs[SIT9531X_PRIO_MAX_SLOTS];\n+\tu8\t\tprio_last[SIT9531X_PRIO_NUM_SRC];\n+\tu16\t\tprio_mask;\n+\tu8\t\tcfg_prio[SIT9531X_PRIO_NUM_SRC];\n+\tu16\t\tcfg_known;\n+\tu8\t\tseen_srcs[SIT9531X_PRIO_MAX_SLOTS];\n+\tbool\t\tseen_valid;\n+};\n+\n+/*\n+ * struct sit9531x_dev - SiT9531x device instance\n+ * @info:\t\tdetected chip variant info\n+ * @dev:\t\tparent device\n+ * @client:\t\tI2C client backing @regmap\n+ * @regmap:\t\tpaged register map of the device\n+ * @dplls:\t\tDPLL devices registered for this chip\n+ * @multiop_lock:\tmutex for multi-register atomic operations\n+ * @ref:\t\tarray of input reference states\n+ * @out:\t\tarray of output states\n+ * @chan:\t\tarray of per-PLL channel states\n+ * @xtal_freq:\t\tcrystal oscillator frequency in Hz\n+ * @kworker:\t\tkthread worker for periodic polling\n+ * @work:\t\tdelayed work for periodic state checks\n+ * @clock_id:\t\tIEEE 1588 EUI-64 clock identifier\n+ * @reset_gpio:\t\toptional reset line (DT \"reset-gpios\"), NULL if absent\n+ * @irq:\t\toptional INTRB IRQ number (from DT \"interrupts\" via the\n+ *\t\t\tI2C client), 0 if no IRQ is wired\n+ * @intsync_src:\tPLL index currently sourcing inter-PLL\n+ *\t\t\tsynchronization (INTSYNC), or -1 when disabled\n+ * @intsync_partial:\t@intsync_src holds only part of the source\n+ *\t\t\tconfiguration, left by an enable or a disable\n+ *\t\t\tthat stopped part way\n+ * @irq_ack_fails:\tconsecutive failures to acknowledge the\n+ *\t\t\tnotification latches from the interrupt handler\n+ * @fvco_band_warned:\tbit per PLL whose Fref * DIVN has been reported as\n+ *\t\t\toutside its VCO band; written under @multiop_lock\n+ */\n+struct sit9531x_dev {\n+\tstruct device\t\t\t*dev;\n+\tstruct i2c_client\t\t*client;\n+\tstruct regmap\t\t\t*regmap;\n+\tconst struct sit9531x_chip_info\t*info;\n+\t/* Serializes multi-step register sequences */\n+\tstruct mutex\t\t\tmultiop_lock;\n+\n+\t/* Hardware state */\n+\tu8\t\t\tirq_ack_fails;\n+\tu8\t\t\tfvco_band_warned;\n+\tstruct sit9531x_ref\tref[SIT9531X_MAX_INPUTS + 1]; /* +1 for xtal */\n+\tstruct sit9531x_out\tout[SIT9531X_MAX_OUTPUTS];\n+\tstruct sit9531x_chan\tchan[SIT9531X_NUM_PLLS];\n+\tu32\t\t\txtal_freq;\n+\n+\t/* DPLL channels */\n+\tstruct list_head\tdplls;\n+\n+\t/* Monitor */\n+\tstruct kthread_worker\t\t*kworker;\n+\tstruct kthread_delayed_work\twork;\n+\n+\t/* Device identity */\n+\tu64\t\t\tclock_id;\n+\n+\t/* Optional DT-described GPIO / IRQ lines */\n+\tstruct gpio_desc\t*reset_gpio;\n+\tint\t\t\tirq;\n+\n+\t/* Inter-PLL synchronization state */\n+\ts8\t\t\tintsync_src;\n+\tbool\t\t\tintsync_partial;\n+\n+};\n+\n+extern const struct regmap_config sit9531x_regmap_config;\n+\n+/* ---- Core lifecycle ---- */\n+int sit9531x_dev_probe(struct sit9531x_dev *sitdev);\n+int sit9531x_dev_start(struct sit9531x_dev *sitdev);\n+void sit9531x_dev_stop(struct sit9531x_dev *sitdev);\n+\n+/* ---- Register access ---- */\n+int sit9531x_read_u8(struct sit9531x_dev *sitdev, unsigned int reg,\n+\t\t u8 *val);\n+int sit9531x_write_u8(struct sit9531x_dev *sitdev, unsigned int reg,\n+\t\t u8 val);\n+int sit9531x_read_pll_u8(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t u8 offset, u8 *val);\n+int sit9531x_write_pll_u8(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t u8 offset, u8 val);\n+int sit9531x_update_pll_u8(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t u8 offset, u8 mask, u8 val);\n+\n+/* ---- Input enable/disable ---- */\n+int sit9531x_input_disable(struct sit9531x_dev *sitdev, u8 index);\n+int sit9531x_input_enable(struct sit9531x_dev *sitdev, u8 index);\n+\n+/* ---- Input priority ---- */\n+bool sit9531x_input_prio_present(struct sit9531x_dev *sitdev,\n+\t\t\t\t u8 pll_idx, u8 input_idx);\n+int sit9531x_input_prio_set(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t u8 input_idx, u8 prio);\n+int sit9531x_input_prio_get(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t u8 input_idx, u8 *prio);\n+int sit9531x_input_prio_remove(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t u8 input_idx);\n+int sit9531x_input_prio_add(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t u8 input_idx);\n+\n+/* ---- Output enable/disable (Hi-Z control) ---- */\n+int sit9531x_output_disable(struct sit9531x_dev *sitdev, u8 index);\n+int sit9531x_output_enable(struct sit9531x_dev *sitdev, u8 index);\n+int sit9531x_output_state_refresh(struct sit9531x_dev *sitdev,\n+\t\t\t\t u8 out_idx);\n+\n+/* ---- Output frequency ---- */\n+int sit9531x_output_freq_set(struct sit9531x_dev *sitdev, u8 out_idx,\n+\t\t\t u8 pll_idx, u64 frequency);\n+int sit9531x_output_freq_get(struct sit9531x_dev *sitdev, u8 out_idx,\n+\t\t\t u64 *frequency);\n+\n+/* ---- Output phase adjust (PRG_RST_DELAY register-based) ---- */\n+int sit9531x_output_phase_read(struct sit9531x_dev *sitdev, u8 out_idx,\n+\t\t\t s32 *phase_ps, bool *clamped);\n+int sit9531x_output_phase_adjust_set(struct sit9531x_dev *sitdev,\n+\t\t\t\t u8 out_idx, s32 phase_ps);\n+\n+/* ---- Notification clear ---- */\n+int sit9531x_clear_notifications(struct sit9531x_dev *sitdev);\n+\n+/* ---- INTSYNC (inter-PLL synchronization) ---- */\n+int sit9531x_intsync_enable(struct sit9531x_dev *sitdev, u8 src_pll_idx);\n+int sit9531x_intsync_disable(struct sit9531x_dev *sitdev, u8 src_pll_idx);\n+int sit9531x_intsync_src_detect(struct sit9531x_dev *sitdev);\n+\n+/* ---- Phase offset (TDC readback) ---- */\n+int sit9531x_chan_routed_ref_read(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t\t u8 *ref);\n+int sit9531x_input_mon_fetch(struct sit9531x_dev *sitdev);\n+int sit9531x_phase_offset_read(struct sit9531x_dev *sitdev, u8 pll_idx,\n+\t\t\t s64 *phase_offset);\n+\n+/* ---- State helpers ---- */\n+\n+/*\n+ * sit9531x_pll_page - get register page for PLL index\n+ * @pll_idx: PLL index (0 = PLLA, 3 = PLLD)\n+ */\n+static inline u8 sit9531x_pll_page(u8 pll_idx)\n+{\n+\treturn SIT9531X_PAGE_PLLA + pll_idx;\n+}\n+\n+/*\n+ * Logical input pins are interleaved: even index = P lane, odd\n+ * index = N lane of pair index/2 (IN0P, IN0N, IN1P, IN1N, ...).\n+ * Index SIT9531X_MAX_INPUTS is the XO input.\n+ */\n+\n+/*\n+ * sit9531x_input_pair - get input pair number for a logical input index\n+ * @index: logical input pin index\n+ */\n+static inline u8 sit9531x_input_pair(u8 index)\n+{\n+\treturn index \u003e\u003e 1;\n+}\n+\n+/*\n+ * sit9531x_input_is_n - check if a logical input index is an N lane\n+ * @index: logical input pin index\n+ */\n+static inline bool sit9531x_input_is_n(u8 index)\n+{\n+\treturn index \u0026 1;\n+}\n+\n+/*\n+ * sit9531x_input_hw_src - translate logical input index to source encoding\n+ * @index: logical input pin index\n+ *\n+ * The priority table and CLK_ACTIVESEL registers use a non-contiguous\n+ * source encoding: 0-3 = CLK0P..CLK3P, 5 = OCXO, 6 = INTSYNC,\n+ * 7-10 = CLK0N..CLK3N.\n+ */\n+static inline u8 sit9531x_input_hw_src(u8 index)\n+{\n+\tif (index == SIT9531X_MAX_INPUTS)\n+\t\treturn SIT9531X_PRIO_SRC_OCXO;\n+\tif (index == SIT9531X_INTSYNC_PIN_ID)\n+\t\treturn SIT9531X_PRIO_SRC_INTSYNC;\n+\tif (sit9531x_input_is_n(index))\n+\t\treturn SIT9531X_PRIO_SRC_N_BASE + sit9531x_input_pair(index);\n+\treturn sit9531x_input_pair(index);\n+}\n+\n+/*\n+ * sit9531x_hw_src_input - translate source encoding to logical input index\n+ * @src: 4-bit hardware source encoding\n+ *\n+ * Return: logical input index (INTSYNC maps to SIT9531X_INTSYNC_PIN_ID),\n+ * or SIT9531X_REF_INVALID if @src is a reserved value\n+ */\n+static inline u8 sit9531x_hw_src_input(u8 src)\n+{\n+\tif (src \u003c SIT9531X_NUM_INPUT_PAIRS)\n+\t\treturn src * 2;\n+\tif (src == SIT9531X_PRIO_SRC_OCXO)\n+\t\treturn SIT9531X_MAX_INPUTS;\n+\tif (src == SIT9531X_PRIO_SRC_INTSYNC)\n+\t\treturn SIT9531X_INTSYNC_PIN_ID;\n+\tif (src \u003e= SIT9531X_PRIO_SRC_N_BASE \u0026\u0026\n+\t src \u003c SIT9531X_PRIO_SRC_N_BASE + SIT9531X_NUM_INPUT_PAIRS)\n+\t\treturn (src - SIT9531X_PRIO_SRC_N_BASE) * 2 + 1;\n+\treturn SIT9531X_REF_INVALID;\n+}\n+\n+/*\n+ * sit9531x_prio_src_usable - does a priority slot name a reference?\n+ * @src: 4-bit hardware source encoding held in a table slot\n+ *\n+ * Codes 4 and 11 address a fifth input pair this part does not have, and\n+ * a value outside the encoding is not a source either, so a slot holding\n+ * one of them names nothing the device can select. Canonicalizing an N\n+ * lane to its pair first would not change the answer -- both lanes are\n+ * references -- so this needs no device state.\n+ *\n+ * Return: true when the code resolves to a reference the device can use\n+ */\n+static inline bool sit9531x_prio_src_usable(u8 src)\n+{\n+\treturn sit9531x_hw_src_input(src) != SIT9531X_REF_INVALID;\n+}\n+\n+/*\n+ * sit9531x_ref_state_get - get reference state by index\n+ * @index:\tlogical input index\n+ *\n+ * Return: pointer to the cached input reference state\n+ */\n+static inline const struct sit9531x_ref *\n+sit9531x_ref_state_get(const struct sit9531x_dev *sitdev, u8 index)\n+{\n+\treturn \u0026sitdev-\u003eref[index];\n+}\n+\n+/*\n+ * sit9531x_out_state_get - get output state by index\n+ * @index:\tlogical output index\n+ *\n+ * Return: pointer to the cached output state\n+ */\n+static inline const struct sit9531x_out *\n+sit9531x_out_state_get(const struct sit9531x_dev *sitdev, u8 index)\n+{\n+\treturn \u0026sitdev-\u003eout[index];\n+}\n+\n+/*\n+ * sit9531x_chan_state_get - get channel state by PLL index\n+ *\n+ * Return: pointer to the cached per-PLL channel state\n+ */\n+static inline const struct sit9531x_chan *\n+sit9531x_chan_state_get(const struct sit9531x_dev *sitdev, u8 pll_idx)\n+{\n+\treturn \u0026sitdev-\u003echan[pll_idx];\n+}\n+\n+#endif /* _SIT9531X_CORE_H */\ndiff --git a/drivers/dpll/sit9531x/dpll.c b/drivers/dpll/sit9531x/dpll.c\nnew file mode 100644\nindex 0000000000000..4c86ee9e79804\n--- /dev/null\n+++ b/drivers/dpll/sit9531x/dpll.c\n@@ -0,0 +1,1731 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * SiTime SiT9531x DPLL subsystem callbacks and registration\n+ *\n+ * Copyright (C) 2026 SiTime Corp.\n+ * Author: Ali Rouhi \u003carouhi@sitime.com\u003e\n+ * Author: Oleg Zadorozhnyi \u003cOleg.Zadorozhnyi@devoxsoftware.com\u003e\n+ *\n+ * DPLL device ops, pin ops (separate input/output), pin registration,\n+ * and periodic change detection.\n+ */\n+\n+#include \u003clinux/dpll.h\u003e\n+#include \u003clinux/err.h\u003e\n+#include \u003clinux/kthread.h\u003e\n+#include \u003clinux/list.h\u003e\n+#include \u003clinux/netlink.h\u003e\n+#include \u003clinux/slab.h\u003e\n+\n+#include \"core.h\"\n+#include \"dpll.h\"\n+#include \"prop.h\"\n+#include \"regs.h\"\n+\n+static bool sit9531x_dpll_is_input_pin(const struct sit9531x_dpll_pin *pin)\n+{\n+\treturn pin-\u003edir == DPLL_PIN_DIRECTION_INPUT;\n+}\n+\n+static bool\n+sit9531x_dpll_is_intsync_pin(const struct sit9531x_dpll_pin *pin)\n+{\n+\treturn sit9531x_dpll_is_input_pin(pin) \u0026\u0026\n+\t pin-\u003eid == SIT9531X_INTSYNC_PIN_ID;\n+}\n+\n+static bool\n+sit9531x_dpll_is_intsync_src_pin(const struct sit9531x_dpll_pin *pin)\n+{\n+\treturn !sit9531x_dpll_is_input_pin(pin) \u0026\u0026\n+\t pin-\u003eid == SIT9531X_INTSYNC_OUT_PIN_ID;\n+}\n+\n+static bool\n+sit9531x_dpll_is_xo_pin(const struct sit9531x_dpll_pin *pin)\n+{\n+\treturn sit9531x_dpll_is_input_pin(pin) \u0026\u0026\n+\t pin-\u003eid == SIT9531X_MAX_INPUTS;\n+}\n+\n+/*\n+ * The pins the poll watches: the selection-role pins -- regular references\n+ * and the INTSYNC destination -- whose state, operational state and\n+ * priority can move without a request: the device selects on its own, the\n+ * monitors follow the signal, and a table rewritten behind the driver\n+ * re-seeds the priorities. Outputs (incl. the INTSYNC source) change only\n+ * through their own set callback and the XO is permanently connected.\n+ */\n+static bool\n+sit9531x_dpll_is_selection_pin(const struct sit9531x_dpll_pin *pin)\n+{\n+\treturn sit9531x_dpll_is_input_pin(pin) \u0026\u0026\n+\t !sit9531x_dpll_is_xo_pin(pin);\n+}\n+\n+/*\n+ * Status of a PLL whose outer loop is frozen in holdover.\n+ *\n+ * The interface has a PLL report holdover for a hold forced by\n+ * disconnecting every input only when it was LOCKED_HO_ACQ before, that\n+ * is, when it has a valid holdover estimate to run on; without one it\n+ * stays UNLOCKED. Removing the last source from a PLL's table forces\n+ * exactly that hold, so a frozen PLL that lists no source is in holdover\n+ * only while its memory is valid. A PLL frozen with sources still listed\n+ * lost its lock, and is in holdover on the device's say-so.\n+ */\n+static enum dpll_lock_status\n+sit9531x_dpll_frozen_status(const struct sit9531x_chan *chan)\n+{\n+\tif (!chan-\u003eprio_mask \u0026\u0026 !chan-\u003eho_valid)\n+\t\treturn DPLL_LOCK_STATUS_UNLOCKED;\n+\n+\treturn DPLL_LOCK_STATUS_HOLDOVER;\n+}\n+\n+/*\n+ * The cached state this reports comes from the outer loss-of-lock byte\n+ * (page 0, reg 0x06), the PLL mode bit (PLL page, reg 0x31), inner LOL\n+ * (reg 0x92), the holdover freeze byte (reg 0x0A), the per-PLL\n+ * holdover-valid bit (PLL page, reg 0x06) and the priority table.\n+ */\n+static int\n+sit9531x_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv,\n+\t\t\t enum dpll_lock_status *status,\n+\t\t\t enum dpll_lock_status_error *status_error,\n+\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct sit9531x_dpll *sitdpll = dpll_priv;\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\tconst struct sit9531x_chan *chan;\n+\n+\tif (status_error)\n+\t\t*status_error = DPLL_LOCK_STATUS_ERROR_NONE;\n+\n+\tchan = sit9531x_chan_state_get(sitdev, sitdpll-\u003eid);\n+\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (!chan-\u003eactive) {\n+\t\t/*\n+\t\t * A PLL the loaded configuration leaves unused never reaches\n+\t\t * its active state. Nothing drives its loss-of-lock bit, so\n+\t\t * without this it would report a lock it does not have.\n+\t\t */\n+\t\t*status = DPLL_LOCK_STATUS_UNLOCKED;\n+\t} else if (chan-\u003einner_lol) {\n+\t\t/*\n+\t\t * The core publishes the error detail only for the unlocked\n+\t\t * and holdover states, so an inner loss of lock reported\n+\t\t * under a locked status would never reach userspace. An\n+\t\t * inner loop that is not locked is not a locked PLL.\n+\t\t */\n+\t\t*status = chan-\u003eho_freeze ? sit9531x_dpll_frozen_status(chan) :\n+\t\t\t\t\t DPLL_LOCK_STATUS_UNLOCKED;\n+\t} else if (chan-\u003emode) {\n+\t\t/*\n+\t\t * Free-run: the outer loop is disabled, so the PLL tracks no\n+\t\t * reference at all and its loss-of-lock bit means nothing.\n+\t\t * That is what UNLOCKED describes -- \"not yet locked to any\n+\t\t * valid input (or was forced by user)\".\n+\t\t */\n+\t\t*status = DPLL_LOCK_STATUS_UNLOCKED;\n+\t} else if (chan-\u003eho_freeze) {\n+\t\t/*\n+\t\t * Tested before the lock: the freeze and the outer\n+\t\t * loss-of-lock bit are read independently, and a PLL frozen\n+\t\t * in holdover is not tracking a reference whatever its\n+\t\t * loss-of-lock bit says -- the pin-state contract below\n+\t\t * counts a frozen PLL as tracking nothing for the same\n+\t\t * reason.\n+\t\t */\n+\t\t*status = sit9531x_dpll_frozen_status(chan);\n+\t} else if (chan-\u003elocked) {\n+\t\t/*\n+\t\t * HO_ACQ is locked *and* holdover memory acquired, so it needs\n+\t\t * the holdover-valid bit rather than following from the lock.\n+\t\t */\n+\t\tif (chan-\u003eho_valid)\n+\t\t\t*status = DPLL_LOCK_STATUS_LOCKED_HO_ACQ;\n+\t\telse\n+\t\t\t*status = DPLL_LOCK_STATUS_LOCKED;\n+\t} else {\n+\t\t*status = DPLL_LOCK_STATUS_UNLOCKED;\n+\t}\n+\n+\t/* Report inner LOL as an error condition */\n+\tif (status_error \u0026\u0026 chan-\u003einner_lol)\n+\t\t*status_error = DPLL_LOCK_STATUS_ERROR_UNDEFINED;\n+\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * sit9531x_dpll_lock_status_baseline - seed the status the poll compares to\n+ *\n+ * Called once the device is registered. The create notification carried\n+ * the lock status the cache holds, so that is the baseline the first tick\n+ * compares against; taken from the placeholder the allocation left, every\n+ * PLL that is locked or in holdover would look like it had just changed.\n+ */\n+void sit9531x_dpll_lock_status_baseline(struct sit9531x_dpll *sitdpll)\n+{\n+\tsit9531x_dpll_lock_status_get(sitdpll-\u003edpll_dev, sitdpll,\n+\t\t\t\t \u0026sitdpll-\u003elock_status,\n+\t\t\t\t \u0026sitdpll-\u003elock_status_error, NULL);\n+}\n+\n+/*\n+ * Mode\n+ * ====\n+ * enum dpll_mode differentiates how a DPLL selects an input: AUTOMATIC\n+ * has the device pick the highest-priority one, MANUAL has userspace\n+ * request one. The device only implements the former through this\n+ * driver, so AUTOMATIC is the only mode advertised.\n+ *\n+ * With a single mode there is nothing to switch, so no .mode_set: the core\n+ * answers a mode request with -EOPNOTSUPP. Free-run -- the outer loop\n+ * disabled through PLL page reg 0x31[5] -- is not a mode in those terms,\n+ * because no input is selected either way. It is reported through lock\n+ * status instead, and entered and left through the chip-specific tool\n+ * rather than over netlink.\n+ *\n+ * The device could implement real MANUAL: PLL_CONFIG1F_PLL (PLL page reg\n+ * 0x1F) bit 6 switches a PLL from priority-based to manual active select,\n+ * and with MISCINNER_PLL (reg 0x18) bit 5 the PLL follows a manual input\n+ * select -- the input-select pins, or with GPIO_INPUT_FUNC_CTRL5..8\n+ * (page 0, regs 0xE8-0xEB) bit 4 the register's own low nibble -- which\n+ * pins one reference while the loop keeps running. Wiring that up would\n+ * let .state_on_dpll_set() accept CONNECTED, but regs 0x18 and 0x1F carry\n+ * GUI-generated configuration in their other bits, so it is left out.\n+ * Advertising MANUAL and then refusing the one request MANUAL exists for\n+ * is the worse of the two incomplete answers, and after merge it would be\n+ * ABI. A profile that sets manual select anyway is reported at probe,\n+ * since the mode this driver reports would then be wrong.\n+ */\n+static int\n+sit9531x_dpll_mode_get(const struct dpll_device *dpll, void *dpll_priv,\n+\t\t enum dpll_mode *mode, struct netlink_ext_ack *extack)\n+{\n+\t*mode = DPLL_MODE_AUTOMATIC;\n+\n+\treturn 0;\n+}\n+\n+static int\n+sit9531x_dpll_supported_modes_get(const struct dpll_device *dpll,\n+\t\t\t\t void *dpll_priv, unsigned long *modes,\n+\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\t__set_bit(DPLL_MODE_AUTOMATIC, modes);\n+\n+\treturn 0;\n+}\n+\n+const struct dpll_device_ops sit9531x_dpll_device_ops = {\n+\t.lock_status_get\t= sit9531x_dpll_lock_status_get,\n+\t.mode_get\t\t= sit9531x_dpll_mode_get,\n+\t.supported_modes_get\t= sit9531x_dpll_supported_modes_get,\n+\t/* temp_get not available -- SiT9531x has no on-die temp sensor */\n+};\n+\n+/*\n+ * Pin-state contract\n+ * ==================\n+ * The five pin ops tables below fall into three roles, and only the first\n+ * has a selection state machine. Each state_on_dpll callback implements\n+ * the rules for its role and nothing else, so the tables cannot drift\n+ * apart the way five independent encodings of this did.\n+ *\n+ * SELECTION role -- physical input pins, INTSYNC destination pin.\n+ * The state is what userspace asked for; what the device is doing with\n+ * the pin is the operational state. Predicates, all evaluated under\n+ * multiop_lock:\n+ * M source is present in THIS PLL's hardware priority table\n+ * S chan-\u003erouted_ref == this pin's id (the reference the device\n+ * reports feeding the PLL)\n+ * L chan-\u003elocked \u0026\u0026 !chan-\u003emode \u0026\u0026 !chan-\u003eho_freeze\n+ * (tracking a reference: outer loop running, locked, not frozen)\n+ * N the input lane's clock monitor reports loss of signal\n+ * Q the lane's monitor reports a frequency drift, with signal\n+ * state get:\n+ * SELECTABLE M\n+ * DISCONNECTED !M\n+ * operstate get:\n+ * ACTIVE S \u0026\u0026 L \u0026\u0026 !N\n+ * NO_SIGNAL N\n+ * QUAL_FAILED !N \u0026\u0026 Q \u0026\u0026 !(S \u0026\u0026 L)\n+ * STANDBY otherwise\n+ * set:\n+ * DISCONNECTED remove from this PLL's table; a physical input also\n+ * releases this DPLL's claim and powers the shared\n+ * receiver down on the last release\n+ * SELECTABLE add to this PLL's table; a physical input powers the\n+ * receiver up and takes the claim, in that order\n+ * CONNECTED -EOPNOTSUPP -- the device selects by priority and has\n+ * no mode that pins one reference (see \"Mode\" above)\n+ * other -EINVAL\n+ *\n+ * S is not the active selection the driver writes to the table: when\n+ * the selected source loses its signal the device falls back to another\n+ * listed source on its own and leaves the selection naming the dead one,\n+ * so the reference the device reports feeding the PLL is read instead,\n+ * and after such a fallback the pin the PLL moved to is the active one.\n+ * The ACTIVE test needs L as well as S because being fed to the PLL is\n+ * not proof the loop uses it: a free-running, frozen or unlocked PLL\n+ * follows nothing. It needs !N because the device names what it feeds\n+ * the PLL, not what the PLL is locked to, and a lane without signal is\n+ * not that whatever the lock bit still says. The INTSYNC destination\n+ * has no monitor, so N and Q never hold for it.\n+ *\n+ * M is read from the hardware priority table, not from ref-\u003epll_mask,\n+ * which is only the shared-receiver refcount and says nothing about one\n+ * DPLL's eligibility. Priority is kept by the driver per source and\n+ * PLL, independent of M, so a pin reports the same priority whether it\n+ * is connected or not.\n+ *\n+ * DRIVE role -- output pins, INTSYNC source pin.\n+ * Is this pin or net being driven? Nothing is selected here, so:\n+ * CONNECTED pin or net is driven\n+ * DISCONNECTED pin is muted (Hi-Z), or this PLL does not drive it\n+ * SELECTABLE -EINVAL on set, never reported by get\n+ *\n+ * FIXED role -- XO pin. Always CONNECTED; it cannot be routed.\n+ */\n+\n+/*\n+ * Report a selection-role pin's state on this DPLL. @pin_id is a logical\n+ * input index, SIT9531X_INTSYNC_PIN_ID for the INTSYNC destination.\n+ *\n+ * Membership comes from chan-\u003eprio_mask, which is the priority table read\n+ * back from the chip -- not a record of what the driver asked for. The\n+ * getter runs on every poll for every input pin of every DPLL, so it takes\n+ * the mask the worker refreshed rather than rescanning the table over I2C\n+ * each time; table writes refresh it too, so a get right after a set does\n+ * not report the old membership.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+static void\n+sit9531x_dpll_selection_state_get(struct sit9531x_dev *sitdev,\n+\t\t\t\t const struct sit9531x_dpll *sitdpll,\n+\t\t\t\t u8 pin_id, enum dpll_pin_state *state)\n+{\n+\tconst struct sit9531x_chan *chan;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tchan = sit9531x_chan_state_get(sitdev, sitdpll-\u003eid);\n+\n+\tif (chan-\u003eprio_mask \u0026 BIT(sit9531x_input_hw_src(pin_id)))\n+\t\t*state = DPLL_PIN_STATE_SELECTABLE;\n+\telse\n+\t\t*state = DPLL_PIN_STATE_DISCONNECTED;\n+}\n+\n+/*\n+ * Does the device feed a locked PLL from the INTSYNC net?\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+static bool sit9531x_dpll_intsync_tracked(struct sit9531x_dev *sitdev)\n+{\n+\tconst struct sit9531x_chan *chan;\n+\tu8 pll;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tfor (pll = 0; pll \u003c SIT9531X_NUM_PLLS; pll++) {\n+\t\tchan = sit9531x_chan_state_get(sitdev, pll);\n+\t\tif (chan-\u003erouted_ref == SIT9531X_INTSYNC_PIN_ID \u0026\u0026\n+\t\t chan-\u003elocked \u0026\u0026 !chan-\u003eho_freeze)\n+\t\t\treturn true;\n+\t}\n+\n+\treturn false;\n+}\n+\n+/*\n+ * Does a selection-role pin carry a signal? A physical input has the\n+ * device's LOS monitor; the INTSYNC net has no monitor, and what stands in\n+ * for one is the driver's record of a PLL driving it. A partial source\n+ * configuration does not count: the source pin reports it as no\n+ * connection, and the net is not confirmed driven.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+static bool\n+sit9531x_dpll_selection_no_signal(struct sit9531x_dev *sitdev, u8 pin_id)\n+{\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (pin_id == SIT9531X_INTSYNC_PIN_ID) {\n+\t\t/*\n+\t\t * The driver knows the net is driven when it enabled the\n+\t\t * source itself or found the configuration it would have\n+\t\t * written. A profile can drive the net with a configuration\n+\t\t * of its own; then a PLL the device feeds from the net and\n+\t\t * holds locked is the evidence that the net carries a signal.\n+\t\t */\n+\t\tif (sitdev-\u003eintsync_src \u003e= 0 \u0026\u0026 !sitdev-\u003eintsync_partial)\n+\t\t\treturn false;\n+\t\treturn !sit9531x_dpll_intsync_tracked(sitdev);\n+\t}\n+\n+\treturn pin_id \u003c sitdev-\u003einfo-\u003enum_inputs \u0026\u0026\n+\t sit9531x_ref_state_get(sitdev, pin_id)-\u003elos;\n+}\n+\n+/*\n+ * Is this the reference the PLL is tracking now? See the S \u0026\u0026 L \u0026\u0026 !N\n+ * predicate in the pin-state contract. This is also what gates the\n+ * measurements taken against the active reference.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+static bool\n+sit9531x_dpll_selection_active(struct sit9531x_dev *sitdev,\n+\t\t\t const struct sit9531x_dpll *sitdpll, u8 pin_id)\n+{\n+\tconst struct sit9531x_chan *chan;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tchan = sit9531x_chan_state_get(sitdev, sitdpll-\u003eid);\n+\n+\tif (chan-\u003erouted_ref != pin_id || !chan-\u003elocked || chan-\u003emode ||\n+\t chan-\u003eho_freeze)\n+\t\treturn false;\n+\n+\t/*\n+\t * Fed to the PLL is not locked to: the device names the source it\n+\t * routes, and a source that lost its signal is one the PLL is about\n+\t * to be moved off, whatever the lock bit still says.\n+\t */\n+\tif (sit9531x_dpll_selection_no_signal(sitdev, pin_id))\n+\t\treturn false;\n+\n+\treturn true;\n+}\n+\n+/*\n+ * Report a selection-role pin's operational state on this DPLL.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+static void\n+sit9531x_dpll_selection_operstate_get(struct sit9531x_dev *sitdev,\n+\t\t\t\t const struct sit9531x_dpll *sitdpll,\n+\t\t\t\t u8 pin_id,\n+\t\t\t\t enum dpll_pin_operstate *operstate)\n+{\n+\tconst struct sit9531x_ref *ref;\n+\n+\tlockdep_assert_held(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (sit9531x_dpll_selection_active(sitdev, sitdpll, pin_id)) {\n+\t\t*operstate = DPLL_PIN_OPERSTATE_ACTIVE;\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * Standby is a qualified source the PLL is not using. A net nobody\n+\t * drives is no more that than a lane in LOS is.\n+\t */\n+\tif (sit9531x_dpll_selection_no_signal(sitdev, pin_id)) {\n+\t\t*operstate = DPLL_PIN_OPERSTATE_NO_SIGNAL;\n+\t\treturn;\n+\t}\n+\n+\tif (pin_id \u003c sitdev-\u003einfo-\u003enum_inputs) {\n+\t\tref = sit9531x_ref_state_get(sitdev, pin_id);\n+\t\tif (ref-\u003equal_fail) {\n+\t\t\t*operstate = DPLL_PIN_OPERSTATE_QUAL_FAILED;\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\n+\t*operstate = DPLL_PIN_OPERSTATE_STANDBY;\n+}\n+\n+static int\n+sit9531x_dpll_input_pin_operstate_on_dpll_get(const struct dpll_pin *pin,\n+\t\t\t\t\t void *pin_priv,\n+\t\t\t\t\t const struct dpll_device *dpll,\n+\t\t\t\t\t void *dpll_priv,\n+\t\t\t\t\t enum dpll_pin_operstate *state,\n+\t\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct sit9531x_dpll_pin *dpin = pin_priv;\n+\tstruct sit9531x_dpll *sitdpll = dpll_priv;\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\tsit9531x_dpll_selection_operstate_get(sitdev, sitdpll, dpin-\u003eid,\n+\t\t\t\t\t state);\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\n+\treturn 0;\n+}\n+\n+static int\n+sit9531x_dpll_input_pin_direction_get(const struct dpll_pin *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,\n+\t\t\t\t enum dpll_pin_direction *direction,\n+\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\t*direction = DPLL_PIN_DIRECTION_INPUT;\n+\treturn 0;\n+}\n+\n+/*\n+ * sit9531x_dpll_input_pin_frequency_get - read input pin frequency\n+ *\n+ * Returns the rate the board wired to the input, the first entry of its\n+ * supported-frequencies-hz; an input has no frequency setter. An input\n+ * the firmware lists no rate for is registered without this callback\n+ * (sit9531x_dpll_pin_ops_get()), so its attribute is absent rather than\n+ * zero.\n+ */\n+static int\n+sit9531x_dpll_input_pin_frequency_get(const struct dpll_pin *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 sit9531x_dpll_pin *dpin = pin_priv;\n+\tstruct sit9531x_dpll *sitdpll = dpll_priv;\n+\tconst struct sit9531x_ref *ref;\n+\n+\tref = sit9531x_ref_state_get(sitdpll-\u003edev, dpin-\u003eid);\n+\t*frequency = ref-\u003efreq;\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * sit9531x_dpll_input_pin_state_on_dpll_get - get input pin DPLL state\n+ *\n+ * Selection role; see the pin-state contract above.\n+ */\n+static int\n+sit9531x_dpll_input_pin_state_on_dpll_get(const struct dpll_pin *pin,\n+\t\t\t\t\t void *pin_priv,\n+\t\t\t\t\t const struct dpll_device *dpll,\n+\t\t\t\t\t void *dpll_priv,\n+\t\t\t\t\t enum dpll_pin_state *state,\n+\t\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct sit9531x_dpll_pin *dpin = pin_priv;\n+\tstruct sit9531x_dpll *sitdpll = dpll_priv;\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\tsit9531x_dpll_selection_state_get(sitdev, sitdpll, dpin-\u003eid, state);\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * sit9531x_dpll_input_pin_state_on_dpll_set - set input pin DPLL state\n+ *\n+ * Enables or disables the physical input receiver via Page 0x02\n+ * force/state registers (sit9531x_input_disable/enable()) and updates\n+ * this DPLL's Page 1 priority table so the state is honoured by the\n+ * PLL's automatic reference selection, not just at the input buffer.\n+ * Selection role; see the pin-state contract above for the states.\n+ *\n+ * The priority table is per PLL, so it is always updated for this DPLL.\n+ * A single physical input feeds every DPLL, so the hardware receiver is\n+ * only cut off once the last DPLL has released it: ref-\u003epll_mask tracks\n+ * which DPLLs currently claim the input, and the physical disable\n+ * happens on the transition to an empty mask.\n+ */\n+static int\n+sit9531x_dpll_input_pin_state_on_dpll_set(const struct dpll_pin *pin,\n+\t\t\t\t\t void *pin_priv,\n+\t\t\t\t\t const struct dpll_device *dpll,\n+\t\t\t\t\t void *dpll_priv,\n+\t\t\t\t\t enum dpll_pin_state state,\n+\t\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct sit9531x_dpll_pin *dpin = pin_priv;\n+\tstruct sit9531x_dpll *sitdpll = dpll_priv;\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\tstruct sit9531x_ref *ref = \u0026sitdev-\u003eref[dpin-\u003eid];\n+\tu8 hw_src = sit9531x_input_hw_src(dpin-\u003eid);\n+\tu8 pll_bit = BIT(sitdpll-\u003eid);\n+\tbool enabled_here = false;\n+\tint rc;\n+\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\n+\tswitch (state) {\n+\tcase DPLL_PIN_STATE_DISCONNECTED:\n+\t\trc = sit9531x_input_prio_remove(sitdev, sitdpll-\u003eid, hw_src);\n+\t\t/*\n+\t\t * The table write, the latch and the holdover release are\n+\t\t * three steps behind one return code, so ask the table what\n+\t\t * actually happened rather than reading the errno as \"no\n+\t\t * change\". A source that is gone from the table has been\n+\t\t * released whatever else failed.\n+\t\t */\n+\t\tif (rc \u0026\u0026 sit9531x_input_prio_present(sitdev, sitdpll-\u003eid,\n+\t\t\t\t\t\t hw_src))\n+\t\t\tbreak;\n+\t\tref-\u003epll_mask \u0026= ~pll_bit;\n+\t\t/*\n+\t\t * The receiver is shared, so the last DPLL to let go turns it\n+\t\t * off. That has to happen even when the table rewrite\n+\t\t * reported an error, or the input stays powered with nothing\n+\t\t * tracking it; the first error is the one returned.\n+\t\t */\n+\t\tif (!ref-\u003epll_mask) {\n+\t\t\tint off_rc = sit9531x_input_disable(sitdev, dpin-\u003eid);\n+\n+\t\t\tif (off_rc \u0026\u0026 !rc)\n+\t\t\t\trc = off_rc;\n+\t\t}\n+\t\tbreak;\n+\tcase DPLL_PIN_STATE_CONNECTED:\n+\t\t/*\n+\t\t * CONNECTED asks for this input and no other, which the\n+\t\t * device cannot be told to do: it selects by priority and the\n+\t\t * manual-active-select path is not wired up (see \"Mode\").\n+\t\t * Refuse instead of quietly behaving like SELECTABLE.\n+\t\t */\n+\t\tNL_SET_ERR_MSG(extack,\n+\t\t\t \"Device selects its reference by priority; use selectable\");\n+\t\trc = -EOPNOTSUPP;\n+\t\tbreak;\n+\tcase DPLL_PIN_STATE_SELECTABLE:\n+\t\t/*\n+\t\t * Gate the receiver on whenever it is off, not only when this\n+\t\t * DPLL holds no claim yet. The two are tracked separately --\n+\t\t * the claim comes from the priority table, the receiver from\n+\t\t * the force bits -- so a PLL that already lists the input can\n+\t\t * still find it powered down, and skipping the enable would\n+\t\t * report success for a reference that cannot reach the loop.\n+\t\t */\n+\t\tif (!ref-\u003eenabled) {\n+\t\t\trc = sit9531x_input_enable(sitdev, dpin-\u003eid);\n+\t\t\tif (rc)\n+\t\t\t\tbreak;\n+\t\t\tenabled_here = true;\n+\t\t}\n+\t\trc = sit9531x_input_prio_add(sitdev, sitdpll-\u003eid, hw_src);\n+\t\tif (rc \u0026\u0026 !sit9531x_input_prio_present(sitdev, sitdpll-\u003eid,\n+\t\t\t\t\t\t hw_src)) {\n+\t\t\t/*\n+\t\t\t * Undo only what this request did. A receiver the\n+\t\t\t * loaded configuration had already turned on is not\n+\t\t\t * this request's to turn off.\n+\t\t\t */\n+\t\t\tif (enabled_here)\n+\t\t\t\tsit9531x_input_disable(sitdev, dpin-\u003eid);\n+\t\t\tbreak;\n+\t\t}\n+\t\t/*\n+\t\t * Claim the input for this DPLL only once it is both enabled\n+\t\t * and present in the priority table. Setting the mask before\n+\t\t * prio_add would leak the claim if prio_add failed, keeping the\n+\t\t * shared input receiver powered even after every DPLL released\n+\t\t * it.\n+\t\t */\n+\t\tref-\u003epll_mask |= pll_bit;\n+\t\tbreak;\n+\tdefault:\n+\t\trc = -EINVAL;\n+\t\tbreak;\n+\t}\n+\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\n+\t/*\n+\t * Leave the messages the switch already set in place; only a failure\n+\t * that came from the hardware path still needs one.\n+\t */\n+\tif (rc == -ENOSPC)\n+\t\tNL_SET_ERR_MSG(extack,\n+\t\t\t \"Priority table is full of unique sources on this PLL\");\n+\telse if (rc \u0026\u0026 rc != -EOPNOTSUPP \u0026\u0026 rc != -EINVAL)\n+\t\tNL_SET_ERR_MSG(extack, \"Failed to set input pin state\");\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * sit9531x_dpll_input_pin_prio_get - read input pin priority\n+ *\n+ * Reports the priority sit9531x_input_prio_get() keeps for the source on\n+ * this PLL, connected or not; no register is read.\n+ */\n+static int\n+sit9531x_dpll_input_pin_prio_get(const struct dpll_pin *pin, void *pin_priv,\n+\t\t\t\t const struct dpll_device *dpll,\n+\t\t\t\t void *dpll_priv, u32 *prio,\n+\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct sit9531x_dpll_pin *dpin = pin_priv;\n+\tstruct sit9531x_dpll *sitdpll = dpll_priv;\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\tu8 slot;\n+\tint rc;\n+\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\trc = sit9531x_input_prio_get(sitdev, sitdpll-\u003eid,\n+\t\t\t\t sit9531x_input_hw_src(dpin-\u003eid), \u0026slot);\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\t/*\n+\t * dpin-\u003eprio is not touched here: it is the poll's baseline for\n+\t * spotting a change to notify, and a get refreshing it would hide\n+\t * the change from the poll.\n+\t */\n+\t*prio = slot;\n+\treturn 0;\n+}\n+\n+/*\n+ * sit9531x_dpll_input_pin_prio_set - set input pin priority\n+ *\n+ * Records the priority and, for a pin in this PLL's table, rewrites the\n+ * Page 1 table in priority order (sit9531x_input_prio_set()). The other\n+ * pins keep their priorities, so only the named pin changes and the core\n+ * notifies it. A pin that is not in the table keeps the priority for when\n+ * it is connected.\n+ */\n+static int\n+sit9531x_dpll_input_pin_prio_set(const struct dpll_pin *pin, void *pin_priv,\n+\t\t\t\t const struct dpll_device *dpll,\n+\t\t\t\t void *dpll_priv, u32 prio,\n+\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct sit9531x_dpll_pin *dpin = pin_priv;\n+\tstruct sit9531x_dpll *sitdpll = dpll_priv;\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\tint rc;\n+\n+\tif (dpin-\u003edir != DPLL_PIN_DIRECTION_INPUT) {\n+\t\tNL_SET_ERR_MSG(extack, \"Priority applies only to input pins\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (prio \u003e U8_MAX) {\n+\t\tNL_SET_ERR_MSG(extack, \"Priority out of range (0-255)\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\trc = sit9531x_input_prio_set(sitdev, sitdpll-\u003eid,\n+\t\t\t\t sit9531x_input_hw_src(dpin-\u003eid),\n+\t\t\t\t (u8)prio);\n+\tif (!rc)\n+\t\tdpin-\u003eprio = prio;\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (rc) {\n+\t\tNL_SET_ERR_MSG(extack, \"Failed to set input priority\");\n+\t\treturn rc;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * sit9531x_dpll_input_pin_phase_offset_get - phase offset of a reference\n+ *\n+ * What this reports, and what it deliberately does not:\n+ *\n+ * The ABI defines the attribute as the phase difference between the signal\n+ * on a pin and its parent DPLL device, so this is the loop's own residual\n+ * error, sampled with the loop closed. On a locked DPLL it therefore\n+ * trends small -- that is the measurement, not an artefact of it. The\n+ * documentation describes the reported value as one that may be averaged\n+ * over prior measurements, which suits a closed-loop residual and not a\n+ * one-shot open-loop capture; the core publishes whatever this callback\n+ * returns, so the averaging, if any, would be this driver's to do.\n+ *\n+ * The chip can also measure the reference against the local oscillator\n+ * with the outer loop's correction frozen, which is a different quantity\n+ * and the one the documented phase-difference procedure produces. That\n+ * needs the digital loop filter held (and, on the 1PPS PLL, the automatic\n+ * phase- and frequency-lock helpers held off), which leaves the PLL\n+ * undisciplined until it is released. A netlink read must not do that,\n+ * so that measurement is not offered here at all; it belongs to a caller\n+ * that can own the freeze and restore it.\n+ *\n+ * Precondition, which this callback cannot create: the TDC compares\n+ * against a signal the PLL drives, so a PLL driving no output with its\n+ * zero-delay buffer off has nothing to measure. SiTime clock\n+ * engineering confirms this is a property of the hardware, not of\n+ * SiTime's TDC measurement procedure, which satisfies it by mapping a\n+ * spare output and restarting the PLL -- side effects that do not belong\n+ * in a getter, so a reading taken in that state is simply not meaningful.\n+ *\n+ * Non-selected pins and a PLL with no programmed divider report zero\n+ * rather than an error: the DPLL core propagates any error from this\n+ * callback and fails the whole pin dump with it. The core has no per-pin\n+ * \"no data\" for phase offset, as it has -ENODATA for the fractional\n+ * frequency offset, so it is a value or no callback at all.\n+ */\n+static int\n+sit9531x_dpll_input_pin_phase_offset_get(const struct dpll_pin *pin,\n+\t\t\t\t\t void *pin_priv,\n+\t\t\t\t\t const struct dpll_device *dpll,\n+\t\t\t\t\t void *dpll_priv, s64 *phase_offset,\n+\t\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct sit9531x_dpll_pin *dpin = pin_priv;\n+\tstruct sit9531x_dpll *sitdpll = dpll_priv;\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\tbool drives = false;\n+\ts64 offset;\n+\tu8 routed, i;\n+\tint rc;\n+\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\n+\t/*\n+\t * The on-chip TDC is a per-PLL resource that always measures the\n+\t * phase difference between the VCO and the PLL's currently\n+\t * selected reference; it cannot be pointed at an arbitrary input,\n+\t * so an input that is not the active reference reports 0 rather\n+\t * than the active reference's value.\n+\t *\n+\t * The sample needs both: the PLL tracking this pin as the poll last\n+\t * saw it (locked, outer loop running, not frozen), and the device\n+\t * still feeding it this pin, read now -- the device moves on its\n+\t * own, and a cache up to a poll period old could attribute a live\n+\t * measurement to the pin the PLL used to be fed. The device is read\n+\t * again after the sample for the same reason: it can switch during\n+\t * the dozen transfers the sample takes.\n+\t *\n+\t * The TDC also compares against a signal the PLL drives, so a PLL\n+\t * with no routed, driving output has nothing to measure and reports\n+\t * 0 like any other pin without a reading. Output state is never\n+\t * polled, so a mute or unmute whose read-back failed is read\n+\t * through here first, as the output's own getter does, rather than\n+\t * taken from a cache that was left describing the state before it.\n+\t */\n+\tfor (i = 0; i \u003c sitdev-\u003einfo-\u003enum_outputs; i++) {\n+\t\tif (!sitdev-\u003eout[i].routed ||\n+\t\t sitdev-\u003eout[i].pll_idx != sitdpll-\u003eid)\n+\t\t\tcontinue;\n+\t\tif (sitdev-\u003eout[i].state_stale) {\n+\t\t\trc = sit9531x_output_state_refresh(sitdev, i);\n+\t\t\tif (rc) {\n+\t\t\t\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\t\t\t\tNL_SET_ERR_MSG(extack,\n+\t\t\t\t\t \"Output mute state could not be read back\");\n+\t\t\t\treturn rc;\n+\t\t\t}\n+\t\t}\n+\t\tif (sitdev-\u003eout[i].enabled)\n+\t\t\tdrives = true;\n+\t}\n+\n+\t/*\n+\t * The lane's signal is read live as well: a loss since the last poll\n+\t * makes the device fall back on its own, and a sample taken then\n+\t * would be credited to a pin the PLL is no longer on.\n+\t */\n+\trc = sit9531x_input_mon_fetch(sitdev);\n+\tif (rc) {\n+\t\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\t\tNL_SET_ERR_MSG(extack,\n+\t\t\t \"Failed to read the input clock monitors\");\n+\t\treturn rc;\n+\t}\n+\n+\tif (!drives ||\n+\t !sit9531x_dpll_selection_active(sitdev, sitdpll, dpin-\u003eid)) {\n+\t\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\t\t*phase_offset = 0;\n+\t\treturn 0;\n+\t}\n+\n+\trc = sit9531x_chan_routed_ref_read(sitdev, sitdpll-\u003eid, \u0026routed);\n+\tif (!rc \u0026\u0026 routed == dpin-\u003eid) {\n+\t\trc = sit9531x_phase_offset_read(sitdev, sitdpll-\u003eid, \u0026offset);\n+\t\tif (!rc)\n+\t\t\trc = sit9531x_chan_routed_ref_read(sitdev, sitdpll-\u003eid,\n+\t\t\t\t\t\t\t \u0026routed);\n+\t\tif (!rc \u0026\u0026 routed != dpin-\u003eid)\n+\t\t\trc = -ENODATA;\n+\t} else if (!rc) {\n+\t\trc = -ENODATA;\n+\t}\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\n+\t/*\n+\t * -ENODATA means no reading: the PLL has no known VCO rate, or the\n+\t * device moved the PLL off this pin around the sample. Report 0 so a\n+\t * full pin-get dump does not fail over it. Every other errno,\n+\t * -ENODEV from a vanished adapter included, is a failure.\n+\t */\n+\tif (rc == -ENODATA) {\n+\t\t*phase_offset = 0;\n+\t\treturn 0;\n+\t}\n+\tif (rc) {\n+\t\tNL_SET_ERR_MSG(extack, \"TDC phase readback failed\");\n+\t\treturn rc;\n+\t}\n+\n+\t*phase_offset = offset;\n+\treturn 0;\n+}\n+\n+static const struct dpll_pin_ops sit9531x_dpll_input_pin_ops = {\n+\t.direction_get\t\t= sit9531x_dpll_input_pin_direction_get,\n+\t.frequency_get\t\t= sit9531x_dpll_input_pin_frequency_get,\n+\t.state_on_dpll_get\t= sit9531x_dpll_input_pin_state_on_dpll_get,\n+\t.state_on_dpll_set\t= sit9531x_dpll_input_pin_state_on_dpll_set,\n+\t.operstate_on_dpll_get\t= sit9531x_dpll_input_pin_operstate_on_dpll_get,\n+\t.prio_get\t\t= sit9531x_dpll_input_pin_prio_get,\n+\t.prio_set\t\t= sit9531x_dpll_input_pin_prio_set,\n+\t.phase_offset_get\t= sit9531x_dpll_input_pin_phase_offset_get,\n+};\n+\n+/*\n+ * The same without frequency_get, for an input whose firmware node lists\n+ * no supported-frequencies-hz (or that has no node at all): there is no\n+ * rate to report, and the core fails a whole pin dump on an error from\n+ * any one callback, so the attribute is left out rather than reported\n+ * as 0 Hz.\n+ */\n+static const struct dpll_pin_ops sit9531x_dpll_input_pin_norate_ops = {\n+\t.direction_get\t\t= sit9531x_dpll_input_pin_direction_get,\n+\t.state_on_dpll_get\t= sit9531x_dpll_input_pin_state_on_dpll_get,\n+\t.state_on_dpll_set\t= sit9531x_dpll_input_pin_state_on_dpll_set,\n+\t.operstate_on_dpll_get\t= sit9531x_dpll_input_pin_operstate_on_dpll_get,\n+\t.prio_get\t\t= sit9531x_dpll_input_pin_prio_get,\n+\t.prio_set\t\t= sit9531x_dpll_input_pin_prio_set,\n+\t.phase_offset_get\t= sit9531x_dpll_input_pin_phase_offset_get,\n+};\n+\n+/*\n+ * INTSYNC pin ops\n+ *\n+ * INTSYNC is the chip's inter-PLL sync net: one PLL drives it and other\n+ * PLLs may lock to it instead of to an external reference. The two\n+ * roles are exposed as two separate pins so neither overloads the other:\n+ *\n+ * - a source (output) pin registered on every DPLL. Connecting it on a\n+ * DPLL makes that DPLL drive INTSYNC; only one DPLL may drive it at a\n+ * time. It has no priority ops -- driving the net is not a reference\n+ * selection.\n+ * - a destination (input) pin registered on every DPLL. Connecting it\n+ * on a DPLL makes that DPLL eligible to lock to INTSYNC as a\n+ * reference, so it carries the priority ops.\n+ */\n+\n+/* ---- INTSYNC source (output) pin ---- */\n+\n+/* The INTSYNC source pin is an output; its direction_get is defined below. */\n+static int\n+sit9531x_dpll_output_pin_direction_get(const struct dpll_pin *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,\n+\t\t\t\t enum dpll_pin_direction *direction,\n+\t\t\t\t struct netlink_ext_ack *extack);\n+\n+/*\n+ * Does this PLL drive the INTSYNC net with a complete configuration?\n+ * What the source pin reports as CONNECTED.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+static bool\n+sit9531x_dpll_intsync_src_connected(const struct sit9531x_dev *sitdev,\n+\t\t\t\t const struct sit9531x_dpll *sitdpll)\n+{\n+\treturn sitdev-\u003eintsync_src == sitdpll-\u003eid \u0026\u0026 !sitdev-\u003eintsync_partial;\n+}\n+\n+static int\n+sit9531x_dpll_intsync_src_state_on_dpll_get(const struct dpll_pin *pin,\n+\t\t\t\t\t void *pin_priv,\n+\t\t\t\t\t const struct dpll_device *dpll,\n+\t\t\t\t\t void *dpll_priv,\n+\t\t\t\t\t enum dpll_pin_state *state,\n+\t\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct sit9531x_dpll *sitdpll = dpll_priv;\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\tif (sit9531x_dpll_intsync_src_connected(sitdev, sitdpll))\n+\t\t*state = DPLL_PIN_STATE_CONNECTED;\n+\telse\n+\t\t*state = DPLL_PIN_STATE_DISCONNECTED;\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * sit9531x_dpll_intsync_src_state_on_dpll_set - drive INTSYNC from a PLL\n+ *\n+ * CONNECTED -\u003e this PLL drives the INTSYNC net\n+ * DISCONNECTED -\u003e stop driving INTSYNC if this PLL drives it\n+ *\n+ * SELECTABLE is rejected: driving the net is an explicit output routing,\n+ * not an automatic-selection candidate, matching the regular output pin.\n+ */\n+static int\n+sit9531x_dpll_intsync_src_state_on_dpll_set(const struct dpll_pin *pin,\n+\t\t\t\t\t void *pin_priv,\n+\t\t\t\t\t const struct dpll_device *dpll,\n+\t\t\t\t\t void *dpll_priv,\n+\t\t\t\t\t enum dpll_pin_state state,\n+\t\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct sit9531x_dpll_pin *dpin = pin_priv;\n+\tstruct sit9531x_dpll *sitdpll = dpll_priv;\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\tint rc = 0, detect_rc = 0;\n+\tbool changed = false;\n+\tbool was_connected, moved;\n+\tu8 hw_src;\n+\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\n+\twas_connected = sit9531x_dpll_intsync_src_connected(sitdev, sitdpll);\n+\n+\tswitch (state) {\n+\tcase DPLL_PIN_STATE_CONNECTED:\n+\t\t/*\n+\t\t * A partial configuration on this PLL is not a connection:\n+\t\t * run the enable again so a retry after a failed one repairs\n+\t\t * the device instead of reporting success over it.\n+\t\t */\n+\t\tif (was_connected)\n+\t\t\tbreak;\n+\t\tif (sitdev-\u003eintsync_src \u003e= 0 \u0026\u0026\n+\t\t sitdev-\u003eintsync_src != sitdpll-\u003eid) {\n+\t\t\tNL_SET_ERR_MSG(extack,\n+\t\t\t\t \"INTSYNC is already sourced by another PLL\");\n+\t\t\trc = -EBUSY;\n+\t\t\tbreak;\n+\t\t}\n+\t\t/*\n+\t\t * A PLL that already lists INTSYNC among its references must\n+\t\t * not also drive it: the destination side refuses the mirror\n+\t\t * of this, and without the check here the net could be routed\n+\t\t * back into the PLL feeding it.\n+\t\t */\n+\t\thw_src = sit9531x_input_hw_src(SIT9531X_INTSYNC_PIN_ID);\n+\t\tif (sit9531x_input_prio_present(sitdev, sitdpll-\u003eid, hw_src)) {\n+\t\t\tNL_SET_ERR_MSG(extack,\n+\t\t\t\t \"PLL selects INTSYNC as a reference; it cannot drive it\");\n+\t\t\trc = -EBUSY;\n+\t\t\tbreak;\n+\t\t}\n+\t\trc = sit9531x_intsync_enable(sitdev, sitdpll-\u003eid);\n+\t\tchanged = true;\n+\t\tbreak;\n+\tcase DPLL_PIN_STATE_DISCONNECTED:\n+\t\tif (sitdev-\u003eintsync_src != sitdpll-\u003eid)\n+\t\t\tbreak;\n+\t\trc = sit9531x_intsync_disable(sitdev, sitdpll-\u003eid);\n+\t\tchanged = true;\n+\t\tbreak;\n+\tdefault:\n+\t\trc = -EINVAL;\n+\t\tbreak;\n+\t}\n+\n+\t/*\n+\t * Record what was done before confirming it, and only when this PLL\n+\t * actually changed: the core forwards a request for the state the\n+\t * pin is already in, and a DISCONNECTED on a PLL that never drove the\n+\t * net must not erase the owner. The refresh below leaves the cache\n+\t * untouched when a read fails, and a cache that wrongly says nobody\n+\t * drives the net would let a second PLL be configured to drive it.\n+\t */\n+\tif (changed \u0026\u0026 !rc) {\n+\t\tsitdev-\u003eintsync_src = state == DPLL_PIN_STATE_CONNECTED ?\n+\t\t\t\t sitdpll-\u003eid : -1;\n+\t\tsitdev-\u003eintsync_partial = false;\n+\t}\n+\n+\t/*\n+\t * Re-scan hardware after a transition so the cache follows a\n+\t * partially failed enable or disable as closely as possible.\n+\t */\n+\tif (changed)\n+\t\tdetect_rc = sit9531x_intsync_src_detect(sitdev);\n+\t/*\n+\t * A failed enable that the scan still finds on this PLL is partial\n+\t * even when every register reads the enable pattern: the small\n+\t * update that commits it may be the step that failed. Mark it, so\n+\t * a retried connect runs the enable again.\n+\t *\n+\t * When the scan itself failed, nothing says how far the enable and\n+\t * its rollback got, and the net may be driven. Record this PLL as\n+\t * the partial owner rather than leave the cache saying nobody drives\n+\t * the net, which would let a second PLL be configured on top of it.\n+\t * A disconnect on this PLL runs the disable and a scan that settle\n+\t * the record.\n+\t */\n+\tif (changed \u0026\u0026 rc \u0026\u0026 state == DPLL_PIN_STATE_CONNECTED \u0026\u0026\n+\t (detect_rc || sitdev-\u003eintsync_src == sitdpll-\u003eid)) {\n+\t\tsitdev-\u003eintsync_src = sitdpll-\u003eid;\n+\t\tsitdev-\u003eintsync_partial = true;\n+\t}\n+\t/*\n+\t * The refresh only re-reads what the device now shows. Failing\n+\t * the request because that read hit a bus error would tell\n+\t * userspace the enable did not happen when it did.\n+\t */\n+\tif (detect_rc)\n+\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t \"INTSYNC source cache not refreshed: %d\\n\",\n+\t\t\t detect_rc);\n+\n+\t/*\n+\t * What the pin reports may have moved although the request failed:\n+\t * a disable that stopped at its last step leaves nothing driving\n+\t * the net, a failed enable leaves a partial owner. The core\n+\t * notifies only a request that succeeded and the poll does not\n+\t * watch this pin, so announce it here, as the output setter does.\n+\t */\n+\tmoved = sit9531x_dpll_intsync_src_connected(sitdev, sitdpll) !=\n+\t\twas_connected;\n+\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (rc) {\n+\t\tstruct dpll_pin *dpll_pin;\n+\n+\t\tif (rc != -EBUSY \u0026\u0026 rc != -EINVAL \u0026\u0026 rc != -EOPNOTSUPP)\n+\t\t\tNL_SET_ERR_MSG(extack,\n+\t\t\t\t \"Failed to set INTSYNC source state\");\n+\t\t/*\n+\t\t * The core's lock is held here, as the helper requires.\n+\t\t * Teardown clears the handle before it unregisters the pin;\n+\t\t * see the output state setter.\n+\t\t */\n+\t\tdpll_pin = READ_ONCE(dpin-\u003edpll_pin);\n+\t\tif (moved \u0026\u0026 dpll_pin)\n+\t\t\t__dpll_pin_change_ntf(dpll_pin);\n+\t}\n+\n+\treturn rc;\n+}\n+\n+static const struct dpll_pin_ops sit9531x_dpll_intsync_src_pin_ops = {\n+\t.direction_get\t\t= sit9531x_dpll_output_pin_direction_get,\n+\t.state_on_dpll_get\t= sit9531x_dpll_intsync_src_state_on_dpll_get,\n+\t.state_on_dpll_set\t= sit9531x_dpll_intsync_src_state_on_dpll_set,\n+};\n+\n+/* ---- INTSYNC destination (input) pin ---- */\n+\n+/*\n+ * sit9531x_dpll_intsync_dst_state_on_dpll_get - INTSYNC reference state\n+ *\n+ * Selection role, so the contract above decides this exactly as it does\n+ * for a physical input: the priority table is the eligibility record, and\n+ * whether a source PLL happens to be driving the net right now is no more\n+ * a state than a momentary LOS is on an external reference. The one\n+ * addition is that the PLL driving INTSYNC is never its own destination.\n+ */\n+static int\n+sit9531x_dpll_intsync_dst_state_on_dpll_get(const struct dpll_pin *pin,\n+\t\t\t\t\t void *pin_priv,\n+\t\t\t\t\t const struct dpll_device *dpll,\n+\t\t\t\t\t void *dpll_priv,\n+\t\t\t\t\t enum dpll_pin_state *state,\n+\t\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct sit9531x_dpll *sitdpll = dpll_priv;\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\tif (sitdev-\u003eintsync_src == sitdpll-\u003eid)\n+\t\t*state = DPLL_PIN_STATE_DISCONNECTED;\n+\telse\n+\t\tsit9531x_dpll_selection_state_get(sitdev, sitdpll,\n+\t\t\t\t\t\t SIT9531X_INTSYNC_PIN_ID,\n+\t\t\t\t\t\t state);\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * sit9531x_dpll_intsync_dst_state_on_dpll_set - lock a PLL to INTSYNC\n+ *\n+ * Selection role, so this accepts and refuses what a physical input does,\n+ * CONNECTED included: the device pins no reference on request whichever\n+ * source is asked for. INTSYNC is an internal net with no physical\n+ * receiver, so only the per-PLL priority table is touched; the source pin\n+ * controls generation.\n+ */\n+static int\n+sit9531x_dpll_intsync_dst_state_on_dpll_set(const struct dpll_pin *pin,\n+\t\t\t\t\t void *pin_priv,\n+\t\t\t\t\t const struct dpll_device *dpll,\n+\t\t\t\t\t void *dpll_priv,\n+\t\t\t\t\t enum dpll_pin_state state,\n+\t\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct sit9531x_dpll *sitdpll = dpll_priv;\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\tu8 hw_src = sit9531x_input_hw_src(SIT9531X_INTSYNC_PIN_ID);\n+\tint rc;\n+\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\n+\tswitch (state) {\n+\tcase DPLL_PIN_STATE_DISCONNECTED:\n+\t\trc = sit9531x_input_prio_remove(sitdev, sitdpll-\u003eid, hw_src);\n+\t\tbreak;\n+\tcase DPLL_PIN_STATE_CONNECTED:\n+\t\tNL_SET_ERR_MSG(extack,\n+\t\t\t \"Device selects its reference by priority; use selectable\");\n+\t\trc = -EOPNOTSUPP;\n+\t\tbreak;\n+\tcase DPLL_PIN_STATE_SELECTABLE:\n+\t\tif (sitdev-\u003eintsync_src == sitdpll-\u003eid) {\n+\t\t\tNL_SET_ERR_MSG(extack,\n+\t\t\t\t \"PLL cannot lock to the INTSYNC it drives\");\n+\t\t\trc = -EINVAL;\n+\t\t\tbreak;\n+\t\t}\n+\t\trc = sit9531x_input_prio_add(sitdev, sitdpll-\u003eid, hw_src);\n+\t\tbreak;\n+\tdefault:\n+\t\trc = -EINVAL;\n+\t\tbreak;\n+\t}\n+\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (rc == -ENOSPC)\n+\t\tNL_SET_ERR_MSG(extack,\n+\t\t\t \"Priority table is full of unique sources on this PLL\");\n+\telse if (rc \u0026\u0026 rc != -EINVAL \u0026\u0026 rc != -EOPNOTSUPP)\n+\t\tNL_SET_ERR_MSG(extack, \"Failed to set INTSYNC input state\");\n+\n+\treturn rc;\n+}\n+\n+static int\n+sit9531x_dpll_intsync_dst_operstate_on_dpll_get(const struct dpll_pin *pin,\n+\t\t\t\t\t\tvoid *pin_priv,\n+\t\t\t\t\t\tconst struct dpll_device *dpll,\n+\t\t\t\t\t\tvoid *dpll_priv,\n+\t\t\t\t\t\tenum dpll_pin_operstate *state,\n+\t\t\t\t\t\tstruct netlink_ext_ack *extack)\n+{\n+\tstruct sit9531x_dpll *sitdpll = dpll_priv;\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\tsit9531x_dpll_selection_operstate_get(sitdev, sitdpll,\n+\t\t\t\t\t SIT9531X_INTSYNC_PIN_ID,\n+\t\t\t\t\t state);\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Do not add .frequency_get / the generic input state getter here: the\n+ * destination pin id is SIT9531X_INTSYNC_PIN_ID, one past the end of the\n+ * ref[] array (INTSYNC is an internal net with no ref[] entry). The ops\n+ * below only ever key on chan[] and the priority table, never ref[id].\n+ */\n+static const struct dpll_pin_ops sit9531x_dpll_intsync_dst_pin_ops = {\n+\t.direction_get\t\t= sit9531x_dpll_input_pin_direction_get,\n+\t.state_on_dpll_get\t= sit9531x_dpll_intsync_dst_state_on_dpll_get,\n+\t.state_on_dpll_set\t= sit9531x_dpll_intsync_dst_state_on_dpll_set,\n+\t.operstate_on_dpll_get\t=\n+\t\tsit9531x_dpll_intsync_dst_operstate_on_dpll_get,\n+\t.prio_get\t\t= sit9531x_dpll_input_pin_prio_get,\n+\t.prio_set\t\t= sit9531x_dpll_input_pin_prio_set,\n+};\n+\n+/*\n+ * XO (crystal oscillator) pin ops\n+ *\n+ * The XO is the chip's internal reference oscillator that feeds every\n+ * PLL. It is exposed so userspace can see the on-chip reference, but it\n+ * cannot be routed or disconnected, so it is reported permanently\n+ * connected and offers no state_on_dpll_set / prio ops.\n+ */\n+\n+static int\n+sit9531x_dpll_xo_pin_state_on_dpll_get(const struct dpll_pin *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,\n+\t\t\t\t enum dpll_pin_state *state,\n+\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\t*state = DPLL_PIN_STATE_CONNECTED;\n+\treturn 0;\n+}\n+\n+static const struct dpll_pin_ops sit9531x_dpll_xo_pin_ops = {\n+\t.direction_get\t\t= sit9531x_dpll_input_pin_direction_get,\n+\t.frequency_get\t\t= sit9531x_dpll_input_pin_frequency_get,\n+\t.state_on_dpll_get\t= sit9531x_dpll_xo_pin_state_on_dpll_get,\n+};\n+\n+static int\n+sit9531x_dpll_output_pin_direction_get(const struct dpll_pin *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,\n+\t\t\t\t enum dpll_pin_direction *direction,\n+\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\t*direction = DPLL_PIN_DIRECTION_OUTPUT;\n+\treturn 0;\n+}\n+\n+/*\n+ * sit9531x_dpll_output_pin_frequency_get - read output pin frequency\n+ *\n+ * Reads the DIVO divider back from the chip and computes the live\n+ * frequency as Fvco / DIVO. Falls back to the cached value only when\n+ * the output is not resolvable through the divider chain (e.g. not\n+ * mapped to a PLL), so transport/register errors still surface.\n+ */\n+static int\n+sit9531x_dpll_output_pin_frequency_get(const struct dpll_pin *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 sit9531x_dpll_pin *dpin = pin_priv;\n+\tstruct sit9531x_dpll *sitdpll = dpll_priv;\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\tint rc;\n+\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\trc = sit9531x_output_freq_get(sitdev, dpin-\u003eid, frequency);\n+\tif (rc == -ENODATA)\n+\t\t*frequency = sit9531x_out_state_get(sitdev, dpin-\u003eid)-\u003efreq;\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\n+\treturn rc == -ENODATA ? 0 : rc;\n+}\n+\n+/*\n+ * sit9531x_dpll_output_pin_frequency_set - set output pin frequency\n+ *\n+ * computes DIVO = Fvco / frequency and writes the\n+ * 34-bit output divider to the output system registers via\n+ * sit9531x_output_freq_set().\n+ */\n+static int\n+sit9531x_dpll_output_pin_frequency_set(const struct dpll_pin *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 sit9531x_dpll_pin *dpin = pin_priv;\n+\tstruct sit9531x_dpll *sitdpll = dpll_priv;\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\tu8 actual_pll;\n+\tint rc;\n+\n+\t/*\n+\t * Read the PLL that drives this output from its OUT_MAP state\n+\t * (populated by out_state_fetch from the chip's OUT_MAP registers).\n+\t * That is the index the output register programming below is keyed\n+\t * by; the output is registered under the DPLL matching this PLL.\n+\t */\n+\tactual_pll = sitdev-\u003eout[dpin-\u003eid].pll_idx;\n+\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\trc = sit9531x_output_freq_set(sitdev, dpin-\u003eid, actual_pll,\n+\t\t\t\t frequency);\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (rc)\n+\t\tNL_SET_ERR_MSG(extack, \"Output frequency set failed\");\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * sit9531x_dpll_output_pin_state_on_dpll_get - get output pin state\n+ *\n+ * reports CONNECTED when the output is driven and\n+ * DISCONNECTED when it has been muted via sit9531x_output_disable().\n+ */\n+static int\n+sit9531x_dpll_output_pin_state_on_dpll_get(const struct dpll_pin *pin,\n+\t\t\t\t\t void *pin_priv,\n+\t\t\t\t\t const struct dpll_device *dpll,\n+\t\t\t\t\t void *dpll_priv,\n+\t\t\t\t\t enum dpll_pin_state *state,\n+\t\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct sit9531x_dpll_pin *dpin = pin_priv;\n+\tstruct sit9531x_dpll *sitdpll = dpll_priv;\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\tconst struct sit9531x_out *out;\n+\tint rc;\n+\n+\t/*\n+\t * A mute whose read-back failed left the cache unconfirmed; there is\n+\t * no poll of output state to correct it, so read it here rather than\n+\t * report a value that may predate the request.\n+\t */\n+\tif (sitdev-\u003eout[dpin-\u003eid].state_stale) {\n+\t\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\t\trc = sit9531x_output_state_refresh(sitdev, dpin-\u003eid);\n+\t\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\t\tif (rc) {\n+\t\t\tNL_SET_ERR_MSG(extack,\n+\t\t\t\t \"Output mute state could not be read back\");\n+\t\t\treturn rc;\n+\t\t}\n+\t}\n+\n+\tout = sit9531x_out_state_get(sitdev, dpin-\u003eid);\n+\t*state = out-\u003eenabled ? DPLL_PIN_STATE_CONNECTED\n+\t\t\t : DPLL_PIN_STATE_DISCONNECTED;\n+\treturn 0;\n+}\n+\n+/*\n+ * sit9531x_dpll_output_pin_state_on_dpll_set - mute/un-mute an output\n+ *\n+ * Forces Hi-Z on the output through the Hi-Z force/state register pairs\n+ * of its slot, the differential and the single-ended one.\n+ * CONNECTED -\u003e enable (release the override, the device drives it)\n+ * DISCONNECTED -\u003e disable (force Hi-Z)\n+ */\n+static int\n+sit9531x_dpll_output_pin_state_on_dpll_set(const struct dpll_pin *pin,\n+\t\t\t\t\t void *pin_priv,\n+\t\t\t\t\t const struct dpll_device *dpll,\n+\t\t\t\t\t void *dpll_priv,\n+\t\t\t\t\t enum dpll_pin_state state,\n+\t\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct sit9531x_dpll_pin *dpin = pin_priv;\n+\tstruct sit9531x_dpll *sitdpll = dpll_priv;\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\tbool enable = state == DPLL_PIN_STATE_CONNECTED;\n+\tbool was_enabled, changed;\n+\tint rc;\n+\n+\tif (!enable \u0026\u0026 state != DPLL_PIN_STATE_DISCONNECTED)\n+\t\treturn -EINVAL;\n+\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\n+\t/*\n+\t * The core forwards a request for the state the output is already\n+\t * in. The Hi-Z pairs say what the pad does now, so read them and\n+\t * answer such a request from them rather than run the programming\n+\t * sequence, which holds the output loops unlocked for the 100 ms its\n+\t * commit takes and would change nothing. A read that fails leaves\n+\t * the request to the sequence and its own read-back.\n+\t */\n+\tif (!sit9531x_output_state_refresh(sitdev, dpin-\u003eid) \u0026\u0026\n+\t sitdev-\u003eout[dpin-\u003eid].enabled == enable) {\n+\t\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\t\treturn 0;\n+\t}\n+\n+\twas_enabled = sitdev-\u003eout[dpin-\u003eid].enabled;\n+\n+\tif (enable)\n+\t\trc = sit9531x_output_enable(sitdev, dpin-\u003eid);\n+\telse\n+\t\trc = sit9531x_output_disable(sitdev, dpin-\u003eid);\n+\n+\t/*\n+\t * A read-back that failed left the cache unconfirmed, and the\n+\t * writes before it most likely landed: count that as a change too,\n+\t * and let the getter's read-through fill in the state when the\n+\t * notification is built.\n+\t */\n+\tchanged = sitdev-\u003eout[dpin-\u003eid].enabled != was_enabled ||\n+\t\t sitdev-\u003eout[dpin-\u003eid].state_stale;\n+\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (rc) {\n+\t\tstruct dpll_pin *dpll_pin;\n+\n+\t\tNL_SET_ERR_MSG(extack, \"Failed to set output pin state\");\n+\t\t/*\n+\t\t * The core notifies only a request that succeeded, and the\n+\t\t * poll does not watch outputs. A failed request whose\n+\t\t * read-back shows the output did change, or could not show\n+\t\t * whether it did, still has to be announced, or subscribers\n+\t\t * keep the old state for good. The core's lock is held here,\n+\t\t * as the helper requires.\n+\t\t *\n+\t\t * Teardown clears the pin's handle without that lock before\n+\t\t * it unregisters the pin, and this callback spends the\n+\t\t * sequence's 100 ms under it. Read the handle once: NULL is\n+\t\t * a pin on its way out, with nobody left to tell, and a\n+\t\t * handle still set stays valid until this callback returns.\n+\t\t */\n+\t\tdpll_pin = READ_ONCE(dpin-\u003edpll_pin);\n+\t\tif (changed \u0026\u0026 dpll_pin)\n+\t\t\t__dpll_pin_change_ntf(dpll_pin);\n+\t}\n+\n+\treturn rc;\n+}\n+\n+/*\n+ * sit9531x_dpll_output_pin_phase_adjust_get - read output phase adjustment\n+ *\n+ * Returns what the delay registers hold, i.e. the value\n+ * sit9531x_output_phase_adjust_set() programmed after quantization, read\n+ * from the cache unless a failed request left it unconfirmed.\n+ */\n+static int\n+sit9531x_dpll_output_pin_phase_adjust_get(const struct dpll_pin *pin,\n+\t\t\t\t\t void *pin_priv,\n+\t\t\t\t\t const struct dpll_device *dpll,\n+\t\t\t\t\t void *dpll_priv, s32 *phase_adjust,\n+\t\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct sit9531x_dpll_pin *dpin = pin_priv;\n+\tstruct sit9531x_dpll *sitdpll = dpll_priv;\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\tint rc;\n+\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\t/*\n+\t * A request whose writes reached the device but whose commit or\n+\t * phase flush failed left the cache describing the delay before it.\n+\t * There is no poll of the delay registers to correct that, so read\n+\t * them here rather than report a value the output is not using.\n+\t */\n+\tif (sitdev-\u003eout[dpin-\u003eid].phase_stale) {\n+\t\tbool clamped;\n+\t\ts32 phase_ps;\n+\n+\t\trc = sit9531x_output_phase_read(sitdev, dpin-\u003eid, \u0026phase_ps,\n+\t\t\t\t\t\t\u0026clamped);\n+\t\tif (rc) {\n+\t\t\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\t\t\tNL_SET_ERR_MSG(extack,\n+\t\t\t\t \"Output delay could not be read back\");\n+\t\t\treturn rc;\n+\t\t}\n+\t\tsitdev-\u003eout[dpin-\u003eid].phase_adj = phase_ps;\n+\t\tsitdev-\u003eout[dpin-\u003eid].phase_armed = !clamped \u0026\u0026 phase_ps;\n+\t\tsitdev-\u003eout[dpin-\u003eid].phase_stale = false;\n+\t}\n+\t*phase_adjust = sit9531x_out_state_get(sitdev, dpin-\u003eid)-\u003ephase_adj;\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * sit9531x_dpll_output_pin_phase_adjust_set - set output phase adjustment\n+ *\n+ * Programs the per-output PRG_RST_DELAY registers for deterministic\n+ * phase offset; see sit9531x_output_phase_adjust_set() in core.c.\n+ */\n+static int\n+sit9531x_dpll_output_pin_phase_adjust_set(const struct dpll_pin *pin,\n+\t\t\t\t\t void *pin_priv,\n+\t\t\t\t\t const struct dpll_device *dpll,\n+\t\t\t\t\t void *dpll_priv, s32 phase_adjust,\n+\t\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct sit9531x_dpll_pin *dpin = pin_priv;\n+\tstruct sit9531x_dpll *sitdpll = dpll_priv;\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\tint rc;\n+\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\trc = sit9531x_output_phase_adjust_set(sitdev, dpin-\u003eid, phase_adjust);\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\n+\tif (rc) {\n+\t\tNL_SET_ERR_MSG(extack, \"Phase adjust failed\");\n+\t\treturn rc;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static const struct dpll_pin_ops sit9531x_dpll_output_pin_ops = {\n+\t.direction_get\t\t= sit9531x_dpll_output_pin_direction_get,\n+\t.frequency_get\t\t= sit9531x_dpll_output_pin_frequency_get,\n+\t.frequency_set\t\t= sit9531x_dpll_output_pin_frequency_set,\n+\t.state_on_dpll_get\t= sit9531x_dpll_output_pin_state_on_dpll_get,\n+\t.state_on_dpll_set\t= sit9531x_dpll_output_pin_state_on_dpll_set,\n+\t.phase_adjust_get\t= sit9531x_dpll_output_pin_phase_adjust_get,\n+\t.phase_adjust_set\t= sit9531x_dpll_output_pin_phase_adjust_set,\n+};\n+\n+const struct dpll_pin_ops *\n+sit9531x_dpll_pin_ops_get(const struct sit9531x_dpll_pin *pin)\n+{\n+\tif (!sit9531x_dpll_is_input_pin(pin)) {\n+\t\tif (sit9531x_dpll_is_intsync_src_pin(pin))\n+\t\t\treturn \u0026sit9531x_dpll_intsync_src_pin_ops;\n+\t\treturn \u0026sit9531x_dpll_output_pin_ops;\n+\t}\n+\tif (sit9531x_dpll_is_intsync_pin(pin))\n+\t\treturn \u0026sit9531x_dpll_intsync_dst_pin_ops;\n+\tif (sit9531x_dpll_is_xo_pin(pin))\n+\t\treturn \u0026sit9531x_dpll_xo_pin_ops;\n+\t/*\n+\t * Seeded from the firmware node before the pin is registered and\n+\t * never written afterwards, so register and unregister agree.\n+\t */\n+\tif (!sit9531x_ref_state_get(pin-\u003edpll-\u003edev, pin-\u003eid)-\u003efreq)\n+\t\treturn \u0026sit9531x_dpll_input_pin_norate_ops;\n+\treturn \u0026sit9531x_dpll_input_pin_ops;\n+}\n+\n+/*\n+ * The state, operational state and priority of a selection-role pin as\n+ * the cache has them now: the baseline at registration, and what the poll\n+ * compares it against on every tick.\n+ *\n+ * Caller must hold sitdev-\u003emultiop_lock.\n+ */\n+static void sit9531x_dpll_pin_snapshot(struct sit9531x_dpll *sitdpll,\n+\t\t\t\t const struct sit9531x_dpll_pin *pin,\n+\t\t\t\t enum dpll_pin_state *state,\n+\t\t\t\t enum dpll_pin_operstate *operstate,\n+\t\t\t\t u8 *prio)\n+{\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\tu8 id = pin-\u003eid;\n+\n+\tif (id == SIT9531X_INTSYNC_PIN_ID \u0026\u0026 sitdev-\u003eintsync_src == sitdpll-\u003eid)\n+\t\t*state = DPLL_PIN_STATE_DISCONNECTED;\n+\telse\n+\t\tsit9531x_dpll_selection_state_get(sitdev, sitdpll, id, state);\n+\tsit9531x_dpll_selection_operstate_get(sitdev, sitdpll, id, operstate);\n+\tif (sit9531x_input_prio_get(sitdev, sitdpll-\u003eid,\n+\t\t\t\t sit9531x_input_hw_src(id), prio))\n+\t\t*prio = pin-\u003eprio;\n+}\n+\n+/*\n+ * sit9531x_dpll_pin_baseline - seed what the poll compares a pin against\n+ *\n+ * Called right after the pin is registered, under multiop_lock, so the\n+ * poll's first comparison is against what the create notification\n+ * carried. A baseline taken on the first tick instead would absorb\n+ * whatever moved between registration and that tick -- a lane losing its\n+ * signal, a table rewritten and re-seeded -- and leave userspace with the\n+ * snapshot from registration until the next change.\n+ */\n+void sit9531x_dpll_pin_baseline(struct sit9531x_dpll_pin *pin)\n+{\n+\tstruct sit9531x_dpll *sitdpll = pin-\u003edpll;\n+\n+\tlockdep_assert_held(\u0026sitdpll-\u003edev-\u003emultiop_lock);\n+\n+\tif (!sit9531x_dpll_is_selection_pin(pin))\n+\t\treturn;\n+\n+\tsit9531x_dpll_pin_snapshot(sitdpll, pin, \u0026pin-\u003epin_state,\n+\t\t\t\t \u0026pin-\u003eoperstate, \u0026pin-\u003eprio);\n+}\n+\n+/*\n+ * sit9531x_dpll_changes_check - check for state changes and notify\n+ *\n+ * Called from sit9531x_dev_periodic_work(). Compares current hardware\n+ * state against cached values and sends netlink notifications on changes.\n+ */\n+void sit9531x_dpll_changes_check(struct sit9531x_dpll *sitdpll)\n+{\n+\tenum dpll_lock_status_error status_error = DPLL_LOCK_STATUS_ERROR_NONE;\n+\tstruct sit9531x_dev *sitdev = sitdpll-\u003edev;\n+\tenum dpll_lock_status lock_status;\n+\tstruct sit9531x_dpll_pin *pin;\n+\tint rc;\n+\n+\trc = sit9531x_dpll_lock_status_get(sitdpll-\u003edpll_dev, sitdpll,\n+\t\t\t\t\t \u0026lock_status, \u0026status_error, NULL);\n+\tif (rc) {\n+\t\tdev_err(sitdev-\u003edev, \"Failed to get DPLL%u lock status: %d\\n\",\n+\t\t\tsitdpll-\u003eid, rc);\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * The core publishes the error detail alongside the status, so a\n+\t * change in either is a change subscribers have to be told about:\n+\t * an inner loss of lock appearing or clearing while the status\n+\t * stays UNLOCKED would otherwise be visible only to a later GET.\n+\t */\n+\tif (sitdpll-\u003elock_status != lock_status ||\n+\t sitdpll-\u003elock_status_error != status_error) {\n+\t\tsitdpll-\u003elock_status = lock_status;\n+\t\tsitdpll-\u003elock_status_error = status_error;\n+\t\tdpll_device_change_ntf(sitdpll-\u003edpll_dev);\n+\t}\n+\n+\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\tlist_for_each_entry(pin, \u0026sitdpll-\u003epins, list) {\n+\t\tenum dpll_pin_operstate operstate;\n+\t\tenum dpll_pin_state state;\n+\t\tbool changed;\n+\t\tu8 prio;\n+\n+\t\tif (!sit9531x_dpll_is_selection_pin(pin))\n+\t\t\tcontinue;\n+\n+\t\tsit9531x_dpll_pin_snapshot(sitdpll, pin, \u0026state, \u0026operstate,\n+\t\t\t\t\t \u0026prio);\n+\n+\t\tchanged = state != pin-\u003epin_state ||\n+\t\t\t operstate != pin-\u003eoperstate || prio != pin-\u003eprio;\n+\t\tif (changed)\n+\t\t\tdev_dbg(sitdev-\u003edev,\n+\t\t\t\t\"%s: state %u-\u003e%u operstate %u-\u003e%u prio %u-\u003e%u\\n\",\n+\t\t\t\tpin-\u003elabel, pin-\u003epin_state, state,\n+\t\t\t\tpin-\u003eoperstate, operstate, pin-\u003eprio, prio);\n+\n+\t\tpin-\u003epin_state = state;\n+\t\tpin-\u003eoperstate = operstate;\n+\t\tpin-\u003eprio = prio;\n+\n+\t\t/*\n+\t\t * The notification helper takes DPLL-subsystem locks that the\n+\t\t * callbacks run under, so it cannot be called with\n+\t\t * multiop_lock held; mark the pin and send after the walk.\n+\t\t */\n+\t\tif (changed)\n+\t\t\tpin-\u003entf_pending = true;\n+\t}\n+\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\n+\tlist_for_each_entry(pin, \u0026sitdpll-\u003epins, list) {\n+\t\tif (!pin-\u003entf_pending)\n+\t\t\tcontinue;\n+\t\tpin-\u003entf_pending = false;\n+\t\tdpll_pin_change_ntf(pin-\u003edpll_pin);\n+\t}\n+}\ndiff --git a/drivers/dpll/sit9531x/dpll.h b/drivers/dpll/sit9531x/dpll.h\nnew file mode 100644\nindex 0000000000000..c2706ced92cd1\n--- /dev/null\n+++ b/drivers/dpll/sit9531x/dpll.h\n@@ -0,0 +1,70 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/*\n+ * SiTime SiT9531x DPLL subsystem interface\n+ *\n+ * Copyright (C) 2026 SiTime Corp.\n+ * Author: Ali Rouhi \u003carouhi@sitime.com\u003e\n+ * Author: Oleg Zadorozhnyi \u003cOleg.Zadorozhnyi@devoxsoftware.com\u003e\n+ *\n+ * DPLL device and pin structures, and function declarations for\n+ * the DPLL registration and callback layer.\n+ */\n+\n+#ifndef _SIT9531X_DPLL_H\n+#define _SIT9531X_DPLL_H\n+\n+#include \u003clinux/dpll.h\u003e\n+#include \u003clinux/list.h\u003e\n+#include \u003clinux/types.h\u003e\n+\n+struct sit9531x_dev;\n+\n+/* Per-pin DPLL state. */\n+struct sit9531x_dpll_pin {\n+\tstruct list_head\t\tlist;\n+\tstruct sit9531x_dpll\t\t*dpll;\n+\tstruct dpll_pin\t\t\t*dpll_pin;\n+\tdpll_tracker\t\t\ttracker;\n+\tstruct fwnode_handle\t\t*fwnode;\n+\tchar\t\t\t\tlabel[8];\t/* \"IN0\", \"OUT3\" */\n+\tenum dpll_pin_direction\t\tdir;\n+\tu8\t\t\t\tid;\t\t/* hardware index */\n+\tu8\t\t\t\tprio;\n+\tenum dpll_pin_state\t\tpin_state;\n+\tenum dpll_pin_operstate\t\toperstate;\n+\tbool\t\t\t\tntf_pending;\n+};\n+\n+/* Per-PLL DPLL device state. */\n+struct sit9531x_dpll {\n+\tstruct list_head\t\tlist;\n+\tstruct sit9531x_dev\t\t*dev;\n+\tstruct dpll_device\t\t*dpll_dev;\n+\tdpll_tracker\t\t\ttracker;\n+\tstruct dpll_device_ops\t\tops;\t/* per-instance copy */\n+\tstruct list_head\t\tpins;\n+\tu8\t\t\t\tid;\t/* 0 = PLLA .. 3 = PLLD */\n+\tenum dpll_lock_status\t\tlock_status;\n+\tenum dpll_lock_status_error\tlock_status_error;\n+};\n+\n+/* ---- DPLL allocation and registration ---- */\n+/*\n+ * The callback tables stay with the callbacks; the registration code that\n+ * hands them to the subsystem lives next to probe() in core.c.\n+ */\n+extern const struct dpll_device_ops sit9531x_dpll_device_ops;\n+const struct dpll_pin_ops *\n+sit9531x_dpll_pin_ops_get(const struct sit9531x_dpll_pin *pin);\n+\n+struct sit9531x_dpll *sit9531x_dpll_alloc(struct sit9531x_dev *sitdev, u8 ch);\n+void sit9531x_dpll_free(struct sit9531x_dpll *sitdpll);\n+int sit9531x_dpll_register(struct sit9531x_dpll *sitdpll);\n+void sit9531x_dpll_unregister(struct sit9531x_dpll *sitdpll);\n+\n+/* ---- Periodic change detection ---- */\n+void sit9531x_dpll_lock_status_baseline(struct sit9531x_dpll *sitdpll);\n+void sit9531x_dpll_pin_baseline(struct sit9531x_dpll_pin *pin);\n+void sit9531x_dpll_changes_check(struct sit9531x_dpll *sitdpll);\n+\n+#endif /* _SIT9531X_DPLL_H */\ndiff --git a/drivers/dpll/sit9531x/prop.c b/drivers/dpll/sit9531x/prop.c\nnew file mode 100644\nindex 0000000000000..3aaafb0efd782\n--- /dev/null\n+++ b/drivers/dpll/sit9531x/prop.c\n@@ -0,0 +1,472 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * SiTime SiT9531x firmware node property parsing\n+ *\n+ * Copyright (C) 2026 SiTime Corp.\n+ * Author: Ali Rouhi \u003carouhi@sitime.com\u003e\n+ * Author: Oleg Zadorozhnyi \u003cOleg.Zadorozhnyi@devoxsoftware.com\u003e\n+ *\n+ * Retrieves pin properties from Device Tree firmware nodes (or\n+ * applies defaults when no firmware node exists).\n+ */\n+\n+#include \u003clinux/dev_printk.h\u003e\n+#include \u003clinux/dpll.h\u003e\n+#include \u003clinux/err.h\u003e\n+#include \u003clinux/fwnode.h\u003e\n+#include \u003clinux/property.h\u003e\n+#include \u003clinux/slab.h\u003e\n+#include \u003clinux/string.h\u003e\n+\n+#include \"core.h\"\n+#include \"prop.h\"\n+\n+/*\n+ * sit9531x_input_pin_label - fill the package label for an input pin\n+ *\n+ * Split out so input-naming changes stay local to this helper.\n+ */\n+static void sit9531x_input_pin_label(struct sit9531x_dev *sitdev,\n+\t\t\t\t struct sit9531x_pin_props *props, u8 id)\n+{\n+\tu8 pair = sit9531x_input_pair(id);\n+\n+\tif (sitdev-\u003eref[id].sig_mode == SIT9531X_MODE_DE)\n+\t\tsnprintf(props-\u003epackage_label,\n+\t\t\t sizeof(props-\u003epackage_label), \"IN%u\", pair);\n+\telse\n+\t\tsnprintf(props-\u003epackage_label,\n+\t\t\t sizeof(props-\u003epackage_label), \"IN%u%c\", pair,\n+\t\t\t sit9531x_input_is_n(id) ? 'N' : 'P');\n+}\n+\n+/*\n+ * sit9531x_prop_pin_package_label_set - generate package label\n+ * @dir:\tpin direction\n+ * @id:\t\tpin index\n+ *\n+ * Generates a package label string. Output pins are named \"OUT0\",\n+ * \"OUT1\", ... Input pins are named after the physical pair and lane:\n+ * \"IN0P\", \"IN0N\", \"IN1P\", ... for single-ended lanes, or \"IN0\",\n+ * \"IN1\", ... when the pair is configured differential (the N lane is\n+ * not registered in that case).\n+ */\n+static void\n+sit9531x_prop_pin_package_label_set(struct sit9531x_dev *sitdev,\n+\t\t\t\t struct sit9531x_pin_props *props,\n+\t\t\t\t enum dpll_pin_direction dir, u8 id)\n+{\n+\t/* The internal INTSYNC pin has a fixed label */\n+\tif (dir == DPLL_PIN_DIRECTION_INPUT \u0026\u0026\n+\t id == SIT9531X_INTSYNC_PIN_ID) {\n+\t\tstrscpy(props-\u003epackage_label, \"INTSYNC\",\n+\t\t\tsizeof(props-\u003epackage_label));\n+\t\tprops-\u003edpll_props.package_label = props-\u003epackage_label;\n+\t\treturn;\n+\t}\n+\n+\t/* The internal XO reference has a fixed label */\n+\tif (dir == DPLL_PIN_DIRECTION_INPUT \u0026\u0026 id == SIT9531X_MAX_INPUTS) {\n+\t\tstrscpy(props-\u003epackage_label, \"XO\",\n+\t\t\tsizeof(props-\u003epackage_label));\n+\t\tprops-\u003edpll_props.package_label = props-\u003epackage_label;\n+\t\treturn;\n+\t}\n+\n+\t/* The internal INTSYNC source (output) pin has a fixed label */\n+\tif (dir == DPLL_PIN_DIRECTION_OUTPUT \u0026\u0026\n+\t id == SIT9531X_INTSYNC_OUT_PIN_ID) {\n+\t\tstrscpy(props-\u003epackage_label, \"SYNCOUT\",\n+\t\t\tsizeof(props-\u003epackage_label));\n+\t\tprops-\u003edpll_props.package_label = props-\u003epackage_label;\n+\t\treturn;\n+\t}\n+\n+\tif (dir == DPLL_PIN_DIRECTION_INPUT)\n+\t\tsit9531x_input_pin_label(sitdev, props, id);\n+\telse\n+\t\t/*\n+\t\t * Name the chip pin, not the driver's index for it. The\n+\t\t * two differ on the variant whose outputs are bonded out\n+\t\t * from a subset of the twelve slots, and a package label\n+\t\t * that named the index would point at a pin that is not\n+\t\t * the one being driven.\n+\t\t */\n+\t\tsnprintf(props-\u003epackage_label, sizeof(props-\u003epackage_label),\n+\t\t\t \"OUT%u\", sitdev-\u003einfo-\u003eclkout_map[id]);\n+\n+\tprops-\u003edpll_props.package_label = props-\u003epackage_label;\n+}\n+\n+/*\n+ * sit9531x_prop_pin_fwnode_get - find firmware node for a pin\n+ * @dir:\tpin direction\n+ * @id:\t\tpin index\n+ *\n+ * Searches for input-pins/output-pins child nodes in DT, looking for a\n+ * child whose \"reg\" property matches the pin. The binding describes reg\n+ * as the hardware index, so an output is matched by the chip slot it\n+ * drives rather than by the driver's index for it: on the variant where\n+ * the two differ, a board describing the pin it wired would otherwise\n+ * have its properties applied to a different one.\n+ *\n+ * Return: 0 on success, -ENOENT if no firmware node exists\n+ */\n+static int\n+sit9531x_prop_pin_fwnode_get(struct sit9531x_dev *sitdev,\n+\t\t\t struct sit9531x_pin_props *props,\n+\t\t\t enum dpll_pin_direction dir, u8 id)\n+{\n+\tstruct fwnode_handle *pins_node, *pin_node;\n+\tconst char *node_name;\n+\n+\tif (dir == DPLL_PIN_DIRECTION_INPUT) {\n+\t\tnode_name = \"input-pins\";\n+\t} else {\n+\t\tnode_name = \"output-pins\";\n+\t\tif (id \u003c sitdev-\u003einfo-\u003enum_outputs)\n+\t\t\tid = sitdev-\u003einfo-\u003eclkout_map[id];\n+\t}\n+\n+\tpins_node = device_get_named_child_node(sitdev-\u003edev, node_name);\n+\tif (!pins_node) {\n+\t\tdev_dbg(sitdev-\u003edev, \"'%s' sub-node is missing\\n\", node_name);\n+\t\treturn -ENOENT;\n+\t}\n+\n+\t/* Enumerate child pin nodes and find the requested one */\n+\tfwnode_for_each_child_node(pins_node, pin_node) {\n+\t\tu32 reg;\n+\n+\t\tif (fwnode_property_read_u32(pin_node, \"reg\", \u0026reg))\n+\t\t\tcontinue;\n+\n+\t\tif (id == reg)\n+\t\t\tbreak;\n+\t}\n+\n+\tfwnode_handle_put(pins_node);\n+\n+\tprops-\u003efwnode = pin_node;\n+\n+\tdev_dbg(sitdev-\u003edev, \"Firmware node for %s %sfound\\n\",\n+\t\tprops-\u003epackage_label, pin_node ? \"\" : \"NOT \");\n+\n+\treturn pin_node ? 0 : -ENOENT;\n+}\n+\n+/*\n+ * sit9531x_pin_props_get - get pin properties for a given pin\n+ * @dir:\tpin direction (INPUT or OUTPUT)\n+ * @index:\tpin index\n+ *\n+ * Allocates a pin properties structure, generates a package label,\n+ * looks up the firmware node if available, and reads optional\n+ * properties (label, connection-type, supported-frequencies-hz).\n+ *\n+ * Call sit9531x_pin_props_put() to free the returned structure.\n+ *\n+ * Return: pointer to pin properties on success, error pointer on error\n+ */\n+struct sit9531x_pin_props *\n+sit9531x_pin_props_get(struct sit9531x_dev *sitdev,\n+\t\t enum dpll_pin_direction dir, u8 index)\n+{\n+\tstruct dpll_pin_frequency *ranges;\n+\tstruct sit9531x_pin_props *props;\n+\tint i, j, num_freqs = 0, rc;\n+\tu64 *freqs = NULL;\n+\tconst char *type;\n+\tu64 curr_freq;\n+\n+\tprops = kzalloc_obj(*props, GFP_KERNEL);\n+\tif (!props)\n+\t\treturn ERR_PTR(-ENOMEM);\n+\n+\tif (dir == DPLL_PIN_DIRECTION_INPUT \u0026\u0026\n+\t index == SIT9531X_INTSYNC_PIN_ID) {\n+\t\t/*\n+\t\t * INTSYNC destination pin: a PLL locks to the INTSYNC net as a\n+\t\t * reference, so it can be connected and re-prioritised.\n+\t\t */\n+\t\tprops-\u003edpll_props.type = DPLL_PIN_TYPE_INT_OSCILLATOR;\n+\t\tprops-\u003edpll_props.capabilities =\n+\t\t\tDPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE |\n+\t\t\tDPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE;\n+\t\tcurr_freq = 0;\n+\t} else if (dir == DPLL_PIN_DIRECTION_OUTPUT \u0026\u0026\n+\t\t index == SIT9531X_INTSYNC_OUT_PIN_ID) {\n+\t\t/*\n+\t\t * INTSYNC source pin: a PLL drives the INTSYNC net. It can be\n+\t\t * connected/disconnected but carries no priority (driving the\n+\t\t * net is not a reference selection) and no frequency.\n+\t\t */\n+\t\tprops-\u003edpll_props.type = DPLL_PIN_TYPE_INT_OSCILLATOR;\n+\t\tprops-\u003edpll_props.capabilities =\n+\t\t\tDPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE;\n+\t\tcurr_freq = 0;\n+\t} else if (dir == DPLL_PIN_DIRECTION_INPUT \u0026\u0026\n+\t\t index == SIT9531X_MAX_INPUTS) {\n+\t\t/* The XO reference is fixed: no state or priority control. */\n+\t\tprops-\u003edpll_props.type = DPLL_PIN_TYPE_INT_OSCILLATOR;\n+\t\tprops-\u003edpll_props.capabilities = 0;\n+\t\tsitdev-\u003eref[index].freq = sitdev-\u003extal_freq;\n+\t\tcurr_freq = sitdev-\u003extal_freq;\n+\t} else if (dir == DPLL_PIN_DIRECTION_INPUT) {\n+\t\tprops-\u003edpll_props.type = DPLL_PIN_TYPE_EXT;\n+\t\tprops-\u003edpll_props.capabilities =\n+\t\t\tDPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE |\n+\t\t\tDPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE;\n+\t\tcurr_freq = sitdev-\u003eref[index].freq;\n+\t} else {\n+\t\t/*\n+\t\t * A synthesized clock output is an external connection with\n+\t\t * no more specific meaning; a board that knows better says\n+\t\t * so through the pin's connection-type property below.\n+\t\t */\n+\t\tprops-\u003edpll_props.type = DPLL_PIN_TYPE_EXT;\n+\t\tprops-\u003edpll_props.capabilities =\n+\t\t\tDPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE;\n+\t\tcurr_freq = sitdev-\u003eout[index].freq;\n+\n+\t\t/*\n+\t\t * Allow phase-adjust over a +/-1 ms window. The subsystem\n+\t\t * rejects pin_set(phase-adjust, X) when X falls outside\n+\t\t * [min, max], so leaving these at 0 silently blocks every\n+\t\t * netlink call. The device holds a delay anywhere within\n+\t\t * the output period, so the bound is the s32 picosecond\n+\t\t * attribute, not the hardware; 1 ms is a round figure below\n+\t\t * it that costs nothing. Only outputs get a range: input\n+\t\t * pins have no .phase_adjust_set, and advertising one there\n+\t\t * would promise userspace something every set would refuse.\n+\t\t */\n+\t\tprops-\u003edpll_props.phase_range.min =\n+\t\t\t-SIT9531X_OUT_PHASE_ADJ_MAX_PS;\n+\t\tprops-\u003edpll_props.phase_range.max =\n+\t\t\tSIT9531X_OUT_PHASE_ADJ_MAX_PS;\n+\t\t/*\n+\t\t * The fine step is 30 ps, but requests are accepted at 1 ps\n+\t\t * resolution and rounded to the nearest achievable delay, so\n+\t\t * advertise the request granularity, not the hardware step.\n+\t\t */\n+\t\tprops-\u003edpll_props.phase_gran = 1;\n+\t}\n+\n+\t/* Generate package label */\n+\tsit9531x_prop_pin_package_label_set(sitdev, props, dir, index);\n+\n+\t/*\n+\t * Both INTSYNC pins are internal to the chip and have no board-level\n+\t * wiring, so they take no properties from the firmware node.\n+\t */\n+\tif (dir == DPLL_PIN_DIRECTION_INPUT \u0026\u0026\n+\t (index == SIT9531X_INTSYNC_PIN_ID ||\n+\t index == SIT9531X_MAX_INPUTS))\n+\t\tgoto skip_fwnode_props;\n+\tif (dir == DPLL_PIN_DIRECTION_OUTPUT \u0026\u0026\n+\t index == SIT9531X_INTSYNC_OUT_PIN_ID)\n+\t\tgoto skip_fwnode_props;\n+\n+\trc = sit9531x_prop_pin_fwnode_get(sitdev, props, dir, index);\n+\tif (rc)\n+\t\tgoto skip_fwnode_props;\n+\n+\t/* Look for \"label\" property -\u003e board label */\n+\tfwnode_property_read_string(props-\u003efwnode, \"label\",\n+\t\t\t\t \u0026props-\u003edpll_props.board_label);\n+\n+\t/* Look for \"connection-type\" property -\u003e pin type enum */\n+\tif (!fwnode_property_read_string(props-\u003efwnode, \"connection-type\",\n+\t\t\t\t\t \u0026type)) {\n+\t\tif (!strcmp(type, \"ext\"))\n+\t\t\tprops-\u003edpll_props.type = DPLL_PIN_TYPE_EXT;\n+\t\telse if (!strcmp(type, \"gnss\"))\n+\t\t\tprops-\u003edpll_props.type = DPLL_PIN_TYPE_GNSS;\n+\t\telse if (!strcmp(type, \"int\"))\n+\t\t\tprops-\u003edpll_props.type = DPLL_PIN_TYPE_INT_OSCILLATOR;\n+\t\telse if (!strcmp(type, \"synce\"))\n+\t\t\tprops-\u003edpll_props.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;\n+\t\telse if (!strcmp(type, \"mux\"))\n+\t\t\tprops-\u003edpll_props.type = DPLL_PIN_TYPE_MUX;\n+\t\telse\n+\t\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t\t \"Unknown pin type '%s'\\n\", type);\n+\t}\n+\n+\tnum_freqs = fwnode_property_count_u64(props-\u003efwnode,\n+\t\t\t\t\t \"supported-frequencies-hz\");\n+\tif (num_freqs \u003c= 0) {\n+\t\tnum_freqs = 0;\n+\t\tgoto skip_fwnode_props;\n+\t}\n+\n+\tfreqs = kcalloc(num_freqs, sizeof(*freqs), GFP_KERNEL);\n+\tif (!freqs) {\n+\t\trc = -ENOMEM;\n+\t\tgoto err_alloc_freqs;\n+\t}\n+\n+\trc = fwnode_property_read_u64_array(props-\u003efwnode,\n+\t\t\t\t\t \"supported-frequencies-hz\",\n+\t\t\t\t\t freqs, num_freqs);\n+\tif (rc) {\n+\t\tdev_warn(sitdev-\u003edev,\n+\t\t\t \"failed to parse supported-frequencies-hz for %s: %d\\n\",\n+\t\t\t props-\u003epackage_label, rc);\n+\t\tgoto err_alloc_ranges;\n+\t}\n+\n+\t/*\n+\t * Seed the runtime ref-\u003efreq with the first DT-listed supported\n+\t * frequency: an input's rate is a board fact the device cannot be\n+\t * asked for, so firmware is the only source. An output is left to\n+\t * the read-back below, which knows what the divider is actually\n+\t * doing.\n+\t */\n+\tif (num_freqs \u003e 0 \u0026\u0026 dir == DPLL_PIN_DIRECTION_INPUT \u0026\u0026\n+\t index != SIT9531X_MAX_INPUTS)\n+\t\tcurr_freq = freqs[0];\n+\n+\t/*\n+\t * An output's current rate is the one its divider produces, so read\n+\t * it rather than assume the first entry of a list of the rates the\n+\t * board supports is the one in force. A rate taken from firmware\n+\t * that the part is not running would be reported as current and,\n+\t * worse, used as the output period the phase adjust quantizes\n+\t * against. An output the configuration does not route has no rate\n+\t * to read, which is not an error.\n+\t */\n+\tif (dir == DPLL_PIN_DIRECTION_OUTPUT \u0026\u0026\n+\t index \u003c sitdev-\u003einfo-\u003enum_outputs) {\n+\t\tu64 hw_freq;\n+\n+\t\tmutex_lock(\u0026sitdev-\u003emultiop_lock);\n+\t\trc = sit9531x_output_freq_get(sitdev, index, \u0026hw_freq);\n+\t\tmutex_unlock(\u0026sitdev-\u003emultiop_lock);\n+\t\tif (!rc)\n+\t\t\tcurr_freq = hw_freq;\n+\t}\n+\n+skip_fwnode_props:\n+\t/* Neither INTSYNC pin carries a frequency attribute */\n+\tif (dir == DPLL_PIN_DIRECTION_INPUT \u0026\u0026\n+\t index == SIT9531X_INTSYNC_PIN_ID)\n+\t\treturn props;\n+\tif (dir == DPLL_PIN_DIRECTION_OUTPUT \u0026\u0026\n+\t index == SIT9531X_INTSYNC_OUT_PIN_ID)\n+\t\treturn props;\n+\n+\t/*\n+\t * Advertise only concrete values from firmware plus current runtime\n+\t * value. For outputs without a firmware list, publish one wide range as\n+\t * an explicit fallback because those pins do support frequency_set.\n+\t */\n+\tranges = kcalloc(num_freqs + 2, sizeof(*ranges), GFP_KERNEL);\n+\tif (!ranges) {\n+\t\trc = -ENOMEM;\n+\t\tgoto err_alloc_ranges;\n+\t}\n+\n+\t/*\n+\t * Publish the seeded rate only once the pin is certain to be built.\n+\t * The allocation above is the last thing that can fail, and a call\n+\t * that reports failure must not leave the device's cached rate\n+\t * changed behind it.\n+\t */\n+\tif (curr_freq) {\n+\t\tif (dir == DPLL_PIN_DIRECTION_INPUT)\n+\t\t\tsitdev-\u003eref[index].freq = curr_freq;\n+\t\telse\n+\t\t\tsitdev-\u003eout[index].freq = curr_freq;\n+\t}\n+\n+\tj = 0;\n+\n+\t/* Current frequency first, when known. */\n+\tif (curr_freq) {\n+\t\tstruct dpll_pin_frequency f = DPLL_PIN_FREQUENCY(curr_freq);\n+\n+\t\tranges[j++] = f;\n+\t}\n+\n+\tfor (i = 0; i \u003c num_freqs; i++) {\n+\t\tstruct dpll_pin_frequency freq = DPLL_PIN_FREQUENCY(freqs[i]);\n+\n+\t\tif (freqs[i] == curr_freq)\n+\t\t\tcontinue;\n+\t\tranges[j++] = freq;\n+\t}\n+\n+\tif (dir == DPLL_PIN_DIRECTION_OUTPUT \u0026\u0026 num_freqs == 0) {\n+\t\tranges[j].min = 1;\n+\t\tranges[j].max = 1000000000ULL; /* 1 GHz */\n+\t\tj++;\n+\t}\n+\n+\tif (j \u003e 0) {\n+\t\tprops-\u003edpll_props.freq_supported = ranges;\n+\t\tprops-\u003edpll_props.freq_supported_num = j;\n+\t} else {\n+\t\tkfree(ranges);\n+\t\tprops-\u003edpll_props.freq_supported = NULL;\n+\t\tprops-\u003edpll_props.freq_supported_num = 0;\n+\t}\n+\n+\tkfree(freqs);\n+\n+\treturn props;\n+\n+err_alloc_ranges:\n+\tkfree(freqs);\n+err_alloc_freqs:\n+\tfwnode_handle_put(props-\u003efwnode);\n+\tkfree(props);\n+\n+\treturn ERR_PTR(rc);\n+}\n+\n+/*\n+ * sit9531x_pin_props_put - release pin properties\n+ * @props:\tpin properties to free\n+ */\n+void sit9531x_pin_props_put(struct sit9531x_pin_props *props)\n+{\n+\tkfree(props-\u003edpll_props.freq_supported);\n+\n+\tif (props-\u003efwnode)\n+\t\tfwnode_handle_put(props-\u003efwnode);\n+\n+\tkfree(props);\n+}\n+\n+/*\n+ * sit9531x_prop_dpll_type_get - get DPLL channel type from firmware\n+ * @index:\tDPLL channel index (0-3)\n+ *\n+ * Reads the \"dpll-types\" string array property from the firmware node\n+ * and returns the corresponding DPLL type enum.\n+ *\n+ * Return: DPLL type for the given channel (default: DPLL_TYPE_PPS)\n+ */\n+enum dpll_type\n+sit9531x_prop_dpll_type_get(struct sit9531x_dev *sitdev, u8 index)\n+{\n+\tconst char *types[SIT9531X_NUM_PLLS];\n+\tint count;\n+\n+\tcount = device_property_read_string_array(sitdev-\u003edev, \"dpll-types\",\n+\t\t\t\t\t\t types, ARRAY_SIZE(types));\n+\n+\tif (index \u003e= count)\n+\t\treturn DPLL_TYPE_PPS;\n+\n+\tif (!strcmp(types[index], \"pps\"))\n+\t\treturn DPLL_TYPE_PPS;\n+\telse if (!strcmp(types[index], \"eec\"))\n+\t\treturn DPLL_TYPE_EEC;\n+\n+\tdev_warn(sitdev-\u003edev, \"Unknown DPLL type '%s', using default\\n\",\n+\t\t types[index]);\n+\n+\treturn DPLL_TYPE_PPS;\n+}\ndiff --git a/drivers/dpll/sit9531x/prop.h b/drivers/dpll/sit9531x/prop.h\nnew file mode 100644\nindex 0000000000000..a65efdfd9ef8a\n--- /dev/null\n+++ b/drivers/dpll/sit9531x/prop.h\n@@ -0,0 +1,37 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/*\n+ * SiTime SiT9531x firmware node property parsing\n+ *\n+ * Copyright (C) 2026 SiTime Corp.\n+ * Author: Ali Rouhi \u003carouhi@sitime.com\u003e\n+ * Author: Oleg Zadorozhnyi \u003cOleg.Zadorozhnyi@devoxsoftware.com\u003e\n+ */\n+\n+#ifndef _SIT9531X_PROP_H\n+#define _SIT9531X_PROP_H\n+\n+#include \u003clinux/dpll.h\u003e\n+#include \u003clinux/fwnode.h\u003e\n+\n+struct sit9531x_dev;\n+\n+/*\n+ * struct sit9531x_pin_props - pin properties from firmware\n+ * @fwnode:\t\tfirmware node handle (NULL if no DT node)\n+ * @dpll_props:\t\tDPLL core pin properties\n+ * @package_label:\tpin package label (e.g. \"IN0\", \"OUT3\")\n+ */\n+struct sit9531x_pin_props {\n+\tstruct fwnode_handle\t\t*fwnode;\n+\tstruct dpll_pin_properties\tdpll_props;\n+\tchar\t\t\t\tpackage_label[8];\n+};\n+\n+enum dpll_type sit9531x_prop_dpll_type_get(struct sit9531x_dev *sitdev,\n+\t\t\t\t\t u8 index);\n+struct sit9531x_pin_props *sit9531x_pin_props_get(struct sit9531x_dev *sitdev,\n+\t\t\t\t\t\t enum dpll_pin_direction dir,\n+\t\t\t\t\t\t u8 index);\n+void sit9531x_pin_props_put(struct sit9531x_pin_props *props);\n+\n+#endif /* _SIT9531X_PROP_H */\ndiff --git a/drivers/dpll/sit9531x/regs.h b/drivers/dpll/sit9531x/regs.h\nnew file mode 100644\nindex 0000000000000..601dafbb21792\n--- /dev/null\n+++ b/drivers/dpll/sit9531x/regs.h\n@@ -0,0 +1,425 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/*\n+ * SiTime SiT9531x register definitions\n+ *\n+ * Copyright (C) 2026 SiTime Corp.\n+ * Author: Ali Rouhi \u003carouhi@sitime.com\u003e\n+ * Author: Oleg Zadorozhnyi \u003cOleg.Zadorozhnyi@devoxsoftware.com\u003e\n+ */\n+\n+#ifndef _SIT9531X_REGS_H\n+#define _SIT9531X_REGS_H\n+\n+/*\n+ * I2C register model:\n+ * - Page select register at offset 0xFF, present in every page\n+ * - Each page has 256 registers (0x00-0xFF)\n+ * - Some pages are paired (e.g. 0x0A/0x1A for PLLA)\n+ */\n+#define SIT9531X_PAGE_SEL\t\t0xFF\n+#define SIT9531X_PAGE_SIZE\t\t0x100\n+#define SIT9531X_NUM_PAGES\t\t32\n+\n+/* Helper macros for page:offset addressing */\n+#define SIT9531X_REG(_page, _offset)\t\t(((_page) \u003c\u003c 8) | (_offset))\n+#define SIT9531X_REG_PAGE(_reg)\t\t((_reg) \u003e\u003e 8)\n+#define SIT9531X_REG_OFFSET(_reg)\t\t((_reg) \u0026 0xFF)\n+\n+#define SIT9531X_PAGE_OUTSYS0\t\t0x03\n+#define SIT9531X_PAGE_OUTSYS1\t\t0x04\n+#define SIT9531X_PAGE_PLLA\t\t\t0x0A\n+#define SIT9531X_PAGE_PLLA_EXT\t\t0x1A\n+\n+/*\n+ * VARIANT_ID is a single byte at page 0 reg 0x02 (95317 = 0x17, 95316 = 0x31).\n+ * Reg 0x03 carries an unrelated revision byte and must not be combined into\n+ * the variant identifier.\n+ */\n+#define SIT9531X_REG_VARIANT_ID\t\tSIT9531X_REG(0x00, 0x02)\n+\n+/* DCO trigger pulse timing: minimum 6 ns required by hardware */\n+\n+/* Page 0 -- PLL inner loop loss-of-lock */\n+#define SIT9531X_REG_PLL_INNER_LOL_STATUS\tSIT9531X_REG(0x00, 0x92)\n+#define SIT9531X_REG_PLL_INNER_LOL_NOTIF\tSIT9531X_REG(0x00, 0x93)\n+\n+/* Page 0 -- Clock monitor PLL / XO status */\n+#define SIT9531X_REG_CMON_NOTIF\t\tSIT9531X_REG(0x00, 0x9E)\n+\n+/* Page 0 -- PLL outer-loop loss-of-lock */\n+#define SIT9531X_REG_OUTER_LOL_STATUS\tSIT9531X_REG(0x00, 0x06)\n+#define SIT9531X_REG_OUTER_LOL_NOTIF\t\tSIT9531X_REG(0x00, 0x07)\n+\n+/* Page 0 -- PLL holdover freeze status */\n+#define SIT9531X_REG_HO_FREEZE_STATUS\tSIT9531X_REG(0x00, 0x0A)\n+#define SIT9531X_REG_HO_FREEZE_NOTIF\tSIT9531X_REG(0x00, 0x0B)\n+\n+/* Page 0 -- INTSYNC (inter-PLL synchronization) global enable */\n+#define SIT9531X_REG_INTSYNC_GLOBAL\t\tSIT9531X_REG(0x00, 0x40)\n+#define SIT9531X_INTSYNC_EN_BIT\t\t6\n+\n+/*\n+ * Priority table: 6 registers per PLL, each holds two priority slots\n+ * nibble-packed. The register holding slots 2n and 2n+1 keeps the\n+ * earlier slot (CLK_SPARE\u003c2n\u003eSEL_PLL) in [7:4] and the later one in\n+ * [3:0].\n+ *\n+ * Base registers for PLLA: 0x16-0x1B (slots 0-10 plus the\n+ * active-reference nibble).\n+ * For PLL N: base + 6 * N (e.g. PLLB starts at 0x1C).\n+ *\n+ * Input source encoding (4-bit value):\n+ * 0=IN0P, 1=IN1P, 2=IN2P, 3=IN3P,\n+ * 5=OCXO, 6=INTSYNC,\n+ * 7=IN0N, 8=IN1N, 9=IN2N, 10=IN3N\n+ *\n+ * Codes 4 and 11 address a fifth input pair that this part does\n+ * not have. They read back as no valid reference.\n+ */\n+#define SIT9531X_PAGE_PRIOSYS\t\t0x01\n+#define SIT9531X_PRIO_BASE_REG\t\t0x16\n+#define SIT9531X_PRIO_REGS_PER_PLL\t\t6\n+#define SIT9531X_PRIO_SLOTS_PER_REG\t\t2\n+/*\n+ * 11 priority slots, CLK_SPARE0SEL_PLL through CLK_SPARE10SEL_PLL.\n+ * The twelfth nibble of the block is not a slot: it is\n+ * CLK_ACTIVESEL_PLL, see SIT9531X_PRIO_ACTIVESEL_OFF below.\n+ */\n+#define SIT9531X_PRIO_MAX_SLOTS\t\t11\n+/* Number of source encodings (0-11), unrelated to the slot count */\n+#define SIT9531X_PRIO_NUM_SRC\t\t12\n+#define SIT9531X_PRIO_NIBBLE_MASK\t\t0x0F\n+#define SIT9531X_PRIO_HI_SHIFT\t\t4\n+/* Input source encoding values (see table above) */\n+/*\n+ * Either of the two codes for the absent fifth pair works as \"no source\";\n+ * the driver writes this one when it empties a slot.\n+ */\n+#define SIT9531X_PRIO_SRC_NONE\t\t\t4\n+#define SIT9531X_PRIO_SRC_OCXO\t\t5\n+#define SIT9531X_PRIO_SRC_INTSYNC\t\t6\n+#define SIT9531X_PRIO_SRC_N_BASE\t\t7\n+/*\n+ * The last register of each PLL's priority block holds, in its low\n+ * nibble, the input source the PLL has currently selected as its\n+ * active reference (CLK_ACTIVESEL_PLL, same 4-bit encoding as above).\n+ */\n+#define SIT9531X_PRIO_ACTIVESEL_OFF\t\t5\n+\n+/*\n+ * Page 0 -- PRG_Directives_GENERIC_0, the main system's programming\n+ * directive register. Every page carries its own copy of this\n+ * register at offset 0x0F with the same bit layout:\n+ *\n+ * bit 6 proceed to loop lock / active state from the PRG_CMD state\n+ * bit 4 update the NVM bank from the efuse contents\n+ * bit 3 read the efuse into the volatile registers\n+ * bit 2 program the efuse\n+ * bit 1 small change update (SIT9531X_SMALL_UPDATE_CMD)\n+ * bit 0 escape to the PRG_CMD state\n+ *\n+ * The NVM bank is a volatile shadow, so bits 4 and 1 are both fine in\n+ * a runtime path: bit 1 for a change made in the active state, bit 4\n+ * to close a PRG_CMD sequence. Only bit 2 writes non-volatile\n+ * storage, and the driver never issues it.\n+ */\n+#define SIT9531X_REG_GLOBAL_UPDATE\t\tSIT9531X_REG(0x00, 0x0F)\n+#define SIT9531X_SMALL_UPDATE_CMD\t\t0x02\n+\n+/* PLL holdover control (PLL page offset) */\n+#define SIT9531X_PLL_REG_HO_CTRL\t\t0x6F\n+#define SIT9531X_PLL_HO_FORCE_BIT\t\t4\n+\n+/* One bit per input PAIR (bit 0 = CLKIN0, ..., bit 3 = CLKIN3) */\n+#define SIT9531X_REG_IN_DE_FORCE\t\tSIT9531X_REG(0x02, 0xE8)\n+#define SIT9531X_REG_IN_DE_STATE\t\tSIT9531X_REG(0x02, 0xE9)\n+#define SIT9531X_REG_IN_SEP_FORCE\t\tSIT9531X_REG(0x02, 0xEA)\n+#define SIT9531X_REG_IN_SEP_STATE\t\tSIT9531X_REG(0x02, 0xEB)\n+#define SIT9531X_REG_IN_SEN_FORCE\t\tSIT9531X_REG(0x02, 0xF2)\n+#define SIT9531X_REG_IN_SEN_STATE\t\tSIT9531X_REG(0x02, 0xF3)\n+\n+/*\n+ * One register per input pair at 0x1B + 0x10 * pair\n+ * (CLKIN0 = 0x1B, CLKIN1 = 0x2B, CLKIN2 = 0x3B, CLKIN3 = 0x4B).\n+ * SE_P_EN/SE_N_EN set means the corresponding lane is configured\n+ * single-ended; both clear means the pair runs differential.\n+ */\n+#define SIT9531X_REG_IN_MODE(_pair)\t\t\\\n+\tSIT9531X_REG(0x02, 0x1B + 0x10 * (_pair))\n+#define SIT9531X_IN_MODE_SE_P_EN\t\tBIT(0)\n+#define SIT9531X_IN_MODE_SE_N_EN\t\tBIT(1)\n+\n+/* ---- Page 0x03 (Output System) registers -- Hi-Z control ---- */\n+#define SIT9531X_REG_HIZ_DIFF_07_MASK\tSIT9531X_REG(0x03, 0xF2)\n+#define SIT9531X_REG_HIZ_DIFF_07_STATE\tSIT9531X_REG(0x03, 0xF3)\n+#define SIT9531X_REG_HIZ_DIFF_811_MASK\tSIT9531X_REG(0x03, 0xF4)\n+#define SIT9531X_REG_HIZ_DIFF_811_STATE\tSIT9531X_REG(0x03, 0xF5)\n+#define SIT9531X_REG_HIZ_SE_07_MASK\t\tSIT9531X_REG(0x03, 0xF8)\n+#define SIT9531X_REG_HIZ_SE_07_STATE\t\tSIT9531X_REG(0x03, 0xF9)\n+#define SIT9531X_REG_HIZ_SE_811_MASK\t\tSIT9531X_REG(0x03, 0xFA)\n+#define SIT9531X_REG_HIZ_SE_811_STATE\tSIT9531X_REG(0x03, 0xFB)\n+/*\n+ * The SE pairs above force the OutP pad of a CMOS output. The OutN pad\n+ * has its own: 0xF6/0xF7 for slots 0-7, and for slots 8-11 the upper\n+ * nibble of the differential pair, 0xF4/0xF5 bits 7:4.\n+ */\n+#define SIT9531X_REG_HIZ_SEN_07_MASK\tSIT9531X_REG(0x03, 0xF6)\n+#define SIT9531X_REG_HIZ_SEN_07_STATE\tSIT9531X_REG(0x03, 0xF7)\n+#define SIT9531X_HIZ_SEN_811_SHIFT\t\t4\n+\n+/*\n+ * Output driver configuration. Either CMOS enable means the output\n+ * is wired single-ended -- one lane, or both driven as CMOS; with\n+ * neither set it is a differential pair.\n+ */\n+#define SIT9531X_OUT_MISC0_BASE\t\t0x1E\n+#define SIT9531X_OUT_MISC0_STRIDE\t\t0x10\n+#define SIT9531X_OUT_CMOS_ENP\t\tBIT(3)\n+#define SIT9531X_OUT_CMOS_ENN\t\tBIT(2)\n+\n+/*\n+ * Output divider registers in Pages 3/4. Each output has a 34-bit\n+ * integer divider mapped to 5 bytes (LSB at base reg, MSB at base-4).\n+ * Outputs 0-5 are on Page 3, outputs 6-11 are on Page 4.\n+ *\n+ * The base register for slot N within a page is:\n+ * clkout_odr_divn_base[slot] = { 0x14, 0x24, 0x34, 0x44, 0x54, 0x64 }\n+ *\n+ * Layout: base=LSB, base-1, base-2, base-3, base-4[1:0]=MSB.\n+ *\n+ * Per-chip clkout_map[] translates output index to slot position.\n+ */\n+#define SIT9531X_PAGE_OUTSYS0_SLOT_MAX\t5 /* slots 0-5 on Page 0x03 */\n+\n+/* Misc output system registers */\n+#define SIT9531X_REG_PRG_DIR_GEN\t\tSIT9531X_REG(0x03, 0x0F)\n+#define SIT9531X_PRG_CMD_STATE\t\t0x01\n+#define SIT9531X_UPDATE_NVM\t\t\t0x10\n+#define SIT9531X_LOOP_LOCK\t\t\t0x40\n+\n+/* Debug register (same offset, per-page) */\n+#define SIT9531X_REG_OUTSYS_DEBUG\t\tSIT9531X_REG(0x03, 0xBD)\n+#define SIT9531X_DEBUG_UNLOCK_VAL\t\t0xC3\n+#define SIT9531X_DEBUG_LOCK_VAL\t\t\t0x00\n+\n+/*\n+ * Per-output programmable phase delay: 34-bit coarse (in VCO clock\n+ * cycles) plus a 3-bit fine field with fixed 30 ps steps. Each output\n+ * has a five-byte block PROG6..PROG2:\n+ *\n+ * base + 0 PROG6 [7:5] OPSTG_VCASC_BUMP (preserve via RMW)\n+ * [4:2] PRG_RST_FINE_DELAY[2:0]\n+ * [1:0] PRG_RST_DELAY[33:32]\n+ * base + 1 PROG5 [7:0] PRG_RST_DELAY[31:24]\n+ * base + 2 PROG4 [7:0] PRG_RST_DELAY[23:16]\n+ * base + 3 PROG3 [7:0] PRG_RST_DELAY[15:8]\n+ * base + 4 PROG2 [7:0] PRG_RST_DELAY[7:0]\n+ *\n+ * Slots 0-5 are on Page 3, slots 6-11 on Page 4. The block base\n+ * within a page is 0x15 + 16 * (slot % 6), where slot is the physical\n+ * output slot from clkout_map[], not the logical output index.\n+ */\n+#define SIT9531X_OUT_PRG_DELAY_BASE\t\t0x15\n+#define SIT9531X_OUT_PRG_SLOT_STRIDE\t\t0x10\n+#define SIT9531X_OUT_PRG_BYTES\t\t\t5\n+/* bits [7:5], preserve */\n+#define SIT9531X_OUT_PRG_OPSTG_MASK\t\t0xE0\n+#define SIT9531X_OUT_PRG_FINE_SHIFT\t\t2\n+#define SIT9531X_OUT_PRG_FINE_MASK\t\t0x1C\t/* bits [4:2] */\n+#define SIT9531X_OUT_PRG_COARSE_HI_MASK\t\t0x03\t/* bits [1:0] */\n+/*\n+ * The divider takes two VCO cycles to act on a programmed delay and\n+ * release the output, so the encoded value carries them and the realized\n+ * delay is the register value less that. The reference flow adds the\n+ * same two.\n+ */\n+#define SIT9531X_OUT_PRG_DIVO_CYCLES\t2\n+\n+#define SIT9531X_OUT_PRG_FINE_STEP_PS\t\t30\n+#define SIT9531X_OUT_PRG_FINE_MAX\t\t7\t/* 3-bit field */\n+#define SIT9531X_OUT_PRG_COARSE_BITS\t\t34\n+\n+/*\n+ * On-demand phase-flush fired from a register rather than a GPIO pin.\n+ * DIVO_PHASE_SEL_REG selects the in-register trigger source and\n+ * DIVO_PHASE_TRIG flushes the output phase when pulsed high then low.\n+ * The unrelated OEb trigger pair in bits [7:6] must be preserved.\n+ */\n+#define SIT9531X_REG_GPIO_FUNC_CTRL1\tSIT9531X_REG(0x00, 0x65)\n+#define SIT9531X_DIVO_PHASE_SEL_REG\tBIT(5)\n+#define SIT9531X_DIVO_PHASE_TRIG\tBIT(4)\n+\n+/* ---- PLL page registers (apply to pages 0x0A-0x0D) ---- */\n+#define SIT9531X_PLL_REG_SMALL_UPDATE\t0x0F\n+\n+/* On-demand phase-flush enable (PLL page reg 0x3D bit 7) */\n+#define SIT9531X_PLL_REG_PHFL_CTRL\t0x3D\n+#define SIT9531X_PLL_PHFL_ON_DEMAND_EN\tBIT(7)\n+\n+/* Whether the PLL has the phase-flush feature (PLL page reg 0x47 bit 7) */\n+#define SIT9531X_PLL_REG_CONFIG47\t0x47\n+#define SIT9531X_PLL_CONFIG47_PHFL_EN\tBIT(7)\n+\n+/* Directives_GENERIC_PLL: the restart bit restarts the PLL, self-clearing */\n+#define SIT9531X_PLL_REG_DIRECTIVES\t0x05\n+#define SIT9531X_PLL_DIRECTIVE_RESTART\tBIT(0)\n+\n+/*\n+ * Loop-filter coefficients on PLL_PAGE regs 0x10-0x15 (3 normal +\n+ * 3 fast-lock) are GUI/NVM-generated by the timing configurator and must not be\n+ * reprogrammed at runtime; the register map flags them as\n+ * \"GUI generated configuration should not change manually\".\n+ */\n+\n+/*\n+ * Output-enable mask, twelve bits: bits 0-7 in LO, bits 8-11 in HI[3:0].\n+ * OUTn is bit n on PLLA and PLLB and bit 11 - n on PLLC and PLLD.\n+ */\n+#define SIT9531X_PLL_REG_OUT_MAP_HI\t\t0x27\n+#define SIT9531X_PLL_REG_OUT_MAP_LO\t\t0x28\n+#define SIT9531X_PLL_REG_STATUS\t\t0x31\n+\n+/* DIVN registers (free-run divider readback) */\n+#define SIT9531X_PLL_REG_DIVN_INT\t\t0x30\n+#define SIT9531X_PLL_REG_DIVN_NUM\t\t0x32 /* 4 bytes (0x32-0x35) */\n+#define SIT9531X_PLL_REG_DIVN_DEN\t\t0x38 /* 4 bytes (0x38-0x3B) */\n+\n+/*\n+ * Signal pathway debug readback -- PLL page. Dig_Sys_ReadCode selects\n+ * which point of the pathway is tapped, Dig_Sys_WriteCode carries the\n+ * modifiers for that read, Dig_Sys_read7..read0 hold the sampled bytes\n+ * and a read of the debug status data register latches a sample. The\n+ * TDC phase measurement is one tap among several, reached through read\n+ * code 69.\n+ */\n+#define SIT9531X_PLL_REG_DBG_READ_CODE\t0xB3\n+#define SIT9531X_PLL_REG_DBG_WRITE_CODE\t0xB4\n+#define SIT9531X_DBG_LOW_FREQ_CLK_BIT\tBIT(7)\n+#define SIT9531X_PLL_REG_DBG_DATA_0\t\t0xB5 /* [7:0] */\n+#define SIT9531X_PLL_REG_DBG_DATA_1\t\t0xB6 /* [15:8] */\n+#define SIT9531X_PLL_REG_DBG_DATA_2\t\t0xB7 /* [23:16] */\n+#define SIT9531X_PLL_REG_DBG_DATA_3\t\t0xB8 /* [31:24] */\n+#define SIT9531X_PLL_REG_DBG_DATA_4\t\t0xB9 /* [34:32] + sign */\n+\n+/* Read code of the TDC phase tap, and the sign bit of its sample */\n+#define SIT9531X_DBG_READ_CODE_TDC\t\t69\n+#define SIT9531X_TDC_SIGN_BIT\t\t3\n+#define SIT9531X_TDC_MAG_HI_MASK\tGENMASK(2, 0)\n+\n+/* DIVN is carried as fixed point, in steps of 1e-12 of a whole divider */\n+#define SIT9531X_DIVN_SCALE\t\t1000000000000ULL\n+\n+#define SIT9531X_PLL_REG_ACTIVE\t\t0x02\n+#define SIT9531X_PLL_ACTIVE_BIT\t\tBIT(0) /* PLL reached active state */\n+\n+/* PLL EXT page INTSYNC configuration registers */\n+#define SIT9531X_PLL_EXT_PAGE(_idx)\t(SIT9531X_PAGE_PLLA_EXT + (_idx))\n+\n+#define SIT9531X_PLL_STATUS_OUTER_DIS\tBIT(5)\n+\n+/*\n+ * Debug register -- PLL page. Writing the key unlocks the page's debug\n+ * registers for writing; writing zero locks them again.\n+ */\n+#define SIT9531X_PLL_REG_DEBUG\t\t0xBD\n+#define SIT9531X_PLL_DEBUG_UNLOCK\t\t0xC3\n+#define SIT9531X_PLL_DEBUG_LOCK\t\t\t0x00\n+\n+/*\n+ * Debug status bus -- PLL page. The address register selects the block\n+ * whose status the data register returns. A read of the data register\n+ * returns what the previous read latched, so the documented procedures\n+ * read it three times and take the last.\n+ */\n+#define SIT9531X_PLL_REG_DBG_STATUS_ADDR\t0xD1\n+#define SIT9531X_PLL_REG_DBG_STATUS_DATA\t0xD0\n+#define SIT9531X_DBG_STATUS_READS\t\t3\n+\n+/*\n+ * Status of the reference routed to the PLL: the low nibble is the\n+ * source the input subsystem feeds it, in the priority-table encoding.\n+ */\n+#define SIT9531X_DBG_STATUS_ROUTED_SRC\t0xB0\n+#define SIT9531X_DBG_ROUTED_SRC_MASK\t\tGENMASK(3, 0)\n+\n+/*\n+ * PLL_CONFIG1F_PLL: bit 6 puts the PLL in manual active select, where it\n+ * no longer arbitrates by priority. The other bits are GUI-generated\n+ * configuration.\n+ */\n+#define SIT9531X_PLL_REG_CONFIG1F\t\t0x1F\n+#define SIT9531X_PLL_CONFIG1F_MANUAL_SEL\tBIT(6)\n+\n+/*\n+ * MISCINNER_PLL: in manual active select, bit 5 makes the PLL follow the\n+ * manual input select instead of its active selection. The other bits\n+ * are GUI-generated configuration.\n+ */\n+#define SIT9531X_PLL_REG_MISCINNER\t\t0x18\n+#define SIT9531X_PLL_MISCINNER_MAN_IN_SEL\tBIT(5)\n+\n+/*\n+ * GPIO_INPUT_FUNC_CTRL5..8 (page 0), one per PLL: the manual input\n+ * select. With bit 4 set the input is the low nibble of this register,\n+ * otherwise it comes from the input-select pins. Bits 6:5 are the\n+ * active/spare choice of an indirect selection (bit 6 enables bit 5,\n+ * which picks the active or the spare clock) and are not decoded here.\n+ */\n+#define SIT9531X_REG_MAN_IN_SEL(_pll)\tSIT9531X_REG(0x00, 0xE8 + (_pll))\n+#define SIT9531X_MAN_IN_SEL_FROM_REG\t\tBIT(4)\n+#define SIT9531X_MAN_IN_SEL_MASK\t\tGENMASK(3, 0)\n+\n+/*\n+ * Per-PLL status register. HO_VALID says the holdover window holds a\n+ * valid frequency estimate, i.e. holdover memory has been acquired; it is\n+ * not the same as HO_FREEZE (page 0, reg 0x0A), which says the PLL has\n+ * already switched over to holdover.\n+ */\n+#define SIT9531X_PLL_REG_STATUS_1\t\t0x06\n+#define SIT9531X_PLL_STATUS_1_HO_VALID\tBIT(2)\n+\n+/*\n+ * Page 0x06 clock monitor status, one nibble per input lane: P lanes in\n+ * 0x02 (IN0P low, IN1P high) and 0x06 (IN2P, IN3P), N lanes in 0x92\n+ * (IN0N, IN1N) and 0x96 (IN2N, IN3N).\n+ */\n+#define SIT9531X_CLKMON_P_STATUS_01\t\tSIT9531X_REG(0x06, 0x02)\n+#define SIT9531X_CLKMON_P_STATUS_23\t\tSIT9531X_REG(0x06, 0x06)\n+#define SIT9531X_CLKMON_N_STATUS_01\t\tSIT9531X_REG(0x06, 0x92)\n+#define SIT9531X_CLKMON_N_STATUS_23\t\tSIT9531X_REG(0x06, 0x96)\n+#define SIT9531X_CLKMON_FINE_DRIFT\t\tBIT(0)\n+#define SIT9531X_CLKMON_COARSE_DRIFT\t\tBIT(1)\n+#define SIT9531X_CLKMON_LOSS\t\t\tBIT(2)\n+#define SIT9531X_CLKMON_P_NOTIF_01\t\tSIT9531X_REG(0x06, 0x03)\n+#define SIT9531X_CLKMON_P_NOTIF_23\t\tSIT9531X_REG(0x06, 0x07)\n+\n+#define SIT9531X_CLKMON_N_NOTIF_01\t\tSIT9531X_REG(0x06, 0x93)\n+#define SIT9531X_CLKMON_N_NOTIF_23\t\tSIT9531X_REG(0x06, 0x97)\n+\n+/* Per-input bit offsets within clock monitor nibble */\n+\n+/*\n+ * EEPROM profile load, page 0. The device compares the CRC stored in the\n+ * EEPROM against the one it computes from what it read; a mismatch means\n+ * the profile on the part is not the profile the board expects.\n+ * NOTIFY_4 collects the read-done bit and the defect bits, all sticky, so\n+ * a healthy load leaves exactly the read-done bit set.\n+ */\n+/* 4 bytes each, MSB first */\n+#define SIT9531X_REG_REC_CRC\t\tSIT9531X_REG(0x00, 0x8A)\n+#define SIT9531X_REG_CAL_CRC\t\tSIT9531X_REG(0x00, 0x8E)\n+#define SIT9531X_REG_EEPROM_NOTIF\t\tSIT9531X_REG(0x00, 0x97)\n+#define SIT9531X_EEPROM_READ_DONE\t\tBIT(0)\n+\n+/* Profile identifier the loaded configuration carries, page 1, 24 bits */\n+/* 3 bytes, LSB first */\n+#define SIT9531X_REG_PROFILE_ID\t\tSIT9531X_REG(0x01, 0x44)\n+\n+/* Consecutive failed INTRB acknowledgements before the line is given up */\n+#define SIT9531X_IRQ_ACK_TRIES\t\t\t8\n+\n+/* ---- Variant ID values (one byte at SIT9531X_REG_VARIANT_ID) ---- */\n+#define SIT9531X_VARIANT_ID_95317\t0x17\n+#define SIT9531X_VARIANT_ID_95316\t0x31\n+\n+#endif /* _SIT9531X_REGS_H */\n"
} |
| 2/1 | 2026/10/09 21:55 | agent | patch-evaluator |
5mModel:core Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "This patch series introduces a new driver for the SiTime SiT95316/SiT95317 I2C-controlled DPLL clock generator chips along with Device Tree documentation. The driver requires physical SiTime hardware on an I2C bus with specific clock inputs and register responses, which is not emulated in standard QEMU/GCE virtualized environments. Because probe will fail immediately without the physical ASIC, no DPLL devices or pins are registered and the driver's runtime logic is structurally unreachable. No core subsystem or core DPLL code was modified.",
"WorthFuzzing": false
}Instruction: You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.
IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.
================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.
- REACHABILITY IS THE PRIMARY GATE:
Fuzzing can only discover bugs in code that can actually execute in standard virtualized
environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
regardless of whether it adds assertions or complex logic.
- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
Patch authors routinely label changes as "cleanups", "refactorings", or state
"No functional change intended". Do NOT take these claims at face value.
Code refactorings that rearrange logic, introduce helper functions, or alter state management
in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
If reachable executable code is modified or refactored, it MUST be fuzzed.
- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
Even if the author believes the invariant always holds, fuzzing is essential to verify whether
an unusual sequence of operations can violate it.
================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:
- Non-kernel and non-executable changes:
* Modifications to Documentation/, comments, or spelling fixes.
* User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
that do not affect the compiled kernel image (vmlinux) or kernel modules.
* Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
that do not alter control flow or data structures.
* Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
* Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
* Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
physical PCI hot-unplug or manual sysfs driver unbinding.
* Code paths exclusive to architectures other than the target architecture.
================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
* Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
* Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
* Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
* Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).
================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:
- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
Do NOT list generic, ubiquitous functions called by almost every program in the corpus
(including, but not limited to: general memory allocators and deallocators, page fault
and trap handlers, or core synchronization primitives; this is not an exhaustive list).
Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
which severely dilutes fuzzing effort away from the actual changes.
- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
List functions that specifically implement the logic being added or altered, or direct API entrypoints
for the subsystem feature under review.
- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).
================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit 4367861b3ff40ec74931b84d543b874db1b6aff6
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Oct 9 21:54:58 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/devicetree/bindings/dpll/sitime,sit95316.yaml b/Documentation/devicetree/bindings/dpll/sitime,sit95316.yaml
new file mode 100644
index 0000000000000..200b9f709a572
--- /dev/null
+++ b/Documentation/devicetree/bindings/dpll/sitime,sit95316.yaml
@@ -0,0 +1,213 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/dpll/sitime,sit95316.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: SiTime SiT95316/SiT95317 DPLL Clock Generator
+
+maintainers:
+ - Ali Rouhi <arouhi@sitime.com>
+
+description: |
+ SiTime SiT95316 and SiT95317 are I2C-controlled programmable clock
+ generators with integrated DPLL for synchronization applications. Both
+ variants contain four PLLs with automatic/manual reference selection,
+ DCO frequency adjustment, and phase offset measurement via an on-chip
+ TDC (Time-to-Digital Converter).
+
+ Both parts have 4 differential input pairs whose lanes can also be
+ driven independently as single-ended references, so 8 inputs are
+ individually selectable. SiT95317 drives 8 outputs, SiT95316
+ drives 12.
+
+ Pin numbering. An input pin's reg is its lane: 2k is INkP and 2k + 1
+ is INkN, 0 to 7. A pair the loaded configuration runs differential is
+ one input, described at 2k. An output pin's reg is its physical output
+ OUTn, 0 to 11; the SiT95317 bonds out OUT0, OUT3, OUT4, OUT5, OUT7, OUT8,
+ OUT9 and OUT11.
+
+properties:
+ compatible:
+ enum:
+ - sitime,sit95316
+ - sitime,sit95317
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ items:
+ - const: xtal
+
+ clock-frequency:
+ description:
+ XO rate in Hz feeding XIN/XO_CLK, for firmware that describes the
+ oscillator as a property rather than as a clock provider, such as
+ ACPI. Use either this or "clocks", not both.
+
+ reset-gpios:
+ maxItems: 1
+ description:
+ GPIO connected to the chip's active-low reset pin (RESETB).
+
+ interrupts:
+ maxItems: 1
+ description:
+ Interrupt from the chip's active-low INTRB output. Asserted when
+ the device detects a status change such as lock acquisition or loss.
+
+ dpll-types:
+ maxItems: 4
+
+ vdd-supply:
+ description: Core supply of the PLLs, 1.8 V (the VDD pins).
+
+ vddin-supply:
+ description:
+ Supply of the input receivers and dividers, 3.3 V (VDDIN).
+
+ vdds-supply:
+ description:
+ Supply of the two GPIO pins that have their own rail (VDDS). The
+ SiT95316 has no such pin.
+
+ input-pins:
+ type: object
+ patternProperties:
+ "^pin@[0-9a-f]+$":
+ type: object
+ properties:
+ reg:
+ maximum: 7
+
+ supported-frequencies-hz:
+ description:
+ The rate wired to the input, in Hz, as a single entry. An
+ input runs at the rate the board feeds it and has no
+ frequency to select, so that entry is what the pin reports
+ as its frequency.
+
+patternProperties:
+ "^vddo([0-9]|1[01]|0_1|10_11)-supply$":
+ description: |
+ Supply of an output driver, named after its VDDO pin. The SiT95317
+ has one per output, VDDO0 to VDDO7, numbered as that part's
+ datasheet numbers its outputs; in the pin numbering of this binding
+ those are outputs 0, 3, 4, 5, 7, 8, 9 and 11, in that order. The
+ SiT95316 has VDDO0_1, VDDO2 to VDDO9 and VDDO10_11, the two shared
+ pins each powering the pair of outputs they are named after.
+
+required:
+ - compatible
+ - reg
+
+dependencies:
+ clocks: [ clock-names ]
+
+oneOf:
+ - required:
+ - clocks
+ - required:
+ - clock-frequency
+
+allOf:
+ - $ref: /schemas/dpll/dpll-device.yaml#
+ - if:
+ properties:
+ compatible:
+ const: sitime,sit95317
+ then:
+ properties:
+ output-pins:
+ patternProperties:
+ "^pin@[0-9a-f]+$":
+ properties:
+ reg:
+ enum: [0, 3, 4, 5, 7, 8, 9, 11]
+ patternProperties:
+ "^vddo(8|9|1[01]|0_1|10_11)-supply$": false
+ else:
+ properties:
+ vdds-supply: false
+ output-pins:
+ patternProperties:
+ "^pin@[0-9a-f]+$":
+ properties:
+ reg:
+ maximum: 11
+ patternProperties:
+ "^vddo(1[01]|[01])-supply$": false
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dpll@68 {
+ compatible = "sitime,sit95316";
+ reg = <0x68>;
+ clocks = <&xo2>;
+ clock-names = "xtal";
+ reset-gpios = <&gpio 78 GPIO_ACTIVE_LOW>;
+ interrupts = <12 IRQ_TYPE_LEVEL_LOW>;
+ vdd-supply = <®_1v8>;
+ vddin-supply = <®_3v3>;
+ vddo0_1-supply = <®_3v3>;
+ vddo10_11-supply = <®_1v8>;
+ dpll-types = "eec", "eec", "eec", "eec";
+
+ input-pins {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* IN0P and IN0N: pair 0 runs single-ended */
+ pin@0 {
+ reg = <0>;
+ label = "clkin0";
+ connection-type = "ext";
+ supported-frequencies-hz = /bits/ 64 <10000000>;
+ };
+
+ pin@1 {
+ reg = <1>;
+ label = "clkin1";
+ connection-type = "synce";
+ supported-frequencies-hz = /bits/ 64 <156250000>;
+ };
+ };
+
+ output-pins {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pin@0 {
+ reg = <0>;
+ label = "clkout0";
+ supported-frequencies-hz = /bits/ 64 <156250000>;
+ };
+
+ pin@1 {
+ reg = <1>;
+ label = "clkout1";
+ supported-frequencies-hz = /bits/ 64 <25000000>;
+ };
+
+ pin@a {
+ reg = <10>;
+ label = "clkout10";
+ supported-frequencies-hz = /bits/ 64 <10000000>;
+ };
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index ba20029693734..65da27d6e84d8 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -1563,6 +1563,8 @@ patternProperties:
description: SiRF Technology, Inc.
"^sis,.*":
description: Silicon Integrated Systems Corp.
+ "^sitime,.*":
+ description: SiTime Corporation
"^sitronix,.*":
description: Sitronix Technology Corporation
"^skov,.*":
diff --git a/MAINTAINERS b/MAINTAINERS
index 72ca3aab21066..64e15831055e3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25182,6 +25182,13 @@ S: Maintained
W: http://www.winischhofer.at/linuxsisusbvga.shtml
F: drivers/usb/misc/sisusbvga/
+SITIME SIT9531X DPLL DRIVER
+M: Ali Rouhi <arouhi@sitime.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/dpll/sitime,sit95316.yaml
+F: drivers/dpll/sit9531x/
+
SL28 CPLD MFD DRIVER
M: Michael Walle <mwalle@kernel.org>
S: Maintained
diff --git a/drivers/dpll/Kconfig b/drivers/dpll/Kconfig
index be98969f040ab..f8f7ca121b932 100644
--- a/drivers/dpll/Kconfig
+++ b/drivers/dpll/Kconfig
@@ -23,6 +23,8 @@ config DPLL_REFCNT_TRACKER
If unsure, say N.
+source "drivers/dpll/sit9531x/Kconfig"
+
source "drivers/dpll/zl3073x/Kconfig"
endmenu
diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile
index 9e7a3a3e592e8..4adc50d748d4d 100644
--- a/drivers/dpll/Makefile
+++ b/drivers/dpll/Makefile
@@ -8,4 +8,5 @@ dpll-y += dpll_core.o
dpll-y += dpll_netlink.o
dpll-y += dpll_nl.o
+obj-$(CONFIG_SIT9531X_DPLL) += sit9531x/
obj-$(CONFIG_ZL3073X) += zl3073x/
diff --git a/drivers/dpll/sit9531x/Kconfig b/drivers/dpll/sit9531x/Kconfig
new file mode 100644
index 0000000000000..47aea8674327c
--- /dev/null
+++ b/drivers/dpll/sit9531x/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config SIT9531X_DPLL
+ tristate "SiTime SiT9531x DPLL driver"
+ depends on I2C && NET
+ select DPLL
+ select REGMAP_I2C
+ help
+ Driver for SiTime SiT9531x family clock generators
+ (SiT95317, SiT95316).
+
+ This driver registers each on-chip PLL as a DPLL device
+ and exposes input/output clocks as DPLL pins, providing
+ runtime configuration via Generic Netlink.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sit9531x.
diff --git a/drivers/dpll/sit9531x/Makefile b/drivers/dpll/sit9531x/Makefile
new file mode 100644
index 0000000000000..b97d2656a4603
--- /dev/null
+++ b/drivers/dpll/sit9531x/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_SIT9531X_DPLL) += sit9531x.o
+sit9531x-y := core.o dpll.o prop.o
diff --git a/drivers/dpll/sit9531x/core.c b/drivers/dpll/sit9531x/core.c
new file mode 100644
index 0000000000000..c2d6fbd0363f1
--- /dev/null
+++ b/drivers/dpll/sit9531x/core.c
@@ -0,0 +1,5298 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SiTime SiT9531x DPLL core driver
+ *
+ * Copyright (C) 2026 SiTime Corp.
+ * Author: Ali Rouhi <arouhi@sitime.com>
+ * Author: Oleg Zadorozhnyi <Oleg.Zadorozhnyi@devoxsoftware.com>
+ *
+ * Core I2C probe, regmap configuration, hardware state management,
+ * and periodic work thread.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "core.h"
+#include "dpll.h"
+#include "prop.h"
+#include "regs.h"
+
+/*
+ * Number of input + output pin positions for pin index allocation. The two
+ * extra input positions are the crystal and the INTSYNC destination, the
+ * extra output position is the INTSYNC source.
+ */
+#define SIT9531X_NUM_INPUT_PINS (SIT9531X_MAX_INPUTS + 2)
+#define SIT9531X_NUM_OUTPUT_PINS (SIT9531X_MAX_OUTPUTS + 1)
+#define SIT9531X_NUM_PINS_TOTAL (SIT9531X_NUM_INPUT_PINS + \
+ SIT9531X_NUM_OUTPUT_PINS)
+
+#define SIT9531X_CHIP(_id, _nin, _nout, _name, _map) \
+ { .id = (_id), .num_inputs = (_nin), .num_outputs = (_nout), \
+ .name = (_name), .clkout_map = (_map) }
+
+/* Per-variant output index -> physical slot mapping */
+static const u8 clkout_map_95317[] = {0, 3, 4, 5, 7, 8, 9, 11};
+static const u8 clkout_map_95316[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+
+static const struct sit9531x_chip_info sit9531x_chip_ids[] = {
+ SIT9531X_CHIP(SIT9531X_VARIANT_ID_95317, 8, 8, "SiT95317",
+ clkout_map_95317),
+ SIT9531X_CHIP(SIT9531X_VARIANT_ID_95316, 8, 12, "SiT95316",
+ clkout_map_95316),
+};
+
+#define SIT9531X_RANGE_OFFSET SIT9531X_PAGE_SIZE
+
+/*
+ * Everything the device holds can change without the driver writing it,
+ * so nothing here is cacheable except the page selector. Caching that
+ * one spares a read of it before every access: the range code selects
+ * the page through a read-modify-write, and with no cache that read goes
+ * to the bus each time.
+ *
+ * The cached value is only as good as the last transfer, though. A
+ * transfer that fails can leave the part on another page than the cache
+ * records, and a part that lost power across suspend is back on page 0.
+ * Every accessor drops the entry on an error and resume drops it too, so
+ * the next access reads the selector again.
+ */
+static bool sit9531x_volatile_reg(struct device *dev __maybe_unused,
+ unsigned int reg)
+{
+ return reg != SIT9531X_PAGE_SEL;
+}
+
+static const struct regmap_range_cfg sit9531x_regmap_range = {
+ .range_min = SIT9531X_RANGE_OFFSET,
+ .range_max = SIT9531X_RANGE_OFFSET +
+ (SIT9531X_NUM_PAGES * SIT9531X_PAGE_SIZE) - 1,
+ .selector_reg = SIT9531X_PAGE_SEL,
+ .selector_mask = GENMASK(7, 0),
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = SIT9531X_PAGE_SIZE,
+};
+
+const struct regmap_config sit9531x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = SIT9531X_RANGE_OFFSET +
+ (SIT9531X_NUM_PAGES * SIT9531X_PAGE_SIZE) - 1,
+ .ranges = &sit9531x_regmap_range,
+ .num_ranges = 1,
+ .volatile_reg = sit9531x_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static void sit9531x_page_cache_drop(struct sit9531x_dev *sitdev)
+{
+ regcache_drop_region(sitdev->regmap, SIT9531X_PAGE_SEL,
+ SIT9531X_PAGE_SEL);
+}
+
+/*
+ * sit9531x_read_u8 - read an 8-bit register
+ * @reg: register in SIT9531X_REG(page, offset) form
+ * @val: output value
+ */
+int sit9531x_read_u8(struct sit9531x_dev *sitdev, unsigned int reg,
+ u8 *val)
+{
+ unsigned int vreg, tmp;
+ int rc;
+
+ vreg = (SIT9531X_REG_PAGE(reg) * SIT9531X_PAGE_SIZE) +
+ SIT9531X_REG_OFFSET(reg) + SIT9531X_RANGE_OFFSET;
+
+ rc = regmap_read(sitdev->regmap, vreg, &tmp);
+ if (rc) {
+ sit9531x_page_cache_drop(sitdev);
+ dev_err(sitdev->dev,
+ "Failed to read page 0x%02x reg 0x%02x: %d\n",
+ SIT9531X_REG_PAGE(reg), SIT9531X_REG_OFFSET(reg), rc);
+ } else {
+ *val = (u8)tmp;
+ }
+
+ return rc;
+}
+
+/*
+ * sit9531x_write_u8 - write an 8-bit register
+ * @reg: register in SIT9531X_REG(page, offset) form
+ * @val: value to write
+ */
+int sit9531x_write_u8(struct sit9531x_dev *sitdev, unsigned int reg,
+ u8 val)
+{
+ unsigned int vreg;
+ int rc;
+
+ vreg = (SIT9531X_REG_PAGE(reg) * SIT9531X_PAGE_SIZE) +
+ SIT9531X_REG_OFFSET(reg) + SIT9531X_RANGE_OFFSET;
+
+ rc = regmap_write(sitdev->regmap, vreg, val);
+ if (rc) {
+ sit9531x_page_cache_drop(sitdev);
+ dev_err(sitdev->dev,
+ "Failed to write page 0x%02x reg 0x%02x: %d\n",
+ SIT9531X_REG_PAGE(reg), SIT9531X_REG_OFFSET(reg), rc);
+ }
+
+ return rc;
+}
+
+/*
+ * sit9531x_read_pll_u8 - read a register on a PLL page
+ * @val: output value
+ */
+int sit9531x_read_pll_u8(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u8 offset, u8 *val)
+{
+ u8 page = sit9531x_pll_page(pll_idx);
+
+ return sit9531x_read_u8(sitdev, SIT9531X_REG(page, offset), val);
+}
+
+/*
+ * sit9531x_write_pll_u8 - write a register on a PLL page
+ * @val: value to write
+ */
+int sit9531x_write_pll_u8(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u8 offset, u8 val)
+{
+ u8 page = sit9531x_pll_page(pll_idx);
+
+ return sit9531x_write_u8(sitdev, SIT9531X_REG(page, offset), val);
+}
+
+/*
+ * sit9531x_update_pll_u8 - read-modify-write a register on a PLL page
+ * @mask: bits to modify
+ * @val: new value for masked bits
+ */
+int sit9531x_update_pll_u8(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u8 offset, u8 mask, u8 val)
+{
+ u8 page = sit9531x_pll_page(pll_idx);
+ unsigned int vreg;
+ int rc;
+
+ vreg = (page * SIT9531X_PAGE_SIZE) + offset + SIT9531X_RANGE_OFFSET;
+
+ /*
+ * A read-modify-write is a read and a write, and either can leave
+ * the cached page selector wrong, so fail the way the single
+ * accessors do.
+ */
+ rc = regmap_update_bits(sitdev->regmap, vreg, mask, val);
+ if (rc) {
+ sit9531x_page_cache_drop(sitdev);
+ dev_err(sitdev->dev,
+ "Failed to update page 0x%02x reg 0x%02x: %d\n",
+ page, offset, rc);
+ }
+
+ return rc;
+}
+
+/*
+ * sit9531x_input_get_regs - get force mask and state register addresses
+ * @index: logical input index
+ * @force_reg: output force mask register address
+ * @state_reg: output state register address
+ *
+ * Selects the correct Page 0x02 register pair based on the pair's
+ * signal mode and the lane (P/N) the index refers to.
+ */
+static void sit9531x_input_get_regs(const struct sit9531x_dev *sitdev,
+ u8 index,
+ unsigned int *force_reg,
+ unsigned int *state_reg)
+{
+ if (sitdev->ref[index].sig_mode == SIT9531X_MODE_DE) {
+ *force_reg = SIT9531X_REG_IN_DE_FORCE;
+ *state_reg = SIT9531X_REG_IN_DE_STATE;
+ } else if (sit9531x_input_is_n(index)) {
+ *force_reg = SIT9531X_REG_IN_SEN_FORCE;
+ *state_reg = SIT9531X_REG_IN_SEN_STATE;
+ } else {
+ *force_reg = SIT9531X_REG_IN_SEP_FORCE;
+ *state_reg = SIT9531X_REG_IN_SEP_STATE;
+ }
+}
+
+/*
+ * sit9531x_input_disable - disable an input reference
+ * @index: logical input index (0-N)
+ *
+ * Sets the force mask bit and clears the state bit for the given
+ * input, effectively disabling it. Register selection depends on
+ * the pair's signal mode (SE/DE) and the lane (P/N); the bit within
+ * each register addresses the input pair.
+ *
+ * Both writes are attempted even when the first fails, and the first
+ * error is returned. Neither is rolled back: the force and state bits
+ * only mean something together, so a transient bus error can leave the
+ * force bit asserted over a state bit that was never programmed, and the
+ * error is what says the override is not to be trusted.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+int sit9531x_input_disable(struct sit9531x_dev *sitdev, u8 index)
+{
+ unsigned int force_reg, state_reg;
+ struct sit9531x_ref *ref;
+ u8 pair, val;
+ int rc, ret;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ if (index >= SIT9531X_MAX_INPUTS)
+ return -EINVAL;
+
+ ref = &sitdev->ref[index];
+ pair = sit9531x_input_pair(index);
+ sit9531x_input_get_regs(sitdev, index, &force_reg, &state_reg);
+
+ rc = sit9531x_read_u8(sitdev, force_reg, &val);
+ if (!rc)
+ rc = sit9531x_write_u8(sitdev, force_reg, val | BIT(pair));
+
+ ret = sit9531x_read_u8(sitdev, state_reg, &val);
+ if (!ret)
+ ret = sit9531x_write_u8(sitdev, state_reg, val & ~BIT(pair));
+ if (ret && !rc)
+ rc = ret;
+
+ /*
+ * Drop the claim even when the pair could not be programmed. The
+ * force bit may be asserted over a state bit that never reached the
+ * device, so the receiver cannot be relied on; leaving the cache
+ * saying it is on makes the next enable skip itself and report a
+ * success the signal does not back. Clearing the force bit again
+ * is harmless, so the worst this costs is one redundant write.
+ */
+ ref->enabled = false;
+
+ return rc;
+}
+
+/*
+ * sit9531x_input_enable - enable an input reference
+ * @index: logical input index (0-N)
+ *
+ * Clears the force mask bit for the given input, returning it to
+ * hardware default (enabled).
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+int sit9531x_input_enable(struct sit9531x_dev *sitdev, u8 index)
+{
+ unsigned int force_reg, state_reg;
+ struct sit9531x_ref *ref;
+ u8 pair, val;
+ int rc;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ if (index >= SIT9531X_MAX_INPUTS)
+ return -EINVAL;
+
+ ref = &sitdev->ref[index];
+ pair = sit9531x_input_pair(index);
+ sit9531x_input_get_regs(sitdev, index, &force_reg, &state_reg);
+
+ rc = sit9531x_read_u8(sitdev, force_reg, &val);
+ if (rc)
+ return rc;
+ rc = sit9531x_write_u8(sitdev, force_reg, val & ~BIT(pair));
+ if (rc)
+ return rc;
+
+ ref->enabled = true;
+
+ return 0;
+}
+
+/*
+ * Output enable / disable (Hi-Z control)
+ *
+ * SiT9531x outputs can be configured as differential (DIFF) or
+ * single-ended (CMOS) depending on the factory blob. Each output slot
+ * has THREE Hi-Z force/state register pairs on Page 0x03: one for the
+ * differential path and one for each CMOS pad, OutP and OutN.
+ *
+ * A mute writes all three so the output goes quiet however it is wired.
+ * That includes both CMOS pads, as SiTime's Output_Disable_Control_HiZ.py
+ * does: the register map names bit 3 of ODRn_MISC0 CMOS_ENP and bit 2
+ * CMOS_ENN, while its table of values gives 0100 as CMOS on OutP, so the
+ * one pad a single-ended configuration drives cannot be told from it.
+ *
+ * slot 0-7 :
+ * DIFF 0xF2/0xF3 CMOS OutP 0xF8/0xF9 CMOS OutN 0xF6/0xF7
+ * slot 8-11:
+ * DIFF 0xF4/0xF5 bits 3:0 CMOS OutP 0xFA/0xFB
+ * CMOS OutN 0xF4/0xF5 bits 7:4
+ *
+ * MASK bit = 1 -> driver takes control of that output's Hi-Z state
+ * STATE bit = 0 -> output is forced to Hi-Z (muted)
+ * STATE bit = 1 -> output is driven (active)
+ *
+ * The output "index" in the driver is logical; the physical slot comes
+ * from info->clkout_map[].
+ */
+
+enum {
+ SIT9531X_HIZ_DIFF,
+ SIT9531X_HIZ_CMOS_P,
+ SIT9531X_HIZ_CMOS_N,
+ SIT9531X_HIZ_PAIRS,
+};
+
+struct sit9531x_hiz_pair {
+ unsigned int mask;
+ unsigned int state;
+ u8 bit;
+};
+
+static void sit9531x_output_get_hiz_regs(u8 slot,
+ struct sit9531x_hiz_pair *p)
+{
+ struct sit9531x_hiz_pair *diff = &p[SIT9531X_HIZ_DIFF];
+ struct sit9531x_hiz_pair *cmos_p = &p[SIT9531X_HIZ_CMOS_P];
+ struct sit9531x_hiz_pair *cmos_n = &p[SIT9531X_HIZ_CMOS_N];
+
+ if (slot <= 7) {
+ diff->mask = SIT9531X_REG_HIZ_DIFF_07_MASK;
+ diff->state = SIT9531X_REG_HIZ_DIFF_07_STATE;
+ diff->bit = slot;
+ cmos_p->mask = SIT9531X_REG_HIZ_SE_07_MASK;
+ cmos_p->state = SIT9531X_REG_HIZ_SE_07_STATE;
+ cmos_p->bit = slot;
+ cmos_n->mask = SIT9531X_REG_HIZ_SEN_07_MASK;
+ cmos_n->state = SIT9531X_REG_HIZ_SEN_07_STATE;
+ cmos_n->bit = slot;
+ } else {
+ diff->mask = SIT9531X_REG_HIZ_DIFF_811_MASK;
+ diff->state = SIT9531X_REG_HIZ_DIFF_811_STATE;
+ diff->bit = slot - 8;
+ cmos_p->mask = SIT9531X_REG_HIZ_SE_811_MASK;
+ cmos_p->state = SIT9531X_REG_HIZ_SE_811_STATE;
+ cmos_p->bit = slot - 8;
+ cmos_n->mask = SIT9531X_REG_HIZ_DIFF_811_MASK;
+ cmos_n->state = SIT9531X_REG_HIZ_DIFF_811_STATE;
+ cmos_n->bit = slot - 8 + SIT9531X_HIZ_SEN_811_SHIFT;
+ }
+}
+
+/*
+ * sit9531x_output_mode_fetch - read how an output is wired
+ *
+ * The Hi-Z force is a separate register pair for the differential path
+ * and for each CMOS pad, and only the pairs belonging to the way the
+ * output is actually wired say anything about whether it is quiet. The
+ * others hold whatever the loaded configuration left in them.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+static int sit9531x_output_mode_fetch(struct sit9531x_dev *sitdev, u8 out_idx)
+{
+ const struct sit9531x_chip_info *info = sitdev->info;
+ u8 slot, page, reg, val;
+ int rc;
+
+ slot = info->clkout_map[out_idx];
+ page = (slot > SIT9531X_PAGE_OUTSYS0_SLOT_MAX) ?
+ SIT9531X_PAGE_OUTSYS1 : SIT9531X_PAGE_OUTSYS0;
+ reg = SIT9531X_OUT_MISC0_BASE +
+ SIT9531X_OUT_MISC0_STRIDE * (slot % 6);
+
+ rc = sit9531x_read_u8(sitdev, SIT9531X_REG(page, reg), &val);
+ if (rc)
+ return rc;
+
+ sitdev->out[out_idx].cmos = !!(val & (SIT9531X_OUT_CMOS_ENP |
+ SIT9531X_OUT_CMOS_ENN));
+
+ return 0;
+}
+
+static int sit9531x_hiz_pair_forced(struct sit9531x_dev *sitdev,
+ const struct sit9531x_hiz_pair *p,
+ bool *forced)
+{
+ u8 mask, state;
+ int rc;
+
+ rc = sit9531x_read_u8(sitdev, p->mask, &mask);
+ if (rc)
+ return rc;
+ rc = sit9531x_read_u8(sitdev, p->state, &state);
+ if (rc)
+ return rc;
+
+ *forced = (mask & BIT(p->bit)) && !(state & BIT(p->bit));
+
+ return 0;
+}
+
+/*
+ * Report whether a slot is currently forced into Hi-Z, i.e. the driver
+ * (or the loaded profile) took control of the pin (MASK bit set) and
+ * forces it to Hi-Z (STATE bit clear). Only the pairs that match how
+ * the output is wired are read: that is what controls the pad. A CMOS
+ * output reads as muted only when both of its pads are forced, since
+ * which one the configuration drives cannot be told (see above).
+ */
+static int sit9531x_output_forced_hiz(struct sit9531x_dev *sitdev,
+ u8 out_idx, bool *muted)
+{
+ struct sit9531x_hiz_pair p[SIT9531X_HIZ_PAIRS];
+ int rc;
+
+ sit9531x_output_get_hiz_regs(sitdev->info->clkout_map[out_idx], p);
+
+ /*
+ * Testing a pair the output is not wired through and taking it as
+ * proof of a mute answers from a register nothing drives, and
+ * disagrees with itself when a mute lands on one pair and fails
+ * on another.
+ */
+ if (!sitdev->out[out_idx].cmos)
+ return sit9531x_hiz_pair_forced(sitdev,
+ &p[SIT9531X_HIZ_DIFF], muted);
+
+ rc = sit9531x_hiz_pair_forced(sitdev, &p[SIT9531X_HIZ_CMOS_P], muted);
+ if (rc || !*muted)
+ return rc;
+
+ return sit9531x_hiz_pair_forced(sitdev, &p[SIT9531X_HIZ_CMOS_N],
+ muted);
+}
+
+/*
+ * sit9531x_output_state_refresh - read an output's mute state back
+ *
+ * Used when a mute could not be confirmed at the time it was written. The
+ * driver does not poll output state, so without this the cached value would
+ * stand until something else happened to write it.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+int sit9531x_output_state_refresh(struct sit9531x_dev *sitdev, u8 out_idx)
+{
+ bool muted;
+ int rc;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ rc = sit9531x_output_forced_hiz(sitdev, out_idx, &muted);
+ if (rc)
+ return rc;
+
+ sitdev->out[out_idx].enabled = !muted;
+ sitdev->out[out_idx].state_stale = false;
+
+ return 0;
+}
+
+static int sit9531x_hiz_set_bit(struct sit9531x_dev *sitdev,
+ unsigned int reg, u8 bit, bool set)
+{
+ u8 cur, new_val;
+ int rc;
+
+ rc = sit9531x_read_u8(sitdev, reg, &cur);
+ if (rc)
+ return rc;
+
+ new_val = set ? (cur | BIT(bit)) : (cur & ~BIT(bit));
+
+ return sit9531x_write_u8(sitdev, reg, new_val);
+}
+
+/* Attempts to re-lock the output loops before reporting them open. */
+#define SIT9531X_LOOP_LOCK_TRIES 3
+
+/*
+ * Commit a programming sequence started by sit9531x_prg_enter():
+ * update the NVM shadow and re-lock the loops. The sleep gives the
+ * hardware its required settling time after the loop-lock command;
+ * it is intentional despite the caller holding multiop_lock, as the
+ * whole NVM + lock sequence must be atomic.
+ */
+static int sit9531x_prg_commit(struct sit9531x_dev *sitdev)
+{
+ int rc, rc2 = 0, rc3;
+ u8 attempt;
+
+ rc = sit9531x_write_u8(sitdev, SIT9531X_REG_PRG_DIR_GEN,
+ SIT9531X_UPDATE_NVM);
+
+ /*
+ * Issue the loop lock even if the update failed: every caller
+ * commits whatever happened after the entry so that the chip never
+ * stays in the PRG_CMD state with its loops open, and returning
+ * early here would defeat that.
+ *
+ * Re-lock the loops. Leaving them open is worse than any other
+ * failure this function can report, and nothing else closes them,
+ * so retry, as the priority commit retries the release of its
+ * forced holdover.
+ */
+ for (attempt = 0; attempt < SIT9531X_LOOP_LOCK_TRIES; attempt++) {
+ rc2 = sit9531x_write_u8(sitdev, SIT9531X_REG_PRG_DIR_GEN,
+ SIT9531X_LOOP_LOCK);
+ if (!rc2)
+ break;
+ usleep_range(1000, 2000);
+ }
+ if (rc2)
+ dev_err(sitdev->dev,
+ "output loops left unlocked after programming: %d\n",
+ rc2);
+
+ msleep(100);
+
+ /*
+ * Put the output-system debug block back the way the device powers
+ * up. Its key register unlocks every debug register while it holds
+ * the unlock value, and each programming sequence writes that value
+ * itself, so nothing needs it left unlocked in between.
+ */
+ rc3 = sit9531x_write_u8(sitdev, SIT9531X_REG_OUTSYS_DEBUG,
+ SIT9531X_DEBUG_LOCK_VAL);
+
+ if (rc)
+ return rc;
+ if (rc2)
+ return rc2;
+
+ /*
+ * The programming is committed and the loops are locked by now;
+ * only the debug key stayed open, and the next sequence writes it
+ * again. Failing here would make the caller skip what follows a
+ * change that did take effect -- and the core drops an identical
+ * retry, so it would never run.
+ */
+ if (rc3)
+ dev_warn(sitdev->dev,
+ "output debug block left unlocked after programming: %d\n",
+ rc3);
+
+ return 0;
+}
+
+/*
+ * Enter the output-system programming state: unlock the debug
+ * registers on Page 3 and issue the PRG_CMD state command. Register
+ * writes that reconfigure the output system only take effect when
+ * they are made inside this state.
+ */
+static int sit9531x_prg_enter(struct sit9531x_dev *sitdev)
+{
+ int rc;
+
+ rc = sit9531x_write_u8(sitdev, SIT9531X_REG_OUTSYS_DEBUG,
+ SIT9531X_DEBUG_UNLOCK_VAL);
+ if (rc)
+ return rc;
+
+ rc = sit9531x_write_u8(sitdev, SIT9531X_REG_PRG_DIR_GEN,
+ SIT9531X_PRG_CMD_STATE);
+ if (rc) {
+ /*
+ * The debug block is unlocked at this point, and a transfer
+ * that reported an error may still have reached the part --
+ * which would leave the device in PRG_CMD with its output
+ * loops open. Callers skip the commit when the entry fails,
+ * so leave the state here the way every completed sequence
+ * does: nothing was changed, so the update commits what the
+ * part already holds, the loops are re-locked, and the
+ * settling time is waited out before the caller drops the
+ * lock and the next request programs the part. The entry
+ * failure is the error to report.
+ */
+ sit9531x_prg_commit(sitdev);
+ return rc;
+ }
+
+ return 0;
+}
+
+/*
+ * sit9531x_output_hiz_write - mute or unmute an output
+ *
+ * Muting takes control of the pin (MASK=1) and forces it to Hi-Z
+ * (STATE=0) on every Hi-Z pair of the slot -- differential, CMOS OutP
+ * and CMOS OutN -- because the output must go quiet whichever way it is
+ * wired; unmuting releases the override on all of them and hands the pin
+ * back to the device. The caller must already be in the programming
+ * state.
+ */
+static int sit9531x_output_hiz_write(struct sit9531x_dev *sitdev, u8 slot,
+ bool mute)
+{
+ struct sit9531x_hiz_pair p[SIT9531X_HIZ_PAIRS];
+ u8 mask[SIT9531X_HIZ_PAIRS], state[SIT9531X_HIZ_PAIRS];
+ int i, rc, undo_rc;
+
+ sit9531x_output_get_hiz_regs(slot, p);
+
+ if (!mute) {
+ for (i = 0; i < SIT9531X_HIZ_PAIRS; i++) {
+ rc = sit9531x_hiz_set_bit(sitdev, p[i].mask, p[i].bit,
+ false);
+ if (rc)
+ return rc;
+ }
+ return 0;
+ }
+
+ /*
+ * Remember every pair as it was: if the mute fails part-way, that
+ * is what the pad goes back to -- which may already be a mute, from
+ * an earlier request or from the loaded profile.
+ */
+ for (i = 0; i < SIT9531X_HIZ_PAIRS; i++) {
+ rc = sit9531x_read_u8(sitdev, p[i].state, &state[i]);
+ if (rc)
+ return rc;
+ rc = sit9531x_read_u8(sitdev, p[i].mask, &mask[i]);
+ if (rc)
+ return rc;
+ }
+
+ /*
+ * Forced value first, override enable second. Muted is decoded as
+ * MASK set with STATE clear, so enabling the override while STATE
+ * still holds whatever the loaded configuration left there can pin
+ * the pad driven for the width of an I2C transfer.
+ */
+ for (i = 0; i < SIT9531X_HIZ_PAIRS; i++) {
+ rc = sit9531x_hiz_set_bit(sitdev, p[i].state, p[i].bit, false);
+ if (!rc)
+ rc = sit9531x_hiz_set_bit(sitdev, p[i].mask, p[i].bit,
+ true);
+ if (rc)
+ break;
+ }
+ if (!rc)
+ return 0;
+
+ /*
+ * Only part of the mute reached the device. Put every pair it may
+ * have touched back to the bits read above -- override first, then
+ * value, the reverse of the order they went on -- so the pad ends
+ * where the request started rather than on part of a mute, or
+ * un-muted when it was muted before.
+ */
+ for (; i >= 0; i--) {
+ u8 bit = BIT(p[i].bit);
+
+ undo_rc = sit9531x_hiz_set_bit(sitdev, p[i].mask, p[i].bit,
+ mask[i] & bit);
+ if (!undo_rc)
+ undo_rc = sit9531x_hiz_set_bit(sitdev, p[i].state,
+ p[i].bit,
+ state[i] & bit);
+ if (undo_rc)
+ dev_err(sitdev->dev,
+ "slot%u: Hi-Z override left part applied (%d)\n",
+ slot, undo_rc);
+ }
+
+ return rc;
+}
+
+/*
+ * sit9531x_output_disable - mute an output (force Hi-Z)
+ * @index: logical output index (0..info->num_outputs-1)
+ *
+ * Sets MASK and clears STATE on every Hi-Z pair of the slot, differential
+ * and both CMOS pads, so that the output is muted regardless of its
+ * electrical configuration. The
+ * writes are wrapped in the PRG_CMD / NVM update / loop lock sequence
+ * so the new state is applied by the hardware.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+int sit9531x_output_disable(struct sit9531x_dev *sitdev, u8 index)
+{
+ const struct sit9531x_chip_info *info = sitdev->info;
+ bool muted;
+ u8 slot;
+ int rc, ret, state_rc;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ if (index >= info->num_outputs)
+ return -EINVAL;
+
+ slot = info->clkout_map[index];
+ rc = sit9531x_prg_enter(sitdev);
+ if (rc)
+ return rc;
+
+ rc = sit9531x_output_hiz_write(sitdev, slot, true);
+
+ /*
+ * Always leave the PRG_CMD programming state, even on a mid-sequence
+ * write failure: prg_enter() unlocked the output loops, so returning
+ * without prg_commit() would strand the chip in the programming state
+ * with the loops unlocked. Best effort -- keep the first error.
+ */
+ ret = sit9531x_prg_commit(sitdev);
+ if (ret && !rc)
+ rc = ret;
+
+ /*
+ * Keep the software state aligned to what hardware now drives even
+ * when one write in the sequence failed. The commit above may have
+ * applied a partial mask/state combination.
+ */
+ state_rc = sit9531x_output_forced_hiz(sitdev, index, &muted);
+ if (!state_rc) {
+ sitdev->out[index].enabled = !muted;
+ sitdev->out[index].state_stale = false;
+ } else {
+ /*
+ * The writes may well have landed; what failed is the proof.
+ * Mark the cached state for a read-through rather than
+ * reporting the value it had before this call.
+ */
+ sitdev->out[index].state_stale = true;
+ if (!rc)
+ rc = state_rc;
+ }
+
+ return rc;
+}
+
+/*
+ * sit9531x_output_enable - un-mute an output (active state)
+ * @index: logical output index (0..info->num_outputs-1)
+ *
+ * Releases MASK on every Hi-Z pair so the device drives the output
+ * again as the loaded profile configures it. The writes are wrapped
+ * in the PRG_CMD / NVM update / loop lock sequence so the new state is
+ * applied by the hardware.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+int sit9531x_output_enable(struct sit9531x_dev *sitdev, u8 index)
+{
+ const struct sit9531x_chip_info *info = sitdev->info;
+ bool muted;
+ u8 slot;
+ int rc, ret, state_rc;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ if (index >= info->num_outputs)
+ return -EINVAL;
+
+ slot = info->clkout_map[index];
+ rc = sit9531x_prg_enter(sitdev);
+ if (rc)
+ return rc;
+
+ rc = sit9531x_output_hiz_write(sitdev, slot, false);
+
+ /*
+ * Always leave the PRG_CMD programming state, even on a mid-sequence
+ * write failure: prg_enter() unlocked the output loops, so returning
+ * without prg_commit() would strand the chip in the programming state
+ * with the loops unlocked. Best effort -- keep the first error.
+ */
+ ret = sit9531x_prg_commit(sitdev);
+ if (ret && !rc)
+ rc = ret;
+
+ /* See sit9531x_output_disable(): commit can apply part of it. */
+ state_rc = sit9531x_output_forced_hiz(sitdev, index, &muted);
+ if (!state_rc) {
+ sitdev->out[index].enabled = !muted;
+ sitdev->out[index].state_stale = false;
+ } else {
+ /*
+ * The writes may well have landed; what failed is the proof.
+ * Mark the cached state for a read-through rather than
+ * reporting the value it had before this call.
+ */
+ sitdev->out[index].state_stale = true;
+ if (!rc)
+ rc = state_rc;
+ }
+
+ return rc;
+}
+
+/*
+ * Input priority selection
+ *
+ * The SiT9531x has an 11-slot priority table per PLL on Page 1. Each
+ * register holds two slots nibble-packed: the earlier (even, 2n) slot
+ * in [7:4] and the later (odd, 2n+1) slot in [3:0].
+ *
+ * The procedure:
+ * 1. Force PLL into holdover (PLL page reg 0x6F bit 4)
+ * 2. Write priority slots on Page 1
+ * 3. Small change update (Page 0 reg 0x0F bit 1)
+ * 4. Release holdover, unless it was forced before step 1 by something
+ * other than this driver
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+
+/* Page-1 register holding priority slot @slot of @pll_idx. */
+static u16 sit9531x_prio_reg(u8 pll_idx, u8 slot)
+{
+ return SIT9531X_REG(SIT9531X_PAGE_PRIOSYS,
+ SIT9531X_PRIO_BASE_REG +
+ SIT9531X_PRIO_REGS_PER_PLL * pll_idx +
+ slot / SIT9531X_PRIO_SLOTS_PER_REG);
+}
+
+/*
+ * Extract priority slot @slot from its register value. The register
+ * holding slots 2n and 2n+1 keeps the earlier slot in the high nibble
+ * (CLK_SPARE<2n>SEL) and the later one in the low nibble.
+ */
+static u8 sit9531x_prio_slot_get(u8 val, u8 slot)
+{
+ if (slot & 1)
+ return val & SIT9531X_PRIO_NIBBLE_MASK;
+
+ return val >> SIT9531X_PRIO_HI_SHIFT;
+}
+
+/* Place source @src in priority slot @slot of a register value. */
+static u8 sit9531x_prio_slot_set(u8 val, u8 slot, u8 src)
+{
+ if (slot & 1)
+ return (val & (SIT9531X_PRIO_NIBBLE_MASK <<
+ SIT9531X_PRIO_HI_SHIFT)) |
+ (src & SIT9531X_PRIO_NIBBLE_MASK);
+
+ return (val & SIT9531X_PRIO_NIBBLE_MASK) |
+ ((src & SIT9531X_PRIO_NIBBLE_MASK) <<
+ SIT9531X_PRIO_HI_SHIFT);
+}
+
+/*
+ * Commit a priority-table programming sequence through the Page-0
+ * programming directive register.
+ *
+ * A small change update is all the table needs. The NVM-bank and
+ * loop-lock directives that the output system issues do not belong
+ * here: the former programs non-volatile storage from the efuse and
+ * the latter only means anything after an escape to the PRG_CMD
+ * state. This matches the documented input_priority_sel() procedure.
+ */
+static int sit9531x_prio_prg_commit(struct sit9531x_dev *sitdev)
+{
+ int rc;
+
+ rc = sit9531x_write_u8(sitdev, SIT9531X_REG_GLOBAL_UPDATE,
+ SIT9531X_SMALL_UPDATE_CMD);
+ if (rc)
+ return rc;
+
+ usleep_range(1000, 2000);
+
+ return 0;
+}
+
+/**
+ * sit9531x_input_mon_fetch - read the clock monitor status of every lane
+ * @sitdev: device pointer
+ *
+ * Four registers carry a nibble per lane. Loss of signal and frequency
+ * drift are what separate an input the device could lock to from one it
+ * could not, which the priority commit uses to choose the active
+ * selection and the pins report as their operational state.
+ *
+ * Caller must hold sitdev->multiop_lock, or run before registration.
+ */
+int sit9531x_input_mon_fetch(struct sit9531x_dev *sitdev)
+{
+ static const unsigned int regs[] = {
+ SIT9531X_CLKMON_P_STATUS_01, SIT9531X_CLKMON_P_STATUS_23,
+ SIT9531X_CLKMON_N_STATUS_01, SIT9531X_CLKMON_N_STATUS_23,
+ };
+ u8 val[ARRAY_SIZE(regs)], i, pair, nib;
+ int rc;
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++) {
+ rc = sit9531x_read_u8(sitdev, regs[i], &val[i]);
+ if (rc)
+ return rc;
+ }
+
+ for (i = 0; i < sitdev->info->num_inputs; i++) {
+ struct sit9531x_ref *ref = &sitdev->ref[i];
+
+ /* Lane 2k is INkP, 2k + 1 INkN; a register holds two pairs. */
+ pair = sit9531x_input_pair(i);
+ nib = val[(sit9531x_input_is_n(i) ? 2 : 0) + pair / 2];
+ nib = (pair & 1) ? nib >> 4 : nib & 0x0F;
+
+ ref->los = !!(nib & SIT9531X_CLKMON_LOSS);
+ ref->qual_fail = !ref->los &&
+ !!(nib & (SIT9531X_CLKMON_FINE_DRIFT |
+ SIT9531X_CLKMON_COARSE_DRIFT));
+ }
+
+ return 0;
+}
+
+/*
+ * Can the device lock to this source now? Only the input lanes have a
+ * monitor; the on-chip oscillator and the inter-PLL net are taken as
+ * present. A differential pair is watched through its P lane, which is
+ * the one its table entries are canonicalised to.
+ */
+static bool sit9531x_prio_src_live(const struct sit9531x_dev *sitdev, u8 src)
+{
+ u8 index = sit9531x_hw_src_input(src);
+
+ if (index >= sitdev->info->num_inputs)
+ return true;
+
+ return !sitdev->ref[index].los;
+}
+
+/*
+ * Fold a source code to the lane a DPLL pin actually represents.
+ *
+ * Differential input pairs expose only the P lane as a DPLL pin. A
+ * priority table entry encoded as an N lane for such a pair must map to
+ * the P-lane source for pin-facing operations (membership, priority slots,
+ * add/remove/set lookups), matching sit9531x_ref_pll_mask_fetch().
+ */
+static u8 sit9531x_prio_src_canon(const struct sit9531x_dev *sitdev, u8 src)
+{
+ u8 index = sit9531x_hw_src_input(src);
+
+ if (index >= sitdev->info->num_inputs)
+ return src;
+
+ if (sit9531x_input_is_n(index) &&
+ sitdev->ref[index].sig_mode == SIT9531X_MODE_DE)
+ return sit9531x_input_hw_src(index - 1);
+
+ return src;
+}
+
+/*
+ * sit9531x_input_prio_present - is a source listed in a PLL's priority table
+ * @input_idx: input source in hardware encoding (see
+ * sit9531x_input_hw_src())
+ *
+ * Answers from the membership mask that every table write and every poll
+ * refreshes, which is what the pin state getters test. The priority slot
+ * cannot answer this: a source that is not in the table reports the lowest
+ * slot, so the slot value alone does not separate absent from last.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+bool sit9531x_input_prio_present(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u8 input_idx)
+{
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ if (pll_idx >= SIT9531X_NUM_PLLS)
+ return false;
+
+ input_idx = sit9531x_prio_src_canon(sitdev, input_idx);
+ if (input_idx >= SIT9531X_PRIO_NUM_SRC)
+ return false;
+
+ return !!(sitdev->chan[pll_idx].prio_mask & BIT(input_idx));
+}
+
+/*
+ * sit9531x_input_prio_get - read an input's priority for a PLL
+ * @input_idx: input source in hardware encoding (see
+ * sit9531x_input_hw_src())
+ * @prio: output priority (lower is preferred)
+ *
+ * Reports the priority configured for the source on this PLL, which is
+ * kept whether or not the source is currently in the table: state and
+ * priority are separate attributes, so disconnecting an input and
+ * connecting it again must not change the priority it reports. The
+ * value is seeded from the hardware table, and re-seeded whenever the
+ * read-back shows the table was rewritten by something other than this
+ * driver. A source that was never listed reports the lowest slot.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+int sit9531x_input_prio_get(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u8 input_idx, u8 *prio)
+{
+ const struct sit9531x_chan *chan;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ if (pll_idx >= SIT9531X_NUM_PLLS)
+ return -EINVAL;
+ input_idx = sit9531x_prio_src_canon(sitdev, input_idx);
+ if (input_idx >= SIT9531X_PRIO_NUM_SRC)
+ return -EINVAL;
+
+ chan = &sitdev->chan[pll_idx];
+ if (chan->cfg_known & BIT(input_idx))
+ *prio = chan->cfg_prio[input_idx];
+ else
+ *prio = SIT9531X_PRIO_MAX_SLOTS - 1;
+
+ return 0;
+}
+
+/*
+ * Take the configured priorities from a table the hardware holds: each
+ * listed source gets the first slot it occupies. A source the table does
+ * not list keeps whatever it had, so a disconnected input comes back with
+ * its old priority.
+ */
+static void sit9531x_prio_cfg_seed(struct sit9531x_dev *sitdev, u8 pll_idx,
+ const u8 *srcs)
+{
+ struct sit9531x_chan *chan = &sitdev->chan[pll_idx];
+ u16 seeded = 0;
+ u8 slot, src;
+
+ for (slot = 0; slot < SIT9531X_PRIO_MAX_SLOTS; slot++) {
+ src = srcs[slot] & SIT9531X_PRIO_NIBBLE_MASK;
+ if (!sit9531x_prio_src_usable(src))
+ continue;
+ src = sit9531x_prio_src_canon(sitdev, src);
+ if (seeded & BIT(src))
+ continue;
+ seeded |= BIT(src);
+ chan->cfg_prio[src] = slot;
+ chan->cfg_known |= BIT(src);
+ }
+
+ /*
+ * A source this PLL has never listed gets the lowest slot, the value
+ * it reports and the one it is connected at, so its priority does not
+ * change when its state does.
+ */
+ for (src = 0; src < SIT9531X_PRIO_NUM_SRC; src++) {
+ if (chan->cfg_known & BIT(src))
+ continue;
+ chan->cfg_prio[src] = SIT9531X_PRIO_MAX_SLOTS - 1;
+ chan->cfg_known |= BIT(src);
+ }
+
+ memcpy(chan->seen_srcs, srcs, sizeof(chan->seen_srcs));
+ chan->seen_valid = true;
+}
+
+/*
+ * Build the table for a set of member sources: ordered by configured
+ * priority, ties kept in the order the hardware table has them, the slots
+ * past the last member naming no source. Filling them with the code for
+ * no source rather than with copies of the last member keeps every
+ * source in exactly one slot, so the order the table encodes is the
+ * order the priorities say.
+ */
+static void sit9531x_prio_table_build(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u16 members, u8 *srcs)
+{
+ const struct sit9531x_chan *chan = &sitdev->chan[pll_idx];
+ u8 order[SIT9531X_PRIO_NUM_SRC], n = 0, i, j, src;
+
+ for (src = 0; src < SIT9531X_PRIO_NUM_SRC; src++)
+ if (members & BIT(src))
+ order[n++] = src;
+
+ /* Insertion sort: at most a dozen entries. */
+ for (i = 1; i < n; i++) {
+ u8 cur = order[i];
+
+ for (j = i; j > 0; j--) {
+ u8 prev = order[j - 1];
+ u16 kc, kp;
+
+ kc = (chan->cfg_known & BIT(cur)) ?
+ chan->cfg_prio[cur] : U8_MAX;
+ kp = (chan->cfg_known & BIT(prev)) ?
+ chan->cfg_prio[prev] : U8_MAX;
+ if (kc == kp) {
+ /* Keep the hardware order among equals. */
+ kc = chan->prio_last[cur] ?: U8_MAX;
+ kp = chan->prio_last[prev] ?: U8_MAX;
+ }
+ if (kp <= kc)
+ break;
+ order[j] = prev;
+ }
+ order[j] = cur;
+ }
+
+ for (i = 0; i < SIT9531X_PRIO_MAX_SLOTS; i++)
+ srcs[i] = i < n ? order[i] : SIT9531X_PRIO_SRC_NONE;
+}
+
+/*
+ * Refresh a PLL's cached view of its priority table from the source codes
+ * the table holds -- here after a write, and once per poll from the
+ * read-back in sit9531x_chan_state_fetch().
+ *
+ * The membership mask is what the pin state getters test, the per-slot
+ * copy is what a rewrite compares against, and the first-slot array
+ * orders sources of equal priority, so none of them costs a register read
+ * per pin.
+ */
+static void sit9531x_prio_mask_build(struct sit9531x_dev *sitdev, u8 pll_idx,
+ const u8 *srcs)
+{
+ struct sit9531x_chan *chan = &sitdev->chan[pll_idx];
+ u8 first[SIT9531X_PRIO_NUM_SRC] = { 0 };
+ u16 mask = 0;
+ u8 slot, src, src_canon;
+
+ for (slot = 0; slot < SIT9531X_PRIO_MAX_SLOTS; slot++) {
+ src = srcs[slot];
+ src &= SIT9531X_PRIO_NIBBLE_MASK;
+ chan->prio_srcs[slot] = src;
+ src_canon = sit9531x_prio_src_canon(sitdev, src);
+ if (!sit9531x_prio_src_usable(src))
+ continue;
+
+ mask |= BIT(src_canon);
+ if (!first[src_canon])
+ first[src_canon] = slot + 1;
+ }
+
+ /*
+ * Assign unconditionally: a source that has left the table has no
+ * slot, and leaving its old one behind would keep reporting it as
+ * listed for as long as the device runs.
+ */
+ for (src = 0; src < SIT9531X_PRIO_NUM_SRC; src++)
+ chan->prio_last[src] = first[src];
+
+ chan->prio_mask = mask;
+}
+
+/* Attempts to release a forced holdover before reporting it stuck. */
+#define SIT9531X_HO_CLEAR_TRIES 3
+
+static int sit9531x_prio_table_read(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u8 *srcs);
+
+/*
+ * First source in a table that the device could lock to now, compared as
+ * canonical codes, or SIT9531X_PRIO_SRC_NONE when no listed source has a
+ * signal.
+ */
+static u8 sit9531x_prio_top_live(const struct sit9531x_dev *sitdev,
+ const u8 *srcs)
+{
+ u8 i, src;
+
+ for (i = 0; i < SIT9531X_PRIO_MAX_SLOTS; i++) {
+ src = srcs[i] & SIT9531X_PRIO_NIBBLE_MASK;
+ src = sit9531x_prio_src_canon(sitdev, src);
+ if (sit9531x_prio_src_usable(src) &&
+ sit9531x_prio_src_live(sitdev, src))
+ return src;
+ }
+
+ return SIT9531X_PRIO_SRC_NONE;
+}
+
+/*
+ * Choose the active selection for a table about to be latched. After a
+ * table write the PLL goes to the source the selection names; it moves to
+ * another on its own only when that source loses its signal, which is an
+ * event, not a state. So the selection has to name a source the PLL can
+ * use, and it follows the priorities the way the DPLL interface defines
+ * automatic mode -- the highest-priority valid input:
+ *
+ * - When the highest-priority source with signal is not the one the
+ * table held before, the priorities now put another source first, and
+ * the selection goes to it.
+ * - Otherwise the write only reorders sources below it, or removes one
+ * the PLL is not on, and the selection stays where it is while that
+ * source is still listed and has signal: a change further down the
+ * table must not pull a PLL off a healthy reference.
+ * - Otherwise the first listed source with signal; with none alive the
+ * first listed one is as good as any.
+ *
+ * A selection that is still listed but has lost its signal is moved too.
+ * The PLL has then fallen back on its own and the selection still names
+ * the dead source; left alone, the next table write sends the PLL back to
+ * it and it unlocks. The device falls back to the best listed source
+ * that has signal, which is the one chosen here, so moving the selection
+ * there does not move the PLL.
+ *
+ * @old is the table the device holds before this write.
+ */
+static u8 sit9531x_prio_activesel_pick(struct sit9531x_dev *sitdev,
+ const u8 *old, const u8 *srcs, u8 cur)
+{
+ u8 top, i;
+
+ top = sit9531x_prio_top_live(sitdev, srcs);
+ if (top != SIT9531X_PRIO_SRC_NONE &&
+ top != sit9531x_prio_top_live(sitdev, old))
+ return top;
+
+ /*
+ * The table is built from canonical codes, so compare in the same
+ * terms: a differential pair selected through its N-lane code is the
+ * P-lane entry.
+ */
+ cur = sit9531x_prio_src_canon(sitdev, cur & SIT9531X_PRIO_NIBBLE_MASK);
+
+ if (sit9531x_prio_src_usable(cur) &&
+ sit9531x_prio_src_live(sitdev, cur))
+ for (i = 0; i < SIT9531X_PRIO_MAX_SLOTS; i++)
+ if (srcs[i] == cur)
+ return cur;
+
+ if (top != SIT9531X_PRIO_SRC_NONE)
+ return top;
+
+ return srcs[0];
+}
+
+static int sit9531x_prio_table_commit(struct sit9531x_dev *sitdev, u8 pll_idx,
+ const u8 *srcs)
+{
+ struct sit9531x_chan *chan = &sitdev->chan[pll_idx];
+ u8 val, last_old, slot, attempt, written = 0, restored = 0;
+ u8 now[SIT9531X_PRIO_MAX_SLOTS];
+ int rc = 0, prg_rc, ho_rc = 0;
+ bool empty, ho_foreign;
+ u16 reg;
+
+ empty = !sit9531x_prio_src_usable(srcs[0]);
+
+ /*
+ * A forced holdover already in place is this driver's own when the
+ * table lists nothing -- the hold an empty table keeps -- or when a
+ * release is still owed from an earlier write. Any other was put
+ * there by the loaded configuration or by a tool, and a table write
+ * is no reason to lift it: the sequence runs under it and leaves it
+ * as found.
+ */
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx, SIT9531X_PLL_REG_HO_CTRL,
+ &val);
+ if (rc)
+ return rc;
+
+ ho_foreign = (val & BIT(SIT9531X_PLL_HO_FORCE_BIT)) &&
+ chan->prio_mask && !chan->ho_release_pending;
+
+ /*
+ * A write that reports an error may still have reached the part, so
+ * a failure here does not prove the bit stayed clear: nothing is
+ * written to the table, but the release below still runs.
+ */
+ rc = sit9531x_write_pll_u8(sitdev, pll_idx, SIT9531X_PLL_REG_HO_CTRL,
+ val | BIT(SIT9531X_PLL_HO_FORCE_BIT));
+ if (rc)
+ goto release;
+
+ usleep_range(10000, 12000);
+
+ /*
+ * Two slots share a register, and this writes every slot, so both
+ * nibbles are known for every register but the last -- build those
+ * bytes outright. Reading first would raise the question of what a
+ * read returns between the write and the latch, and the answer does
+ * not matter if nothing is read.
+ */
+ for (slot = 0; slot + 1 < SIT9531X_PRIO_MAX_SLOTS; slot += 2) {
+ reg = sit9531x_prio_reg(pll_idx, slot);
+
+ val = sit9531x_prio_slot_set(0, slot, srcs[slot]);
+ val = sit9531x_prio_slot_set(val, slot + 1, srcs[slot + 1]);
+
+ /*
+ * Counted before the write: a write that reports an error
+ * may still have reached the part, so the rollback has to
+ * cover that register too.
+ */
+ written = slot + 2;
+ rc = sit9531x_write_u8(sitdev, reg, val);
+ if (rc)
+ goto rollback;
+ }
+
+ /*
+ * The last register carries slot 10 in its high nibble and the
+ * device's active selection in its low one; see
+ * sit9531x_prio_activesel_pick() for how the selection is chosen.
+ * It needs the signal state now, not as of the last poll.
+ *
+ * The slot setter picks its nibble by parity, so the selection is
+ * addressed as the slot past the last one. This register has not
+ * been written yet in this sequence, so the read returns what the
+ * device is running with, and that byte is what a rollback puts
+ * back.
+ *
+ * A table naming no source at all is what removing the last one
+ * asks for. There is nothing to point the selection at -- the code
+ * for no source is not one the selection takes -- so the nibble is
+ * left alone and the PLL is kept in holdover below instead.
+ */
+ reg = sit9531x_prio_reg(pll_idx, slot);
+
+ rc = sit9531x_read_u8(sitdev, reg, &last_old);
+ if (rc)
+ goto rollback;
+
+ val = sit9531x_prio_slot_set(last_old, slot, srcs[slot]);
+
+ if (!empty) {
+ u8 sel = sit9531x_prio_slot_get(val, slot + 1);
+
+ /*
+ * Without a fresh read the pick would run on the last
+ * poll's monitor state, up to a poll period old. A source
+ * that lost its signal since would still look live, and a
+ * selection naming a dead source sends the PLL to it and it
+ * unlocks, so no selection is written without the read: the
+ * slots written so far are rolled back and the request
+ * fails.
+ */
+ rc = sit9531x_input_mon_fetch(sitdev);
+ if (rc)
+ goto rollback;
+ sel = sit9531x_prio_activesel_pick(sitdev, chan->prio_srcs,
+ srcs, sel);
+ val = sit9531x_prio_slot_set(val, slot + 1, sel);
+ }
+
+ written = SIT9531X_PRIO_MAX_SLOTS;
+ rc = sit9531x_write_u8(sitdev, reg, val);
+ if (rc)
+ goto rollback;
+
+rollback:
+ if (rc && written) {
+ /*
+ * Put the slots that may have reached the device back the
+ * way they were, the register whose write reported the error
+ * included. Latching a table that is neither the previous
+ * order nor the requested one hands the reference selection
+ * loop a priority list nobody asked for. The cache is the
+ * table as last read, which is what those slots held; the
+ * last register also carries the selection, so it goes back
+ * as it was read above.
+ */
+ for (slot = 0; slot < written; slot += 2) {
+ u8 old;
+
+ if (slot + 1 < SIT9531X_PRIO_MAX_SLOTS) {
+ old = sit9531x_prio_slot_set(0, slot,
+ chan->prio_srcs[slot]);
+ old = sit9531x_prio_slot_set(old, slot + 1,
+ chan->prio_srcs[slot + 1]);
+ } else {
+ old = last_old;
+ }
+ if (sit9531x_write_u8(sitdev,
+ sit9531x_prio_reg(pll_idx, slot),
+ old))
+ break;
+
+ restored = slot + 2;
+ }
+ written = restored;
+ }
+
+ /*
+ * Latch unconditionally: the slots that reached the device are in
+ * the table regardless, so the latch keeps hardware and the cache
+ * refresh below consistent with what was actually written.
+ */
+ prg_rc = sit9531x_prio_prg_commit(sitdev);
+ if (prg_rc && !rc)
+ rc = prg_rc;
+
+ /*
+ * Refresh the cache so a get that follows a set does not have to
+ * wait for the next poll. After a complete write that is the table
+ * just written. After a failure it is whatever the device holds
+ * now -- part request, part restore -- so read it back rather than
+ * piece it together: the membership test decides what a failed
+ * request rolls back, and it must not answer for writes that did not
+ * land. A read-back that fails too leaves the next poll to do it.
+ */
+ if (!rc) {
+ sit9531x_prio_mask_build(sitdev, pll_idx, srcs);
+ memcpy(chan->seen_srcs, srcs, sizeof(chan->seen_srcs));
+ chan->seen_valid = true;
+ } else if (!sit9531x_prio_table_read(sitdev, pll_idx, now)) {
+ sit9531x_prio_mask_build(sitdev, pll_idx, now);
+ memcpy(chan->seen_srcs, now, sizeof(chan->seen_srcs));
+ chan->seen_valid = true;
+ }
+
+release:
+ /*
+ * A table that names no source keeps the PLL in the holdover forced
+ * above: that is the one state in which it follows no input, which
+ * is what disconnecting every input asks for. The selection nibble
+ * alone would not do it -- it still names the old source, and the
+ * PLL keeps following that one for as long as it has signal. The
+ * next table write that lists a source releases it.
+ *
+ * Judged on the table the device holds now, as the refresh above
+ * read it, not on the one requested: a write that failed on a PLL
+ * listing nothing leaves it listing nothing, and releasing the hold
+ * then would hand the PLL back to the source the selection still
+ * names while every pin reports disconnected. An empty table whose
+ * latch failed is the same case from the other side.
+ */
+ if (!chan->prio_mask) {
+ dev_dbg(sitdev->dev,
+ "PLL%c: no source listed, holdover kept\n",
+ 'A' + pll_idx);
+ chan->ho_release_pending = false;
+ return rc;
+ }
+
+ if (ho_foreign) {
+ dev_dbg(sitdev->dev,
+ "PLL%c: holdover was forced before the write, left in place\n",
+ 'A' + pll_idx);
+ return rc;
+ }
+
+ /*
+ * Release the forced holdover. Apart from an empty table, nothing
+ * in the driver keeps this bit set, and a retry of the same request
+ * finds the table already as asked and does not come back here, so
+ * a PLL left with it would report holdover until some unrelated
+ * table write on the same PLL cleared it. Retry before giving up;
+ * if it stays set, say so and leave the release owed to the poll,
+ * which retries it every tick until it lands.
+ */
+ for (attempt = 0; attempt < SIT9531X_HO_CLEAR_TRIES; attempt++) {
+ ho_rc = sit9531x_update_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_HO_CTRL,
+ BIT(SIT9531X_PLL_HO_FORCE_BIT),
+ 0);
+ if (!ho_rc)
+ break;
+ usleep_range(1000, 2000);
+ }
+ if (ho_rc) {
+ dev_err(sitdev->dev, "PLL%c left in forced holdover: %d\n",
+ 'A' + pll_idx, ho_rc);
+ if (!rc)
+ rc = ho_rc;
+ }
+ chan->ho_release_pending = !!ho_rc;
+
+ return rc;
+}
+
+/*
+ * sit9531x_prio_table_read - read a PLL's priority-table source codes
+ * @srcs: output array of SIT9531X_PRIO_MAX_SLOTS source codes
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+static int sit9531x_prio_table_read(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u8 *srcs)
+{
+ u8 val, slot;
+ int rc;
+
+ for (slot = 0; slot < SIT9531X_PRIO_MAX_SLOTS; slot++) {
+ rc = sit9531x_read_u8(sitdev,
+ sit9531x_prio_reg(pll_idx, slot), &val);
+ if (rc)
+ return rc;
+
+ srcs[slot] = sit9531x_prio_slot_get(val, slot);
+ }
+
+ return 0;
+}
+
+/*
+ * Rewrite a PLL's table for a new member set, unless it would come out as
+ * the table already holds: every write forces the PLL into holdover for
+ * the length of the sequence, so one that changes nothing is a
+ * disturbance nobody asked for.
+ */
+static int sit9531x_prio_table_apply(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u16 members)
+{
+ u8 srcs[SIT9531X_PRIO_MAX_SLOTS];
+
+ if (hweight16(members) > SIT9531X_PRIO_MAX_SLOTS)
+ return -ENOSPC;
+
+ sit9531x_prio_table_build(sitdev, pll_idx, members, srcs);
+ if (!memcmp(srcs, sitdev->chan[pll_idx].prio_srcs, sizeof(srcs)))
+ return 0;
+
+ return sit9531x_prio_table_commit(sitdev, pll_idx, srcs);
+}
+
+/*
+ * sit9531x_input_prio_set - set an input's priority on a PLL
+ * @input_idx: input source in hardware encoding (0-11, see
+ * sit9531x_input_hw_src())
+ * @prio: priority, lower is preferred
+ *
+ * Records the priority and, when the source is in the PLL's table,
+ * rebuilds the table from the configured priorities. A source that is
+ * not in the table keeps the priority for when it is connected: that is
+ * the pin's state, and it belongs to the state setter. Other sources
+ * keep theirs either way, so no sibling's priority moves.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ *
+ * Return: 0 on success, -EINVAL for a bad PLL or source, <0 on error
+ */
+int sit9531x_input_prio_set(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u8 input_idx, u8 prio)
+{
+ struct sit9531x_chan *chan;
+ u8 old_prio;
+ u16 old_known;
+ int rc;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ if (pll_idx >= SIT9531X_NUM_PLLS)
+ return -EINVAL;
+ input_idx = sit9531x_prio_src_canon(sitdev, input_idx);
+ if (input_idx >= SIT9531X_PRIO_NUM_SRC)
+ return -EINVAL;
+
+ chan = &sitdev->chan[pll_idx];
+ old_prio = chan->cfg_prio[input_idx];
+ old_known = chan->cfg_known;
+ chan->cfg_prio[input_idx] = prio;
+ chan->cfg_known |= BIT(input_idx);
+
+ if (!(chan->prio_mask & BIT(input_idx)))
+ return 0;
+
+ /*
+ * The table is built from the configured priority, so it has to be
+ * recorded before the rebuild; a rebuild that failed takes the
+ * record back with it. Left in place, a refused priority would be
+ * reported by the getter, notified by the poll, and written by the
+ * next rebuild for any other reason.
+ */
+ rc = sit9531x_prio_table_apply(sitdev, pll_idx, chan->prio_mask);
+ if (rc) {
+ chan->cfg_prio[input_idx] = old_prio;
+ chan->cfg_known = old_known;
+ }
+
+ return rc;
+}
+
+/*
+ * sit9531x_input_prio_remove - drop an input from a PLL's priority table
+ * @input_idx: input source in hardware encoding
+ *
+ * Rebuilds the table without the source, which makes a disconnected
+ * input ineligible for automatic reference selection, not just gated at
+ * the input buffer. The source keeps its configured priority for when it
+ * comes back. Removing a source that is absent succeeds without touching
+ * the table. Removing the last one leaves a table that names no source;
+ * the commit then keeps the PLL in holdover, which is what disconnecting
+ * every input asks for.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int sit9531x_input_prio_remove(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u8 input_idx)
+{
+ struct sit9531x_chan *chan;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ if (pll_idx >= SIT9531X_NUM_PLLS)
+ return -EINVAL;
+ input_idx = sit9531x_prio_src_canon(sitdev, input_idx);
+ if (input_idx >= SIT9531X_PRIO_NUM_SRC)
+ return -EINVAL;
+
+ chan = &sitdev->chan[pll_idx];
+ if (!(chan->prio_mask & BIT(input_idx)))
+ return 0;
+
+ return sit9531x_prio_table_apply(sitdev, pll_idx,
+ chan->prio_mask & ~BIT(input_idx));
+}
+
+/*
+ * sit9531x_input_prio_add - make an input eligible in a PLL's table
+ * @input_idx: input source in hardware encoding
+ *
+ * Puts the source back into the table at its configured priority; one the
+ * PLL never listed has the lowest slot (see sit9531x_prio_cfg_seed()). A
+ * source that is already listed leaves the table untouched.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ *
+ * Return: 0 on success, -ENOSPC when the table cannot hold another
+ * source, <0 on error
+ */
+int sit9531x_input_prio_add(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u8 input_idx)
+{
+ struct sit9531x_chan *chan;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ if (pll_idx >= SIT9531X_NUM_PLLS)
+ return -EINVAL;
+ input_idx = sit9531x_prio_src_canon(sitdev, input_idx);
+ if (input_idx >= SIT9531X_PRIO_NUM_SRC)
+ return -EINVAL;
+
+ chan = &sitdev->chan[pll_idx];
+ if (chan->prio_mask & BIT(input_idx))
+ return 0;
+
+ return sit9531x_prio_table_apply(sitdev, pll_idx,
+ chan->prio_mask | BIT(input_idx));
+}
+
+/* Per-slot DIVO base register offsets (6 slots per page) */
+static const u8 clkout_odr_divn_base[] = {
+ 0x14, 0x24, 0x34, 0x44, 0x54, 0x64
+};
+
+/* XO doubler register */
+#define SIT9531X_REG_XO2_GENERIC SIT9531X_REG(0x00, 0x2D)
+#define SIT9531X_XO_DOUBLER_ENB_BIT 7 /* inverted: 0 = enabled */
+
+/* VCO frequency bands (Hz) */
+#define SIT9531X_FVCO_LOWBAND_MIN 4915200000ULL
+#define SIT9531X_FVCO_LOWBAND_MAX 5898240000ULL
+#define SIT9531X_FVCO_HIGHBAND_MIN 6875000000ULL
+#define SIT9531X_FVCO_HIGHBAND_MAX 7812500000ULL
+
+/* The output divider is a 34-bit field */
+#define SIT9531X_DIVO_MAX GENMASK_ULL(33, 0)
+
+/*
+ * sit9531x_is_xo_doubler_enabled - check if Fref doubler is active
+ *
+ * Register 0x2D bit 7 is active-low: 0 = doubler enabled, 1 = disabled.
+ *
+ * Return: 1 if enabled, 0 if disabled, <0 on error
+ */
+static int sit9531x_is_xo_doubler_enabled(struct sit9531x_dev *sitdev)
+{
+ u8 val;
+ int rc;
+
+ rc = sit9531x_read_u8(sitdev, SIT9531X_REG_XO2_GENERIC, &val);
+ if (rc)
+ return rc;
+
+ return (~val >> SIT9531X_XO_DOUBLER_ENB_BIT) & 1u;
+}
+
+/*
+ * DIVN as a fixed-point value: int_part plus fracn/fracd, carried with
+ * SIT9531X_DIVN_SCALE steps per unit. The scale keeps a whole DIVN
+ * well inside s64 while resolving far below the parts-per-trillion the
+ * frequency offset is reported in.
+ */
+static s64 sit9531x_divn_fixed(u32 int_part, s64 fracn, u64 fracd)
+{
+ s64 whole = (s64)int_part * SIT9531X_DIVN_SCALE;
+ u64 frac;
+
+ if (!fracd)
+ return whole;
+
+ frac = mul_u64_u64_div_u64(abs(fracn), SIT9531X_DIVN_SCALE, fracd);
+
+ return fracn < 0 ? whole - (s64)frac : whole + (s64)frac;
+}
+
+/*
+ * sit9531x_divn_static - read the configured DIVN of a PLL
+ * @sitdev: device pointer
+ * @pll_idx: PLL index (0-3)
+ * @divn: result, fixed point as per sit9531x_divn_fixed()
+ *
+ * Reads PLL page regs 0x30 (integer part), 0x32-0x35 (numerator) and
+ * 0x38-0x3B (denominator). The numerator is a two's complement 32-bit
+ * value, so DIVN can sit below the integer part, and the denominator
+ * register holds the divisor minus one.
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int sit9531x_divn_static(struct sit9531x_dev *sitdev, u8 pll_idx,
+ s64 *divn)
+{
+ u32 int_part, fracn_raw = 0, fracd_raw = 0;
+ u64 fracd;
+ s64 fracn;
+ int rc, i;
+ u8 v;
+
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DIVN_INT, &v);
+ if (rc)
+ return rc;
+ int_part = v;
+
+ for (i = 3; i >= 0; i--) {
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DIVN_NUM + i, &v);
+ if (rc)
+ return rc;
+ fracn_raw = (fracn_raw << 8) | v;
+ }
+
+ for (i = 3; i >= 0; i--) {
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DIVN_DEN + i, &v);
+ if (rc)
+ return rc;
+ fracd_raw = (fracd_raw << 8) | v;
+ }
+
+ /*
+ * NUM/DEN is the fractional part of DIVN, so |NUM| is below DEN by
+ * construction. A pair that says otherwise did not come from a
+ * programmed divider, and handing it on would divide by a
+ * denominator small enough for the quotient to leave u64 -- which
+ * is a divide-error exception on x86, not a value a caller could
+ * reject.
+ */
+ fracn = (s32)fracn_raw;
+ fracd = (u64)fracd_raw + 1;
+ if ((u64)abs(fracn) >= fracd)
+ return -ENODATA;
+
+ *divn = sit9531x_divn_fixed(int_part, fracn, fracd);
+
+ return 0;
+}
+
+/* PLLB and PLLD run their VCO in the high band, PLLA and PLLC in the low. */
+static bool sit9531x_fvco_in_band(u8 pll_idx, u64 fvco)
+{
+ if (pll_idx == 1 || pll_idx == 3)
+ return fvco >= SIT9531X_FVCO_HIGHBAND_MIN &&
+ fvco <= SIT9531X_FVCO_HIGHBAND_MAX;
+ return fvco >= SIT9531X_FVCO_LOWBAND_MIN &&
+ fvco <= SIT9531X_FVCO_LOWBAND_MAX;
+}
+
+/*
+ * sit9531x_get_fvco - read VCO frequency from chip's DIVN registers
+ *
+ * Fvco = Fref * DIVN, where DIVN comes from sit9531x_divn_static() and
+ * Fref = xtal_freq << doubler. DIVN is the steady-state Fvco/Fref
+ * target programmed by the NVM blob and is authoritative in both
+ * free-run and sync modes.
+ *
+ * Return: 0 with *fvco set on success, -ENODATA when DIVN is not
+ * programmed (dormant PLL), or the register access error. A bus
+ * failure is never folded into the -ENODATA case, so callers can fail
+ * a request instead of acting on a guessed rate.
+ */
+static int sit9531x_get_fvco(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u64 *fvco)
+{
+ int doubler, rc;
+ u64 fref;
+ s64 divn;
+
+ rc = sit9531x_divn_static(sitdev, pll_idx, &divn);
+ if (rc)
+ return rc;
+ if (divn <= 0)
+ return -ENODATA;
+
+ doubler = sit9531x_is_xo_doubler_enabled(sitdev);
+ if (doubler < 0)
+ return doubler;
+
+ fref = (u64)sitdev->xtal_freq << doubler;
+
+ /*
+ * Round to the nearest hertz: DIVN is carried in fixed point, so a
+ * fraction such as 1/6 is already floored once, and flooring the
+ * product again reads an exact 5 GHz VCO as 4999999999 Hz -- which
+ * then refuses every rate the VCO divides exactly.
+ */
+ *fvco = mul_u64_u64_div_u64(2 * fref, (u64)divn, SIT9531X_DIVN_SCALE);
+ *fvco = (*fvco + 1) / 2;
+
+ /*
+ * No VCO of this family runs below the low band, so a product under
+ * it is not a rate: a DIVN of a cycle or less, say, which the check
+ * above lets through and which a profile that never programmed the
+ * divider can leave behind. Callers scale 34-bit register fields by
+ * 1e12 / Fvco or more, and for a rate far enough below the band that
+ * quotient leaves u64 -- a divide-error exception on x86, not a
+ * value they could reject. Report the unprogrammed divider instead.
+ */
+ if (*fvco < SIT9531X_FVCO_LOWBAND_MIN)
+ return -ENODATA;
+
+ /*
+ * A rate outside the band the PLL's VCO runs in usually means
+ * Fref * DIVN is not what this PLL runs at -- a PLL fed from another
+ * PLL rather than from the XO, for one. The rate derived from the
+ * registers is still the only estimate there is, so a read reports
+ * what it derives; a set refuses to program a divider against it
+ * (sit9531x_output_divo_calc()), since substituting the band edge
+ * would program against a rate nothing supports and report the
+ * result as exact. Say so once per PLL.
+ */
+ if (!sit9531x_fvco_in_band(pll_idx, *fvco) &&
+ !(sitdev->fvco_band_warned & BIT(pll_idx))) {
+ sitdev->fvco_band_warned |= BIT(pll_idx);
+ dev_warn(sitdev->dev,
+ "PLL%c: Fref * DIVN = %llu Hz is outside its VCO band\n",
+ 'A' + pll_idx, *fvco);
+ }
+
+ return 0;
+}
+
+/* Latch a change to a PLL page with a small update. */
+static int sit9531x_pll_small_update(struct sit9531x_dev *sitdev, u8 pll_idx)
+{
+ return sit9531x_write_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_SMALL_UPDATE,
+ SIT9531X_SMALL_UPDATE_CMD);
+}
+
+/*
+ * sit9531x_output_phase_flush - flush the output phase of a PLL
+ *
+ * Fires the chip's on-demand phase-flush (PHFL) so every output divider
+ * of @pll_idx restarts aligned to the PLL phase. Without it a rewritten
+ * DIVO keeps counting from an arbitrary point and the output edge lands
+ * with a persistent offset against the tracked reference (only a power
+ * cycle realigned it).
+ *
+ * The sequence mirrors the documented procedure: arm the on-demand PHFL and
+ * latch it with the PLL-page small-change update, then select the
+ * in-register phase trigger on Page 0 and pulse it. The Page 0 trigger
+ * register is touched read-modify-write so the unrelated OEb bits are
+ * preserved.
+ *
+ * A PLL the loaded configuration builds without the phase-flush feature
+ * (PLL page 0x47 bit 7 clear) has nothing to fire; it is restarted
+ * instead, which restarts its output dividers from the PLL phase. That
+ * is what SiTime's Dely_program_output.py does for such a PLL.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+static int sit9531x_output_phase_flush(struct sit9531x_dev *sitdev, u8 pll_idx)
+{
+ u8 ctrl, orig, phfl, armed_mask = 0, parked = 0, i;
+ int rc, ret;
+
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx, SIT9531X_PLL_REG_CONFIG47,
+ &phfl);
+ if (rc)
+ return rc;
+ if (!(phfl & SIT9531X_PLL_CONFIG47_PHFL_EN))
+ return sit9531x_write_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DIRECTIVES,
+ SIT9531X_PLL_DIRECTIVE_RESTART);
+
+ /*
+ * The trigger below is chip-global: every PLL whose on-demand flush
+ * is armed answers it. Note which ones the loaded profile arms --
+ * this one so its setting can be put back, the others so they can
+ * be parked for the pulse and do not realign outputs nobody asked
+ * to move.
+ */
+ for (i = 0; i < SIT9531X_NUM_PLLS; i++) {
+ rc = sit9531x_read_pll_u8(sitdev, i, SIT9531X_PLL_REG_PHFL_CTRL,
+ &phfl);
+ if (rc)
+ return rc;
+ if (phfl & SIT9531X_PLL_PHFL_ON_DEMAND_EN)
+ armed_mask |= BIT(i);
+ }
+
+ for (i = 0; i < SIT9531X_NUM_PLLS; i++) {
+ if (i == pll_idx || !(armed_mask & BIT(i)))
+ continue;
+ /*
+ * A clear whose write reported an error may still have
+ * landed, latched or not: put it back either way.
+ */
+ parked |= BIT(i);
+ rc = sit9531x_update_pll_u8(sitdev, i,
+ SIT9531X_PLL_REG_PHFL_CTRL,
+ SIT9531X_PLL_PHFL_ON_DEMAND_EN, 0);
+ if (rc)
+ goto unpark;
+ rc = sit9531x_pll_small_update(sitdev, i);
+ if (rc)
+ goto unpark;
+ }
+
+ /*
+ * Arm the on-demand phase-flush on the PLL page. An arm whose write
+ * failed may have landed too, so it goes through the disarm below.
+ */
+ rc = sit9531x_update_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_PHFL_CTRL,
+ SIT9531X_PLL_PHFL_ON_DEMAND_EN,
+ SIT9531X_PLL_PHFL_ON_DEMAND_EN);
+ if (rc)
+ goto disarm;
+
+ /*
+ * Latch it with the PLL small-change update. Written whole, like
+ * every other issue of this directive: the register is a command
+ * register, and a read-modify-write skips the write entirely when
+ * the bit still reads back set from the previous command.
+ */
+ rc = sit9531x_pll_small_update(sitdev, pll_idx);
+ if (rc)
+ goto disarm;
+
+ /*
+ * Select the in-register phase trigger, preserving the OEb bits.
+ * Remember the original register value (with the trigger de-asserted)
+ * so the trigger-source select can be restored once the pulse has
+ * fired.
+ */
+ rc = sit9531x_read_u8(sitdev, SIT9531X_REG_GPIO_FUNC_CTRL1, &ctrl);
+ if (rc)
+ goto disarm;
+
+ orig = ctrl & ~SIT9531X_DIVO_PHASE_TRIG;
+ ctrl = orig | SIT9531X_DIVO_PHASE_SEL_REG;
+ rc = sit9531x_write_u8(sitdev, SIT9531X_REG_GPIO_FUNC_CTRL1, ctrl);
+ if (rc)
+ goto restore;
+
+ /*
+ * Pulse the phase trigger. No explicit delay is needed between the
+ * set and clear writes: each I2C transaction takes far longer than
+ * any minimum pulse width.
+ */
+ rc = sit9531x_write_u8(sitdev, SIT9531X_REG_GPIO_FUNC_CTRL1,
+ ctrl | SIT9531X_DIVO_PHASE_TRIG);
+
+restore:
+ /*
+ * Restore the original trigger-source select. The pulse above has
+ * already latched the flush, so a one-shot flush must not leave the
+ * phase trigger permanently pinned to the in-register source. This
+ * runs even when the pulse write failed, and when the select write
+ * itself reported a failure, since the transfer may have reached
+ * the part all the same; otherwise a failed flush would keep a
+ * hardware trigger source hijacked. The restore error is only
+ * surfaced when it would not mask the earlier failure.
+ */
+ ret = sit9531x_write_u8(sitdev, SIT9531X_REG_GPIO_FUNC_CTRL1, orig);
+ if (ret && !rc)
+ rc = ret;
+
+disarm:
+ /*
+ * Put the on-demand flush enable back as the profile had it. One the
+ * profile left clear must not stay armed: a later assertion of the
+ * restored trigger source would re-flush every output divider of this
+ * PLL. One the profile armed -- a board realigning from a GPIO, say
+ * -- must not be switched off by an unrelated rate change.
+ */
+ if (!(armed_mask & BIT(pll_idx))) {
+ ret = sit9531x_update_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_PHFL_CTRL,
+ SIT9531X_PLL_PHFL_ON_DEMAND_EN, 0);
+ if (!ret)
+ ret = sit9531x_pll_small_update(sitdev, pll_idx);
+ if (ret && !rc)
+ rc = ret;
+ }
+
+unpark:
+ for (i = 0; i < SIT9531X_NUM_PLLS; i++) {
+ if (!(parked & BIT(i)))
+ continue;
+ ret = sit9531x_update_pll_u8(sitdev, i,
+ SIT9531X_PLL_REG_PHFL_CTRL,
+ SIT9531X_PLL_PHFL_ON_DEMAND_EN,
+ SIT9531X_PLL_PHFL_ON_DEMAND_EN);
+ if (!ret)
+ ret = sit9531x_pll_small_update(sitdev, i);
+ if (ret && !rc)
+ rc = ret;
+ }
+
+ return rc;
+}
+
+/*
+ * sit9531x_output_divo_calc - work out an output's divider and its VCO
+ *
+ * Separated from the write so a caller that programs more than the
+ * divider in one sequence can compute the value before it enters the
+ * programming state.
+ */
+static int sit9531x_output_divo_calc(struct sit9531x_dev *sitdev, u8 out_idx,
+ u8 pll_idx, u64 frequency, u64 *fvco_out,
+ u64 *divo_out)
+{
+ const struct sit9531x_chip_info *info = sitdev->info;
+ u64 fvco, divo;
+ int rc;
+
+ if (out_idx >= info->num_outputs || pll_idx >= SIT9531X_NUM_PLLS)
+ return -EINVAL;
+
+ if (!frequency)
+ return -EINVAL;
+
+ /*
+ * The core validates the request against the supported ranges with
+ * the value narrowed to u32 but hands the full u64 down, so a value
+ * like U32_MAX + 1 Hz validates as 1 Hz. Reject anything that does
+ * not fit the narrowed width the validation actually covered.
+ */
+ if (frequency > U32_MAX)
+ return -EINVAL;
+
+ /*
+ * sit9531x_get_fvco() returns the register-derived rate, so a
+ * frequency get and a frequency set divide the same number. A VCO
+ * that cannot be read fails the request: programming a divider from a
+ * guessed rate would put the output far from what was asked for
+ * while reporting success.
+ */
+ rc = sit9531x_get_fvco(sitdev, pll_idx, &fvco);
+ if (rc)
+ return rc == -ENODATA ? -ENODEV : rc;
+
+ /*
+ * Fref * DIVN outside the VCO band is not what the PLL runs at, and
+ * sit9531x_get_fvco() has said so. A divider computed against it
+ * would put the output at some other rate while the exactness check
+ * below passes and the read-back, dividing the same number, confirms
+ * the request. Refuse the set instead.
+ */
+ if (!sit9531x_fvco_in_band(pll_idx, fvco)) {
+ dev_dbg(sitdev->dev,
+ "out%u: PLL%c Fvco %llu Hz is outside its VCO band, rate not settable\n",
+ out_idx, 'A' + pll_idx, fvco);
+ return -EINVAL;
+ }
+
+ /*
+ * Round to nearest rather than down: flooring picks the worse of the
+ * two adjacent dividers whenever the remainder is above half the
+ * request.
+ */
+ divo = div64_u64(fvco + frequency / 2, frequency);
+ if (!divo)
+ return -EINVAL;
+
+ /* DIVO is a 34-bit field; refuse a wider divider rather than truncate it. */
+ if (divo > SIT9531X_DIVO_MAX)
+ return -EINVAL;
+
+ /*
+ * The output divider is an integer divider of the VCO, so the only
+ * rates the part can make are Fvco/N. An output pin that lists no
+ * supported frequencies advertises a continuous range, because the
+ * divisors cannot be enumerated ahead of a known Fvco, so a request
+ * for a rate between two of them arrives here. Refuse it: running
+ * the output at the nearest divider instead and reporting success
+ * would leave the pin several percent off what was asked for with
+ * nothing saying so.
+ */
+ if (div64_u64(fvco, divo) != frequency) {
+ dev_dbg(sitdev->dev,
+ "out%u: %llu Hz is not Fvco/N (Fvco=%llu, nearest %llu Hz)\n",
+ out_idx, frequency, fvco, div64_u64(fvco, divo));
+ return -EINVAL;
+ }
+
+ dev_dbg(sitdev->dev,
+ "out%u: Fvco=%llu freq=%llu DIVO=%llu (effective %llu Hz)\n",
+ out_idx, fvco, frequency, divo, div64_u64(fvco, divo));
+
+ *fvco_out = fvco;
+ *divo_out = divo;
+
+ return 0;
+}
+
+/*
+ * sit9531x_output_divo_write - write the five DIVO bytes of an output
+ *
+ * The caller must already be in the programming state. Bytes written
+ * before a failure are put back, so the output keeps the divider it had
+ * rather than a mixture of the two.
+ */
+static int sit9531x_output_divo_write(struct sit9531x_dev *sitdev, u8 out_idx,
+ u64 divo)
+{
+ const struct sit9531x_chip_info *info = sitdev->info;
+ u8 slot, page, base_reg, divo_bytes[5], old_bytes[5], msb_old;
+ int rc, j, rb_rc;
+ u8 written = 0;
+
+ /* Map output index to physical slot */
+ slot = info->clkout_map[out_idx];
+
+ /* Determine page and per-page slot register */
+ if (slot > SIT9531X_PAGE_OUTSYS0_SLOT_MAX)
+ page = SIT9531X_PAGE_OUTSYS1;
+ else
+ page = SIT9531X_PAGE_OUTSYS0;
+ base_reg = clkout_odr_divn_base[slot % 6];
+
+ divo_bytes[0] = (divo >> 0) & 0xFF;
+ divo_bytes[1] = (divo >> 8) & 0xFF;
+ divo_bytes[2] = (divo >> 16) & 0xFF;
+ divo_bytes[3] = (divo >> 24) & 0xFF;
+ divo_bytes[4] = (divo >> 32) & 0x03; /* only bits [1:0] */
+
+ for (j = 0; j < 5; j++) {
+ rc = sit9531x_read_u8(sitdev,
+ SIT9531X_REG(page, base_reg - j),
+ &old_bytes[j]);
+ if (rc)
+ return rc;
+ }
+
+ msb_old = old_bytes[4];
+ divo_bytes[4] |= msb_old & 0xFC;
+
+ for (j = 0; j < 5; j++) {
+ rc = sit9531x_write_u8(sitdev,
+ SIT9531X_REG(page, base_reg - j),
+ divo_bytes[j]);
+ if (rc)
+ goto rollback;
+ written++;
+ }
+
+ return 0;
+
+rollback:
+ /*
+ * The byte whose write reported the error may still have reached
+ * the part, so it is put back along with the ones that did.
+ */
+ for (j = 0; j <= written && j < 5; j++) {
+ rb_rc = sit9531x_write_u8(sitdev,
+ SIT9531X_REG(page, base_reg - j),
+ old_bytes[j]);
+ if (rb_rc) {
+ dev_err(sitdev->dev,
+ "out%u: DIVO rollback failed (%d), the divider is part old and part new\n",
+ out_idx, rb_rc);
+ if (!rc)
+ rc = rb_rc;
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * Read an output's divider back from the device.
+ *
+ * Return: 0 with *divo set, -ENODATA when the divider is not programmed,
+ * or the register access error
+ */
+static int sit9531x_output_divo_read(struct sit9531x_dev *sitdev, u8 out_idx,
+ u64 *divo)
+{
+ u8 slot, page, base_reg, v;
+ int rc, j;
+
+ slot = sitdev->info->clkout_map[out_idx];
+ if (slot > SIT9531X_PAGE_OUTSYS0_SLOT_MAX)
+ page = SIT9531X_PAGE_OUTSYS1;
+ else
+ page = SIT9531X_PAGE_OUTSYS0;
+ base_reg = clkout_odr_divn_base[slot % 6];
+
+ *divo = 0;
+ for (j = 4; j >= 0; j--) {
+ rc = sit9531x_read_u8(sitdev,
+ SIT9531X_REG(page, base_reg - j), &v);
+ if (rc)
+ return rc;
+ if (j == 4)
+ v &= 0x03;
+ *divo = (*divo << 8) | v;
+ }
+
+ return *divo ? 0 : -ENODATA;
+}
+
+/*
+ * Phase adjust (PRG_RST_DELAY register-based).
+ *
+ * The chip exposes a per-output 34-bit coarse delay measured in VCO
+ * clock periods plus a 3-bit fine delay in fixed 30 ps steps. The
+ * five bytes PROG6..PROG2 hold the field across registers:
+ * base + 0 PROG6 [7:5] OPSTG_VCASC_BUMP (preserved via RMW)
+ * [4:2] PRG_RST_FINE_DELAY
+ * [1:0] PRG_RST_DELAY[33:32]
+ * base + 1 PROG5 PRG_RST_DELAY[31:24]
+ * base + 2 PROG4 PRG_RST_DELAY[23:16]
+ * base + 3 PROG3 PRG_RST_DELAY[15:8]
+ * base + 4 PROG2 PRG_RST_DELAY[7:0]
+ *
+ * Slots 0-5 live on Page 3, slots 6-11 on Page 4, with each slot's
+ * block at base = 0x15 + 16 * (slot % 6); the slot is the physical
+ * output position from clkout_map[], not the logical output index.
+ *
+ * The chip only supports unsigned positive delay. Requests are folded
+ * modulo one output period: positive delays wrap naturally and a negative
+ * phase adjustment (advance) is rendered as (T_out - |phase|).
+ */
+
+/*
+ * Register of byte @i (PROG6 first) of an output's PRG_RST_DELAY block.
+ *
+ * The logical output index maps to the chip's physical output slot. On
+ * SiT95317 the eight logical outputs land on chip slots {0, 3, 4, 5, 7,
+ * 8, 9, 11}; on SiT95316 the map is identity. Page and base address the
+ * slot, not the logical index.
+ */
+static unsigned int sit9531x_output_prg_reg(struct sit9531x_dev *sitdev,
+ u8 out_idx, u8 i)
+{
+ u8 slot = sitdev->info->clkout_map[out_idx];
+ u8 page = (slot > SIT9531X_PAGE_OUTSYS0_SLOT_MAX) ?
+ SIT9531X_PAGE_OUTSYS1 : SIT9531X_PAGE_OUTSYS0;
+ u8 base = SIT9531X_OUT_PRG_DELAY_BASE +
+ SIT9531X_OUT_PRG_SLOT_STRIDE * (slot % 6);
+
+ return SIT9531X_REG(page, base + i);
+}
+
+/* Read the PRG_RST_DELAY bytes of an output, PROG6 first. */
+static int sit9531x_output_phase_bytes_read(struct sit9531x_dev *sitdev,
+ u8 out_idx, u8 *bytes)
+{
+ int rc;
+ u8 i;
+
+ for (i = 0; i < SIT9531X_OUT_PRG_BYTES; i++) {
+ rc = sit9531x_read_u8(sitdev,
+ sit9531x_output_prg_reg(sitdev, out_idx, i),
+ &bytes[i]);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+/*
+ * Encode a quantized delay into the register bytes. @old_bytes supplies
+ * the PROG6 bits that are not the delay's, which are preserved.
+ *
+ * The register value carries the divider's own settling time: the
+ * quantizer works in the delay the caller asked for, the register wants
+ * that plus the two VCO cycles the divider spends acting on it, and a
+ * request of zero still waits those two. Only the register value carries
+ * them; @coarse stays the requested delay, which is what gets cached.
+ */
+static void sit9531x_output_phase_bytes_build(const u8 *old_bytes, u64 coarse,
+ u8 fine, u8 *new_bytes)
+{
+ u64 prg_coarse = coarse + SIT9531X_OUT_PRG_DIVO_CYCLES;
+
+ /* PROG6 RMW: preserve OPSTG_VCASC_BUMP in [7:5] */
+ new_bytes[0] = old_bytes[0] & SIT9531X_OUT_PRG_OPSTG_MASK;
+ new_bytes[0] |= (fine << SIT9531X_OUT_PRG_FINE_SHIFT) &
+ SIT9531X_OUT_PRG_FINE_MASK;
+ new_bytes[0] |= (u8)((prg_coarse >> 32) &
+ SIT9531X_OUT_PRG_COARSE_HI_MASK);
+ new_bytes[1] = (u8)((prg_coarse >> 24) & 0xFF);
+ new_bytes[2] = (u8)((prg_coarse >> 16) & 0xFF);
+ new_bytes[3] = (u8)((prg_coarse >> 8) & 0xFF);
+ new_bytes[4] = (u8)(prg_coarse & 0xFF);
+}
+
+/*
+ * Write an output's delay bytes. The caller must already be in the
+ * programming state. On a failure every byte is put back, the one whose
+ * write reported the error included, since it may have reached the part.
+ */
+static int sit9531x_output_phase_bytes_write(struct sit9531x_dev *sitdev,
+ u8 out_idx, const u8 *new_bytes,
+ const u8 *old_bytes)
+{
+ int rc, ret, rb_rc = 0;
+ u8 i;
+
+ for (i = 0; i < SIT9531X_OUT_PRG_BYTES; i++) {
+ rc = sit9531x_write_u8(sitdev,
+ sit9531x_output_prg_reg(sitdev, out_idx, i),
+ new_bytes[i]);
+ if (rc)
+ goto rollback;
+ }
+
+ return 0;
+
+rollback:
+ for (i = 0; i < SIT9531X_OUT_PRG_BYTES; i++) {
+ ret = sit9531x_write_u8(sitdev,
+ sit9531x_output_prg_reg(sitdev, out_idx, i),
+ old_bytes[i]);
+ if (ret && !rb_rc)
+ rb_rc = ret;
+ }
+ if (rb_rc) {
+ dev_err(sitdev->dev,
+ "out%u: phase-adjust rollback failed (%d), the delay registers are part old and part new\n",
+ out_idx, rb_rc);
+ if (!rc)
+ rc = rb_rc;
+ }
+
+ return rc;
+}
+
+/*
+ * Error of realizing @abs_ps as @cycles whole VCO cycles plus the fine
+ * steps that come nearest, which are returned through @fine.
+ */
+static u64 sit9531x_output_phase_quant_err(u64 abs_ps, u64 fvco, u64 cycles,
+ u8 *fine)
+{
+ u64 cycles_ps, steps = 0;
+
+ cycles_ps = mul_u64_u64_div_u64(cycles, 1000000000000ULL, fvco);
+ if (abs_ps > cycles_ps)
+ steps = div64_u64(abs_ps - cycles_ps +
+ SIT9531X_OUT_PRG_FINE_STEP_PS / 2,
+ SIT9531X_OUT_PRG_FINE_STEP_PS);
+ *fine = min_t(u64, steps, SIT9531X_OUT_PRG_FINE_MAX);
+
+ return abs_diff(abs_ps, cycles_ps +
+ (u64)*fine * SIT9531X_OUT_PRG_FINE_STEP_PS);
+}
+
+/*
+ * Quantize a phase request: fold @phase_ps into one output period and
+ * split it into whole VCO cycles (@coarse_out, without the divider's
+ * settling cycles) and fine steps (@fine_out). @phase_adj gets the delay
+ * the two realize, in the request's sign and bounded to the advertised
+ * range, which is what the cache holds. Pure arithmetic against @fvco
+ * and the divider @divo the output runs on, so a caller can encode
+ * before it enters the programming state.
+ */
+static int sit9531x_output_phase_encode(u64 fvco, u64 divo, s32 phase_ps,
+ u64 *coarse_out, u8 *fine_out,
+ s32 *phase_adj)
+{
+ u64 abs_ps, cycles, frac_ps, coarse = 0, coarse_ps, t_out_ps;
+ s64 phase_norm_ps = 0;
+ u8 fine = 0;
+
+ t_out_ps = mul_u64_u64_div_u64(divo, 1000000000000ULL, fvco);
+ if (!t_out_ps)
+ return -EINVAL;
+
+ /*
+ * Convert to unsigned absolute delay. Both signs are folded
+ * modulo one period: positive delays wrap naturally, negative
+ * delays are rendered as T_out - |phase|. abs() is safe here
+ * because the core rejects anything outside the advertised phase
+ * range, which is +/-1 ms.
+ *
+ * The period is exactly DIVO VCO cycles, whereas in picoseconds it
+ * is a fraction more often than not: 7812.5 ps at 128 MHz from a
+ * 5.12 GHz VCO, and a fold on the truncated 7812 drops the half
+ * picosecond once per period folded away, so 100 us -- 12800
+ * periods exactly -- would come out as 6400 ps rather than nothing.
+ * Fold the whole VCO cycles of the request modulo DIVO instead, and
+ * carry the sub-cycle part across as it is. div64_u64_rem() rather
+ * than the % operator: a 64-bit modulo has no compiler helper on
+ * 32-bit targets and leaves the module with an undefined __umoddi3.
+ */
+ abs_ps = abs(phase_ps);
+ cycles = mul_u64_u64_div_u64(abs_ps, fvco, 1000000000000ULL);
+ frac_ps = abs_ps - mul_u64_u64_div_u64(cycles, 1000000000000ULL, fvco);
+ div64_u64_rem(cycles, divo, &cycles);
+ abs_ps = mul_u64_u64_div_u64(cycles, 1000000000000ULL, fvco) + frac_ps;
+ /*
+ * The truncations above can put a request within a picosecond of a
+ * whole period a picosecond past it; that is the same edge as none.
+ */
+ if (abs_ps >= t_out_ps)
+ abs_ps = 0;
+ phase_norm_ps = phase_ps < 0 ? -(s64)abs_ps : (s64)abs_ps;
+ abs_ps = (phase_ps < 0 && abs_ps) ? t_out_ps - abs_ps : abs_ps;
+
+ if (abs_ps) {
+ u64 floor_cycles, err, alt_err;
+ u8 alt_fine;
+
+ /*
+ * coarse_cycles = abs_ps * Fvco / 1e12 ps/s.
+ * mul_u64_u64_div_u64() avoids overflow when abs_ps approaches
+ * one second of 1 PPS wrap-around.
+ */
+ floor_cycles = mul_u64_u64_div_u64(abs_ps, fvco,
+ 1000000000000ULL);
+
+ /*
+ * Fine = round((abs_ps - coarse * vco_period_ps) / 30 ps).
+ * The fine field spans 210 ps, more than one VCO cycle in
+ * every band, so the cycle count that floors the request is
+ * not always the nearest encoding: one cycle more can land
+ * closer than any fine code when the remainder sits just
+ * under a cycle, and one cycle fewer with a larger fine code
+ * can land closer or exact -- 210 ps at 5 GHz is seven fine
+ * steps, not a cycle and a ten picosecond remainder. Take
+ * the nearest of the three, the floor on a tie.
+ */
+ coarse = floor_cycles;
+ err = sit9531x_output_phase_quant_err(abs_ps, fvco, coarse,
+ &fine);
+ alt_err = sit9531x_output_phase_quant_err(abs_ps, fvco,
+ floor_cycles + 1,
+ &alt_fine);
+ if (alt_err < err) {
+ coarse = floor_cycles + 1;
+ fine = alt_fine;
+ err = alt_err;
+ }
+ if (floor_cycles) {
+ alt_err = sit9531x_output_phase_quant_err(abs_ps, fvco,
+ floor_cycles - 1,
+ &alt_fine);
+ if (alt_err < err) {
+ coarse = floor_cycles - 1;
+ fine = alt_fine;
+ }
+ }
+
+ /*
+ * A delay that quantizes to a whole output period or beyond
+ * is the same edge as no delay at all; program none, so the
+ * registers hold no residual past the period and the cache
+ * below describes exactly what they realize.
+ */
+ coarse_ps = mul_u64_u64_div_u64(coarse, 1000000000000ULL, fvco);
+ if (coarse_ps + (u64)fine * SIT9531X_OUT_PRG_FINE_STEP_PS >=
+ t_out_ps) {
+ coarse = 0;
+ fine = 0;
+ }
+
+ if (coarse + SIT9531X_OUT_PRG_DIVO_CYCLES >=
+ (1ULL << SIT9531X_OUT_PRG_COARSE_BITS))
+ return -ERANGE;
+ }
+
+ coarse_ps = mul_u64_u64_div_u64(coarse, 1000000000000ULL, fvco);
+ abs_ps = coarse_ps + (u64)fine * SIT9531X_OUT_PRG_FINE_STEP_PS;
+ /*
+ * Quantization can also land a few picoseconds past the end of the
+ * advertised range, which the getter must not report. Bound both
+ * signs to the range; the positive one is also what keeps the cast
+ * to the s32 the ABI carries safe.
+ */
+ if (phase_norm_ps < 0)
+ *phase_adj = abs_ps ?
+ -(s32)min_t(u64, t_out_ps - abs_ps,
+ SIT9531X_OUT_PHASE_ADJ_MAX_PS) : 0;
+ else
+ *phase_adj = (s32)min_t(u64, abs_ps,
+ SIT9531X_OUT_PHASE_ADJ_MAX_PS);
+
+ *coarse_out = coarse;
+ *fine_out = fine;
+
+ return 0;
+}
+
+/**
+ * sit9531x_output_phase_read - read an output's programmed delay back
+ * @sitdev: device pointer
+ * @out_idx: logical output index
+ * @phase_ps: result in picoseconds, in the advertised range
+ * @clamped: set when the delay lies beyond the advertised range either
+ * way and @phase_ps reports the end of the range in its place
+ *
+ * The delay the chip holds is part of the profile it loads before probe,
+ * and a rate or phase request that failed after its writes reached the
+ * device leaves the cache describing something else. Decoding the five
+ * PRG_RST_DELAY bytes is the only way to say what the output is really
+ * doing. The registers carry an unsigned delay, and an advance is held
+ * as its complement to the output period. The two can only be told
+ * apart on an output whose period is longer than the advertised range:
+ * there a delay beyond the range whose complement is within it reads
+ * back as that advance. On a faster output every delay is within the
+ * range, and an advance that was set reads back as the delay to the same
+ * edge, which is the same phase.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ *
+ * Return: 0 on success, -ENODATA for an output no PLL drives or whose VCO
+ * rate is unknown, <0 on register access error
+ */
+int sit9531x_output_phase_read(struct sit9531x_dev *sitdev, u8 out_idx,
+ s32 *phase_ps, bool *clamped)
+{
+ const struct sit9531x_chip_info *info = sitdev->info;
+ u8 bytes[SIT9531X_OUT_PRG_BYTES], fine;
+ u64 coarse = 0, fvco, ps, divo;
+ int rc;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ if (out_idx >= info->num_outputs)
+ return -EINVAL;
+
+ *clamped = false;
+
+ /*
+ * An output no PLL drives has no Fvco to decode its delay against;
+ * pll_idx then holds a placeholder, not a routing.
+ */
+ if (!sitdev->out[out_idx].routed)
+ return -ENODATA;
+
+ rc = sit9531x_get_fvco(sitdev, sitdev->out[out_idx].pll_idx, &fvco);
+ if (rc)
+ return rc;
+
+ rc = sit9531x_output_phase_bytes_read(sitdev, out_idx, bytes);
+ if (rc)
+ return rc;
+
+ fine = (bytes[0] & SIT9531X_OUT_PRG_FINE_MASK) >>
+ SIT9531X_OUT_PRG_FINE_SHIFT;
+ coarse = (u64)(bytes[0] & SIT9531X_OUT_PRG_COARSE_HI_MASK) << 32;
+ coarse |= (u64)bytes[1] << 24;
+ coarse |= (u64)bytes[2] << 16;
+ coarse |= (u64)bytes[3] << 8;
+ coarse |= bytes[4];
+
+ /*
+ * The register carries the divider's settling time on top of the
+ * delay that was asked for, so take it back off. A profile can
+ * leave a value below it, which describes no delay at all.
+ */
+ coarse = (coarse > SIT9531X_OUT_PRG_DIVO_CYCLES) ?
+ coarse - SIT9531X_OUT_PRG_DIVO_CYCLES : 0;
+
+ /*
+ * The setter holds an advance as T_out - |advance|, so a delay
+ * beyond the advertised range whose complement is within it is that
+ * advance and reads back as one. Anything else a profile left
+ * beyond the range -- wider than an s32 on a slow output -- reports
+ * the end of the range rather than a value the setter would refuse,
+ * and says so: the end of the range is not the delay, and a rate
+ * change must not re-time it into the device as though it were.
+ *
+ * The period comes from the divider rather than from the cached rate,
+ * which is still unset at probe and whole hertz at best. An output
+ * without a programmed divider has no period to fold against; a
+ * divider that could not be read is an error, not a missing one,
+ * since the unfolded value would be cached as the realized delay.
+ */
+ rc = sit9531x_output_divo_read(sitdev, out_idx, &divo);
+ if (rc && rc != -ENODATA)
+ return rc;
+
+ /*
+ * The period is exactly DIVO VCO cycles, so fold the cycle count
+ * rather than the picoseconds it converts to, which would drop the
+ * fraction of a picosecond of the period once per period folded
+ * away (see sit9531x_output_phase_encode()). The fine steps can
+ * still carry the sum a fraction of a cycle past the period; one
+ * subtraction folds that without anything to accumulate.
+ */
+ if (!rc)
+ div64_u64_rem(coarse, divo, &coarse);
+
+ ps = mul_u64_u64_div_u64(coarse, 1000000000000ULL, fvco);
+ ps += (u64)fine * SIT9531X_OUT_PRG_FINE_STEP_PS;
+
+ if (!rc) {
+ u64 t_out_ps = mul_u64_u64_div_u64(divo, 1000000000000ULL,
+ fvco);
+
+ if (ps >= t_out_ps)
+ ps -= t_out_ps;
+ if (ps > SIT9531X_OUT_PHASE_ADJ_MAX_PS &&
+ t_out_ps - ps <= SIT9531X_OUT_PHASE_ADJ_MAX_PS) {
+ *phase_ps = -(s32)(t_out_ps - ps);
+ return 0;
+ }
+ }
+ *clamped = ps > SIT9531X_OUT_PHASE_ADJ_MAX_PS;
+ *phase_ps = (s32)min_t(u64, ps, SIT9531X_OUT_PHASE_ADJ_MAX_PS);
+
+ return 0;
+}
+
+int sit9531x_output_freq_set(struct sit9531x_dev *sitdev, u8 out_idx,
+ u8 pll_idx, u64 frequency)
+{
+ u8 old_bytes[SIT9531X_OUT_PRG_BYTES], new_bytes[SIT9531X_OUT_PRG_BYTES];
+ u64 fvco, divo, coarse;
+ bool retime = false;
+ s32 phase_adj = 0;
+ int rc, ret;
+ u8 fine;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ rc = sit9531x_output_divo_calc(sitdev, out_idx, pll_idx, frequency,
+ &fvco, &divo);
+ if (rc)
+ return rc;
+
+ /*
+ * The programmed reset delay counts VCO cycles, and a rate change
+ * moves only the divider, so a positive delay keeps its timing; an
+ * advance, though, is held as T_out - |advance| and has to be
+ * re-encoded against the new period. Re-encode the delay the output
+ * realizes, which is what the cache holds, against the new divider,
+ * and write it in the same programming window as the divider: the
+ * rate change then costs one commit and one flush. A second window
+ * would open the output loops and wait out their settling again, and
+ * a second flush would restart the PLL's dividers again -- or, on a
+ * PLL the profile builds without the phase-flush feature, the PLL
+ * itself. For a positive delay the bytes come out the same and
+ * nothing is written. A re-time that cannot be worked out fails
+ * the request before anything is written.
+ */
+ if (sitdev->out[out_idx].phase_armed) {
+ rc = sit9531x_output_phase_encode(fvco, divo,
+ sitdev->out[out_idx].phase_adj,
+ &coarse, &fine, &phase_adj);
+ if (rc)
+ return rc;
+ rc = sit9531x_output_phase_bytes_read(sitdev, out_idx,
+ old_bytes);
+ if (rc)
+ return rc;
+ sit9531x_output_phase_bytes_build(old_bytes, coarse, fine,
+ new_bytes);
+ retime = memcmp(old_bytes, new_bytes, sizeof(new_bytes)) != 0;
+ }
+
+ rc = sit9531x_prg_enter(sitdev);
+ if (rc)
+ return rc;
+
+ rc = sit9531x_output_divo_write(sitdev, out_idx, divo);
+ if (!rc && retime)
+ rc = sit9531x_output_phase_bytes_write(sitdev, out_idx,
+ new_bytes, old_bytes);
+ /*
+ * Step 4: NVM update + loop lock. Always run prg_commit() so the chip
+ * leaves the PRG_CMD state with the output loops re-locked, even when a
+ * write above failed; keep the first error to return. It also carries
+ * the required post-lock settling sleep.
+ */
+ ret = sit9531x_prg_commit(sitdev);
+ if (ret && !rc)
+ rc = ret;
+ if (rc) {
+ /*
+ * The divider may have changed all the same: a failed update
+ * can have reached the part, a loop lock can fail after it
+ * took effect, and a failed rollback leaves whatever landed.
+ * An advance is held as T_out - |advance| against the period
+ * that was, and the re-timed bytes may or may not be in, so
+ * the cache no longer says what the output does. Mark it for
+ * a read-back: the core drops a retry of either request,
+ * the rate because it reads the new one first and the phase
+ * because it would match the stale cache.
+ */
+ if (sitdev->out[out_idx].phase_armed)
+ sitdev->out[out_idx].phase_stale = true;
+ return rc;
+ }
+
+ /*
+ * Step 5: flush the PLL's output phase so the new DIVO starts
+ * aligned instead of keeping the arbitrary phase the divider
+ * happened to be at.
+ */
+ /*
+ * The divider is committed by this point, so the part is already
+ * running at the new rate. A flush that fails leaves the output
+ * divider on its old phase, which is a realignment that did not
+ * happen rather than a rate that did not change -- and reporting a
+ * failure would be doubly wrong, because the core asks for the
+ * current rate first and would drop an identical retry. A re-timed
+ * delay is then committed but not applied either; mark it for a
+ * read-back rather than cache a value the flush did not realize.
+ */
+ rc = sit9531x_output_phase_flush(sitdev, pll_idx);
+ if (rc) {
+ dev_warn(sitdev->dev,
+ "out%u: rate changed but the divider phase was not realigned (%d)\n",
+ out_idx, rc);
+ if (retime)
+ sitdev->out[out_idx].phase_stale = true;
+ rc = 0;
+ } else {
+ /* Whatever flush was owed from before has now run. */
+ sitdev->out[out_idx].flush_pending = false;
+ if (retime) {
+ sitdev->out[out_idx].phase_adj = phase_adj;
+ sitdev->out[out_idx].phase_armed = phase_adj != 0;
+ sitdev->out[out_idx].phase_stale = false;
+ }
+ }
+
+ sitdev->out[out_idx].freq = div64_u64(fvco, divo);
+
+ return rc;
+}
+
+/*
+ * sit9531x_output_freq_get - read output clock frequency from hardware
+ * @out_idx: output index (0-N for this chip variant)
+ * @frequency: output frequency in Hz
+ *
+ * Reads the 34-bit DIVO divider back from the output system registers
+ * and computes the live output frequency as Fvco / DIVO. This stays
+ * correct even when the divider was reprogrammed behind the driver's
+ * back (e.g. by a direct-I2C userspace tool), where the cached value
+ * would be stale.
+ *
+ * The cached output state is refreshed with the computed value.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ *
+ * Return: 0 on success, -ENODATA when the output divider or VCO rate
+ * is not resolvable, <0 on register access error. -ENODATA and not
+ * -ENODEV, which the I2C layer returns for an adapter that is gone
+ * and which must not read as "no rate to report".
+ */
+int sit9531x_output_freq_get(struct sit9531x_dev *sitdev, u8 out_idx,
+ u64 *frequency)
+{
+ const struct sit9531x_chip_info *info = sitdev->info;
+ u64 fvco, divo;
+ u8 pll_idx;
+ int rc;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ if (out_idx >= info->num_outputs)
+ return -EINVAL;
+
+ pll_idx = sitdev->out[out_idx].pll_idx;
+ if (pll_idx >= SIT9531X_NUM_PLLS || !sitdev->out[out_idx].routed)
+ return -ENODATA;
+
+ rc = sit9531x_get_fvco(sitdev, pll_idx, &fvco);
+ if (rc)
+ return rc;
+
+ rc = sit9531x_output_divo_read(sitdev, out_idx, &divo);
+ if (rc)
+ return rc;
+
+ *frequency = div64_u64(fvco, divo);
+ sitdev->out[out_idx].freq = *frequency;
+
+ return 0;
+}
+
+int sit9531x_output_phase_adjust_set(struct sit9531x_dev *sitdev,
+ u8 out_idx, s32 phase_ps)
+{
+ u8 old_bytes[SIT9531X_OUT_PRG_BYTES], new_bytes[SIT9531X_OUT_PRG_BYTES];
+ const struct sit9531x_chip_info *info = sitdev->info;
+ u64 fvco, divo, coarse;
+ u8 pll_idx, fine;
+ s32 phase_adj;
+ int rc, ret;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ if (out_idx >= info->num_outputs)
+ return -EINVAL;
+
+ pll_idx = sitdev->out[out_idx].pll_idx;
+ if (pll_idx >= SIT9531X_NUM_PLLS)
+ return -EINVAL;
+
+ rc = sit9531x_get_fvco(sitdev, pll_idx, &fvco);
+ if (rc)
+ return rc == -ENODATA ? -ENODEV : rc;
+
+ /*
+ * The output period comes from the divider the output runs on, not
+ * from the cached rate: that is whole hertz, so a profile's output at
+ * a fractional rate would fold an advance against the wrong period.
+ */
+ rc = sit9531x_output_divo_read(sitdev, out_idx, &divo);
+ if (rc)
+ return rc == -ENODATA ? -ENODEV : rc;
+
+ rc = sit9531x_output_phase_encode(fvco, divo, phase_ps, &coarse, &fine,
+ &phase_adj);
+ if (rc)
+ return rc;
+
+ rc = sit9531x_output_phase_bytes_read(sitdev, out_idx, old_bytes);
+ if (rc)
+ return rc;
+ sit9531x_output_phase_bytes_build(old_bytes, coarse, fine, new_bytes);
+
+ /*
+ * The pin advertises 1 ps granularity but caches the quantized
+ * value, so a repeated off-grid request reaches here with the
+ * registers already holding it. Rewriting them would still restart
+ * the divider phase of every output on the PLL; skip it unless an
+ * earlier failure left the delay unconfirmed. A delay whose flush
+ * failed is committed and only owes that flush, which matching
+ * bytes would otherwise skip as well.
+ */
+ if (!memcmp(old_bytes, new_bytes, sizeof(new_bytes)) &&
+ !sitdev->out[out_idx].phase_stale) {
+ if (!sitdev->out[out_idx].flush_pending)
+ goto cache;
+ goto flush;
+ }
+
+ /*
+ * The PRG_RST_DELAY bytes live in the output system, so the writes
+ * only take effect when made inside the PRG_CMD programming state and
+ * committed to the NVM shadow, exactly like sit9531x_output_freq_set().
+ */
+ rc = sit9531x_prg_enter(sitdev);
+ if (rc)
+ return rc;
+
+ rc = sit9531x_output_phase_bytes_write(sitdev, out_idx, new_bytes,
+ old_bytes);
+
+ /*
+ * Always leave the PRG_CMD state via prg_commit(), even on a
+ * mid-sequence write failure, so the output loops are re-locked rather
+ * than stranded unlocked; keep the first error.
+ */
+ ret = sit9531x_prg_commit(sitdev);
+ if (ret && !rc)
+ rc = ret;
+ if (rc) {
+ /*
+ * The delay registers were written and the rollback may not
+ * have put all of them back, so what the output realizes is
+ * no longer what the cache says. Mark it so the getter reads
+ * the registers instead of reporting the value that was
+ * cached before this call. No flush ran either way.
+ */
+ sitdev->out[out_idx].phase_stale = true;
+ sitdev->out[out_idx].flush_pending = true;
+ return rc;
+ }
+
+flush:
+ /*
+ * Restart the output divider phase so the freshly programmed delay is
+ * applied against a known edge instead of the divider's arbitrary
+ * running phase.
+ */
+ rc = sit9531x_output_phase_flush(sitdev, pll_idx);
+ if (rc) {
+ /*
+ * The delay is committed but the output keeps the phase it
+ * had, so the cache is left as it is: it still says what the
+ * output realizes. Record the flush that is owed instead.
+ * Marking the cache stale would have the getter publish the
+ * new delay off the registers, the core would then drop a
+ * retry as a request for the value already reported, and the
+ * realignment would never run.
+ */
+ sitdev->out[out_idx].flush_pending = true;
+ return rc;
+ }
+ sitdev->out[out_idx].flush_pending = false;
+ sitdev->out[out_idx].phase_stale = false;
+
+cache:
+ /*
+ * Cache what the registers realize, and only once every step has
+ * succeeded: the core drops a repeated request with the same value,
+ * so a cache updated by a failed call would make the retry a no-op.
+ */
+ sitdev->out[out_idx].phase_adj = phase_adj;
+
+ /*
+ * Arm the re-time a rate change owes only for a delay that is
+ * actually programmed: an advance is held as T_out - |advance|,
+ * which has to be re-encoded against the new period. A request
+ * that quantized to nothing stays nothing at any rate, since the
+ * quantum is the VCO cycle and not the output period, so there is
+ * nothing to re-time for it.
+ */
+ sitdev->out[out_idx].phase_armed = phase_adj != 0;
+
+ return 0;
+}
+
+/*
+ * sit9531x_clear_notifications - clear all notification registers
+ *
+ * Clears all write-1-to-clear notification registers:
+ * - PLL outer LOL notification (Page 0, reg 0x07)
+ * - PLL holdover freeze notification (Page 0, reg 0x0B)
+ * - PLL inner LOL notification (Page 0, reg 0x93)
+ * - Clock monitor XO/PLL notification (Page 0, reg 0x9E)
+ * - Clock input notifications (Page 6, regs 0x03/0x07/0x93/0x97)
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+int sit9531x_clear_notifications(struct sit9531x_dev *sitdev)
+{
+ int rc;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ /* Page 0x00 W1C notification registers */
+ rc = sit9531x_write_u8(sitdev, SIT9531X_REG_OUTER_LOL_NOTIF, 0xFF);
+ if (rc)
+ return rc;
+ rc = sit9531x_write_u8(sitdev, SIT9531X_REG_HO_FREEZE_NOTIF, 0xFF);
+ if (rc)
+ return rc;
+ rc = sit9531x_write_u8(sitdev, SIT9531X_REG_PLL_INNER_LOL_NOTIF, 0xFF);
+ if (rc)
+ return rc;
+ rc = sit9531x_write_u8(sitdev, SIT9531X_REG_CMON_NOTIF, 0xFF);
+ if (rc)
+ return rc;
+
+ /* Page 0x06 clock input monitor notifications */
+ rc = sit9531x_write_u8(sitdev, SIT9531X_CLKMON_P_NOTIF_01, 0xFF);
+ if (rc)
+ return rc;
+ rc = sit9531x_write_u8(sitdev, SIT9531X_CLKMON_P_NOTIF_23, 0xFF);
+ if (rc)
+ return rc;
+ rc = sit9531x_write_u8(sitdev, SIT9531X_CLKMON_N_NOTIF_01, 0xFF);
+ if (rc)
+ return rc;
+ rc = sit9531x_write_u8(sitdev, SIT9531X_CLKMON_N_NOTIF_23, 0xFF);
+ if (rc)
+ return rc;
+
+ dev_dbg(sitdev->dev, "All notification registers cleared\n");
+ return 0;
+}
+
+/*
+ * Close the debug window on a PLL's EXT page. The key register opens
+ * every debug register on that page while it holds the unlock value.
+ */
+static int sit9531x_intsync_debug_lock(struct sit9531x_dev *sitdev, u8 ext_page)
+{
+ return sit9531x_write_u8(sitdev,
+ SIT9531X_REG(ext_page, SIT9531X_PLL_REG_DEBUG),
+ SIT9531X_PLL_DEBUG_LOCK);
+}
+
+/*
+ * INTSYNC configuration register values.
+ * These are written to the source PLL's EXT page to enable/disable
+ * inter-PLL synchronization (lock frequency PLL to phase PLL).
+ */
+struct sit9531x_intsync_reg {
+ u8 offset;
+ u8 en_val;
+ u8 dis_val;
+};
+
+static const struct sit9531x_intsync_reg intsync_config[] = {
+ { 0x2D, 0x02, 0x00 },
+ { 0x50, 0x08, 0x00 },
+ { 0x51, 0x04, 0x00 },
+ { 0x54, 0x02, 0x00 },
+ { 0x55, 0x28, 0x20 },
+ { 0x5C, 0x0F, 0x00 },
+ { 0x5D, 0xFF, 0x00 },
+ { 0x6C, 0xDD, 0x00 },
+};
+
+int sit9531x_intsync_src_detect(struct sit9531x_dev *sitdev)
+{
+ s8 src = -1, partial = -1;
+ u8 global;
+ u8 pll, ext_page;
+ int rc, ret, n_en, n_dis;
+ unsigned int i;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ rc = sit9531x_read_u8(sitdev, SIT9531X_REG_INTSYNC_GLOBAL, &global);
+ if (rc)
+ return rc;
+
+ if (!(global & BIT(SIT9531X_INTSYNC_EN_BIT))) {
+ sitdev->intsync_src = -1;
+ sitdev->intsync_partial = false;
+ return 0;
+ }
+
+ for (pll = 0; pll < SIT9531X_NUM_PLLS; pll++) {
+ ext_page = SIT9531X_PLL_EXT_PAGE(pll);
+
+ /*
+ * These are debug registers, which the enable and disable
+ * sequences only touch with the EXT page's debug window
+ * open; read them the same way.
+ */
+ rc = sit9531x_write_u8(sitdev,
+ SIT9531X_REG(ext_page,
+ SIT9531X_PLL_REG_DEBUG),
+ SIT9531X_PLL_DEBUG_UNLOCK);
+ if (rc)
+ return rc;
+
+ n_en = 0;
+ n_dis = 0;
+ for (i = 0; i < ARRAY_SIZE(intsync_config); i++) {
+ u16 reg;
+ u8 val;
+
+ reg = SIT9531X_REG(ext_page, intsync_config[i].offset);
+
+ rc = sit9531x_read_u8(sitdev, reg, &val);
+ if (rc)
+ break;
+ if (val == intsync_config[i].en_val)
+ n_en++;
+ else if (val == intsync_config[i].dis_val)
+ n_dis++;
+ }
+
+ ret = sit9531x_intsync_debug_lock(sitdev, ext_page);
+ if (!rc)
+ rc = ret;
+ if (rc)
+ return rc;
+
+ if (n_en == ARRAY_SIZE(intsync_config)) {
+ /*
+ * Only one PLL can drive the net. If a second
+ * one matches, the registers are not describing
+ * a state this driver put the device in, so say
+ * so rather than pick silently.
+ */
+ if (src < 0)
+ src = pll;
+ else
+ dev_warn(sitdev->dev,
+ "PLL%c also matches the INTSYNC source pattern; keeping PLL%c\n",
+ 'A' + pll, 'A' + src);
+ } else if (n_en && n_dis &&
+ n_en + n_dis == ARRAY_SIZE(intsync_config) &&
+ partial < 0) {
+ /*
+ * Every register holds one of the two patterns and both
+ * occur: the residue of an enable or a disable that
+ * stopped part way, and nothing a profile writes.
+ */
+ partial = pll;
+ }
+ }
+
+ /*
+ * An enable or a disable that stopped part way leaves a PLL holding
+ * a mix of the two patterns. Treat it as the owner, so that another
+ * PLL cannot be enabled on top of the leftover configuration, but
+ * record that it is partial: a repeated connect on that PLL must run
+ * the enable again rather than take the PLL for configured, and a
+ * repeated disconnect runs the disable again.
+ */
+ sitdev->intsync_partial = src < 0 && partial >= 0;
+ if (sitdev->intsync_partial) {
+ dev_warn(sitdev->dev,
+ "PLL%c holds a partial INTSYNC source configuration\n",
+ 'A' + partial);
+ src = partial;
+ }
+
+ sitdev->intsync_src = src;
+
+ return 0;
+}
+
+static int __sit9531x_intsync_disable(struct sit9531x_dev *sitdev,
+ u8 src_pll_idx, bool restore);
+
+/*
+ * sit9531x_intsync_enable - enable inter-PLL synchronization
+ * @src_pll_idx: source (frequency) PLL index (0-3)
+ *
+ * Enables INTSYNC global bit, unlocks the source PLL's EXT page
+ * debug registers, writes configuration, and triggers a small
+ * update on the source PLL.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+int sit9531x_intsync_enable(struct sit9531x_dev *sitdev, u8 src_pll_idx)
+{
+ u8 ext_page, val;
+ int rc, lock_rc;
+ unsigned int i;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ if (src_pll_idx >= SIT9531X_NUM_PLLS)
+ return -EINVAL;
+
+ ext_page = SIT9531X_PLL_EXT_PAGE(src_pll_idx);
+
+ rc = sit9531x_read_u8(sitdev, SIT9531X_REG_INTSYNC_GLOBAL, &val);
+ if (rc)
+ return rc;
+ rc = sit9531x_write_u8(sitdev, SIT9531X_REG_INTSYNC_GLOBAL,
+ val | BIT(SIT9531X_INTSYNC_EN_BIT));
+ if (rc)
+ return rc;
+
+ /* Small update on Page 0 */
+ rc = sit9531x_write_u8(sitdev, SIT9531X_REG_GLOBAL_UPDATE,
+ SIT9531X_SMALL_UPDATE_CMD);
+ usleep_range(1000, 2000);
+ if (rc)
+ goto relock_err;
+
+ /* Unlock debug on EXT page */
+ rc = sit9531x_write_u8(sitdev,
+ SIT9531X_REG(ext_page,
+ SIT9531X_PLL_REG_DEBUG),
+ SIT9531X_PLL_DEBUG_UNLOCK);
+ if (rc)
+ goto relock_err;
+
+ for (i = 0; i < ARRAY_SIZE(intsync_config); i++) {
+ rc = sit9531x_write_u8(sitdev,
+ SIT9531X_REG(ext_page,
+ intsync_config[i].offset),
+ intsync_config[i].en_val);
+ if (rc)
+ goto relock_err;
+ }
+
+ /* Small update on source PLL */
+ rc = sit9531x_pll_small_update(sitdev, src_pll_idx);
+ if (rc)
+ goto relock_err;
+
+ rc = 0;
+ goto relock;
+
+relock_err:
+ sit9531x_intsync_debug_lock(sitdev, ext_page);
+ goto err_disable;
+
+relock:
+ /*
+ * Close the EXT page debug window the sequence opened. Nothing
+ * else writes the key back, so leaving it open would keep the block
+ * unlocked for as long as the device runs. The net is driven by
+ * now, though, so a failure here must not read as a failed enable:
+ * the caller would then not record the owner of a net that is
+ * driven.
+ */
+ lock_rc = sit9531x_intsync_debug_lock(sitdev, ext_page);
+ if (lock_rc)
+ dev_warn(sitdev->dev,
+ "PLL%c: INTSYNC enabled but the debug window was left open: %d\n",
+ 'A' + src_pll_idx, lock_rc);
+
+ return rc;
+
+err_disable:
+ /*
+ * The global enable is already set at this point. The caller only
+ * records the source PLL when this function succeeds, so nothing
+ * else will ever clear the bit: undo it here rather than leave the
+ * net asserted with a half-written EXT page. The disable must not
+ * put the bit back when it fails part way, as it does for a
+ * disconnect: here the bit is what the rollback set out to clear,
+ * and restoring it would leave the net asserted with no PLL
+ * matching the source pattern, which no owner on record would ever
+ * clear.
+ */
+ {
+ int rollback_rc;
+
+ rollback_rc = __sit9531x_intsync_disable(sitdev, src_pll_idx,
+ false);
+ if (rollback_rc)
+ dev_warn(sitdev->dev,
+ "INTSYNC rollback failed after enable error: %d (original %d)\n",
+ rollback_rc, rc);
+ }
+
+ return rc;
+}
+
+/*
+ * The disable sequence. @restore says whether a failure before the EXT
+ * page holds the disable pattern puts the global enable back, which a
+ * disconnect wants (see restore_global below) and the rollback of a
+ * failed enable does not.
+ */
+static int __sit9531x_intsync_disable(struct sit9531x_dev *sitdev,
+ u8 src_pll_idx, bool restore)
+{
+ u8 ext_page, val;
+ int rc, lock_rc;
+ unsigned int i;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ if (src_pll_idx >= SIT9531X_NUM_PLLS)
+ return -EINVAL;
+
+ ext_page = SIT9531X_PLL_EXT_PAGE(src_pll_idx);
+
+ rc = sit9531x_read_u8(sitdev, SIT9531X_REG_INTSYNC_GLOBAL, &val);
+ if (rc)
+ return rc;
+ rc = sit9531x_write_u8(sitdev, SIT9531X_REG_INTSYNC_GLOBAL,
+ val & ~BIT(SIT9531X_INTSYNC_EN_BIT));
+ if (rc)
+ goto restore_global;
+
+ /* Small update on Page 0 */
+ rc = sit9531x_write_u8(sitdev, SIT9531X_REG_GLOBAL_UPDATE,
+ SIT9531X_SMALL_UPDATE_CMD);
+ usleep_range(1000, 2000);
+ if (rc)
+ goto restore_global;
+
+ /* Unlock debug on EXT page */
+ rc = sit9531x_write_u8(sitdev,
+ SIT9531X_REG(ext_page,
+ SIT9531X_PLL_REG_DEBUG),
+ SIT9531X_PLL_DEBUG_UNLOCK);
+ if (rc)
+ goto restore_global;
+
+ for (i = 0; i < ARRAY_SIZE(intsync_config); i++) {
+ rc = sit9531x_write_u8(sitdev,
+ SIT9531X_REG(ext_page,
+ intsync_config[i].offset),
+ intsync_config[i].dis_val);
+ if (rc)
+ goto restore_global;
+ }
+
+ /*
+ * Small update on source PLL. The global enable is latched off and
+ * every register holds the disable value by now, so the net is not
+ * driven whether or not this latch went through. Putting the bit
+ * back for a failure here would describe the net as driven with no
+ * PLL matching the source pattern: the detector would record no
+ * owner, and nothing would ever clear the bit.
+ */
+ rc = sit9531x_pll_small_update(sitdev, src_pll_idx);
+
+ goto relock;
+
+restore_global:
+ /*
+ * The global enable was cleared first, so a failure here leaves the
+ * EXT page still holding the enable pattern with nothing pointing
+ * at it: the source detector keys on the global bit, would report
+ * the net as unowned, and a retry of the disable would then
+ * short-circuit. Put the bit back so the state stays one the
+ * driver can describe and the request can be repeated.
+ */
+ if (restore &&
+ !sit9531x_read_u8(sitdev, SIT9531X_REG_INTSYNC_GLOBAL, &val) &&
+ !sit9531x_write_u8(sitdev, SIT9531X_REG_INTSYNC_GLOBAL,
+ val | BIT(SIT9531X_INTSYNC_EN_BIT))) {
+ /*
+ * Every write of the global enable, here and in SiTime's
+ * sequences, is followed by the page-0 small update that
+ * applies it. Without it the bit would sit pending until
+ * an unrelated commit through the same update register
+ * applied it, re-asserting the net over a half-written
+ * EXT page with nobody having asked.
+ */
+ sit9531x_write_u8(sitdev, SIT9531X_REG_GLOBAL_UPDATE,
+ SIT9531X_SMALL_UPDATE_CMD);
+ usleep_range(1000, 2000);
+ }
+
+relock:
+ /*
+ * Close the EXT page debug window the sequence opened. After a
+ * disable that took effect, a failure here is a warning, for the
+ * same reason as in sit9531x_intsync_enable().
+ */
+ lock_rc = sit9531x_intsync_debug_lock(sitdev, ext_page);
+ if (lock_rc) {
+ if (rc)
+ dev_warn(sitdev->dev,
+ "PLL%c: debug window left open: %d\n",
+ 'A' + src_pll_idx, lock_rc);
+ else
+ dev_warn(sitdev->dev,
+ "PLL%c: INTSYNC disabled but the debug window was left open: %d\n",
+ 'A' + src_pll_idx, lock_rc);
+ }
+
+ return rc;
+}
+
+/*
+ * sit9531x_intsync_disable - disable inter-PLL synchronization
+ * @src_pll_idx: source (frequency) PLL index (0-3)
+ *
+ * Clears INTSYNC global bit, writes disable values to the source
+ * PLL's EXT page, and triggers a small update.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+int sit9531x_intsync_disable(struct sit9531x_dev *sitdev, u8 src_pll_idx)
+{
+ return __sit9531x_intsync_disable(sitdev, src_pll_idx, true);
+}
+
+/**
+ * sit9531x_phase_offset_read - read phase difference via TDC
+ * @sitdev: device pointer
+ * @pll_idx: PLL index (0-3)
+ * @phase_offset: phase difference in units of 1/DPLL_PHASE_OFFSET_DIVIDER
+ * of a picosecond, as the DPLL subsystem carries it
+ *
+ * Reads the Time-to-Digital Converter (TDC) code from the PLL page
+ * registers -- a 35-bit magnitude with a separate sign bit -- then
+ * converts it using the VCO frequency: phase_diff = tdc_code / fvco.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ *
+ * Return: 0 on success, -ENODATA when the PLL has no known VCO rate
+ * (nothing is sampled then), or the register access error
+ */
+int sit9531x_phase_offset_read(struct sit9531x_dev *sitdev, u8 pll_idx,
+ s64 *phase_offset)
+{
+ u8 v, old_write_code, old_read_code;
+ bool have_old = false;
+ int rc, ret, i;
+ s64 tdc_signed;
+ u64 fvco, mag;
+ u64 tdc_raw;
+ bool sign;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ if (pll_idx >= SIT9531X_NUM_PLLS)
+ return -EINVAL;
+
+ /*
+ * Get the VCO rate first. -ENODATA means the PLL has no rate to
+ * convert against (a divider this board leaves unprogrammed), and
+ * there is then no reason to open the debug window at all -- which
+ * also keeps a bus error while it is open from ever being reported
+ * as that benign case.
+ */
+ rc = sit9531x_get_fvco(sitdev, pll_idx, &fvco);
+ if (rc) {
+ if (rc == -ENODATA)
+ dev_dbg(sitdev->dev,
+ "PLL%c: Fvco unknown, skip TDC\n",
+ 'A' + pll_idx);
+ return rc;
+ }
+
+ /* Unlock the debug page so the TDC registers are accessible. */
+ rc = sit9531x_write_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DEBUG,
+ SIT9531X_PLL_DEBUG_UNLOCK);
+ if (rc)
+ goto relock;
+
+ /*
+ * Remember the tap selection so it can be put back. The key
+ * register is re-locked below, but the mux is not part of the key:
+ * leaving it parked on the TDC with a slow sampling clock selected
+ * is a state change the caller did not ask for, and the next reader
+ * of a different tap would have to know to undo it.
+ */
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_WRITE_CODE,
+ &old_write_code);
+ if (!rc)
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_READ_CODE,
+ &old_read_code);
+ if (rc)
+ goto relock;
+
+ have_old = true;
+
+ /*
+ * Select the debug clock for taps below 200 kHz, then point the
+ * readback at the TDC. Only the one bit is touched: writing the
+ * modifier register whole would clear the fields belonging to
+ * other taps.
+ */
+ rc = sit9531x_update_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_WRITE_CODE,
+ SIT9531X_DBG_LOW_FREQ_CLK_BIT,
+ SIT9531X_DBG_LOW_FREQ_CLK_BIT);
+ if (rc)
+ goto relock;
+ rc = sit9531x_write_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_READ_CODE,
+ SIT9531X_DBG_READ_CODE_TDC);
+ if (rc)
+ goto relock;
+
+ /*
+ * Latch a sample by reading the debug status data register. A
+ * single read returns the previous latch, so read it three times
+ * as the documented phase-difference procedure does.
+ */
+ for (i = 0; i < SIT9531X_DBG_STATUS_READS; i++) {
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_STATUS_DATA,
+ &v);
+ if (rc)
+ goto relock;
+ }
+
+ tdc_raw = 0;
+
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_DATA_4, &v);
+ if (rc)
+ goto relock;
+ sign = !!(v & BIT(SIT9531X_TDC_SIGN_BIT));
+ tdc_raw = (u64)(v & SIT9531X_TDC_MAG_HI_MASK) << 32;
+
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_DATA_3, &v);
+ if (rc)
+ goto relock;
+ tdc_raw |= (u64)v << 24;
+
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_DATA_2, &v);
+ if (rc)
+ goto relock;
+ tdc_raw |= (u64)v << 16;
+
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_DATA_1, &v);
+ if (rc)
+ goto relock;
+ tdc_raw |= (u64)v << 8;
+
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_DATA_0, &v);
+ if (rc)
+ goto relock;
+ tdc_raw |= v;
+
+ /*
+ * Apply sign. Per the register map the sign bit is active-high
+ * for a positive offset: bit set -> +code, bit clear -> -code.
+ */
+ tdc_signed = sign ? (s64)tdc_raw : -(s64)tdc_raw;
+
+ /*
+ * phase_diff (seconds) = tdc_code / fvco
+ * phase_diff (ps) = tdc_code * 1e12 / fvco
+ *
+ * Scaled by the subsystem's divider before the division rather than
+ * after it: one TDC code is 1e12 / fvco ps, a fraction of a
+ * picosecond more often than not (203.45 ps at 4.9152 GHz), and
+ * truncating to whole picoseconds first would leave the three
+ * fractional digits the attribute carries always zero. The
+ * quotient fits u64 for any rate sit9531x_get_fvco() returns: it
+ * refuses one below the VCO band.
+ *
+ * mul_u64_u64_div_u64() keeps the exact Hz denominator; dividing
+ * by whole MHz instead would lose up to ~40 ppm of scale on a
+ * fractional-DIVN Fvco.
+ */
+ mag = mul_u64_u64_div_u64(tdc_signed < 0 ? -tdc_signed : tdc_signed,
+ 1000000000000ULL * DPLL_PHASE_OFFSET_DIVIDER,
+ fvco);
+ *phase_offset = tdc_signed < 0 ? -(s64)mag : (s64)mag;
+
+ rc = 0;
+
+relock:
+ if (have_old) {
+ ret = sit9531x_write_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_READ_CODE,
+ old_read_code);
+ if (ret && !rc)
+ rc = ret;
+ ret = sit9531x_write_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_WRITE_CODE,
+ old_write_code);
+ if (ret && !rc)
+ rc = ret;
+ }
+
+ /*
+ * Close the debug window again. The key register opens every debug
+ * register on this PLL while it holds the unlock value, and this read
+ * runs on every pin-get of a connected input, so leaving it open
+ * would mean normal monitoring permanently unlocks the block.
+ */
+ ret = sit9531x_write_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DEBUG,
+ SIT9531X_PLL_DEBUG_LOCK);
+ if (ret && !rc)
+ rc = ret;
+
+ return rc;
+}
+
+/*
+ * sit9531x_ref_state_fetch - read input reference status from hardware
+ * @index: logical input index
+ *
+ * Reads whether the lane's receiver is on, from the Page 0x02 force and
+ * state bits. Signal status comes from sit9531x_input_mon_fetch(),
+ * which reads every lane's clock monitor in one pass.
+ */
+static int sit9531x_ref_state_fetch(struct sit9531x_dev *sitdev, u8 index)
+{
+ unsigned int force_reg, state_reg;
+ u8 pair, force, state;
+ struct sit9531x_ref *ref;
+ int rc;
+
+ /*
+ * The XTAL/XO reference (index SIT9531X_MAX_INPUTS) is the on-chip
+ * oscillator that feeds every PLL. It cannot be routed or deselected,
+ * so its pin is modeled as permanently connected (see
+ * sit9531x_dpll_xo_pin_ops) and has no receiver to gate. Only the
+ * routable per-lane inputs (0..num_inputs-1) are polled here.
+ */
+ if (index >= SIT9531X_MAX_INPUTS)
+ return -EINVAL;
+
+ ref = &sitdev->ref[index];
+ pair = sit9531x_input_pair(index);
+
+ /*
+ * Whether the receiver is on. This has to come from the chip: it
+ * is the loaded configuration that decides, and without reading it
+ * back every input would look disabled until something called
+ * sit9531x_input_enable(). A lane counts as disabled only while
+ * the force bit overrides it to the off state; with the force bit
+ * clear it follows the configuration, which is the enabled case.
+ */
+ sit9531x_input_get_regs(sitdev, index, &force_reg, &state_reg);
+
+ rc = sit9531x_read_u8(sitdev, force_reg, &force);
+ if (rc)
+ return rc;
+ rc = sit9531x_read_u8(sitdev, state_reg, &state);
+ if (rc)
+ return rc;
+
+ ref->enabled = !((force & BIT(pair)) && !(state & BIT(pair)));
+
+ return 0;
+}
+
+/*
+ * sit9531x_input_mode_fetch - detect SE/DE configuration of an input pair
+ * @pair: input pair number (0-3)
+ *
+ * Reads CLKINx_INPUT_MODE and stores the detected signal mode on both
+ * lanes of the pair. A pair with neither SE lane enabled is running
+ * differential.
+ */
+static int sit9531x_input_mode_fetch(struct sit9531x_dev *sitdev, u8 pair)
+{
+ enum sit9531x_signal_mode sig_mode;
+ u8 mode;
+ int rc;
+
+ rc = sit9531x_read_u8(sitdev, SIT9531X_REG_IN_MODE(pair), &mode);
+ if (rc)
+ return rc;
+
+ if (mode & (SIT9531X_IN_MODE_SE_P_EN | SIT9531X_IN_MODE_SE_N_EN))
+ sig_mode = SIT9531X_MODE_SE;
+ else
+ sig_mode = SIT9531X_MODE_DE;
+
+ sitdev->ref[pair * 2].sig_mode = sig_mode;
+ sitdev->ref[pair * 2 + 1].sig_mode = sig_mode;
+
+ dev_dbg(sitdev->dev, "CLKIN%u mode reg 0x%02x -> %s\n", pair, mode,
+ sig_mode == SIT9531X_MODE_DE ? "differential" : "single-ended");
+
+ return 0;
+}
+
+/* Read the PLL active-state bit (PLL page reg 0x02 bit 0). */
+static int sit9531x_pll_is_active(struct sit9531x_dev *sitdev, u8 pll_idx,
+ bool *active)
+{
+ u8 v;
+ int rc;
+
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx, SIT9531X_PLL_REG_ACTIVE, &v);
+ if (rc)
+ return rc;
+
+ *active = !!(v & SIT9531X_PLL_ACTIVE_BIT);
+
+ return 0;
+}
+
+/**
+ * sit9531x_chan_routed_ref_read - read the reference the device feeds a PLL
+ * @sitdev: device pointer
+ * @pll_idx: PLL index (0-3)
+ * @ref: result: logical input index, SIT9531X_INTSYNC_PIN_ID for the
+ * inter-PLL sync net, or SIT9531X_REF_INVALID when the code
+ * names no registered input
+ *
+ * The active selection names the source the PLL was told to follow. The
+ * input subsystem feeds the PLL that source while it has signal and falls
+ * back to another listed source on its own when it does not, without
+ * touching the selection, so after such a fallback the selection no
+ * longer says which reference the PLL runs on. The debug status bus
+ * does: addressed at the routed reference, it returns the source code
+ * the input subsystem currently feeds the PLL.
+ *
+ * This names the source and nothing more. Whether the PLL is locked to
+ * it is read from the outer-loop lock and the lane's clock monitor; in
+ * free-run the PLL is fed nothing and the bus is not consulted, so the
+ * caller checks the mode first.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int sit9531x_chan_routed_ref_read(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u8 *ref)
+{
+ int rc, lock_rc;
+ u8 v, i;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ rc = sit9531x_write_pll_u8(sitdev, pll_idx, SIT9531X_PLL_REG_DEBUG,
+ SIT9531X_PLL_DEBUG_UNLOCK);
+ if (rc)
+ return rc;
+
+ rc = sit9531x_write_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_STATUS_ADDR,
+ SIT9531X_DBG_STATUS_ROUTED_SRC);
+ if (rc)
+ goto relock;
+
+ /* The first reads return earlier latches; the last is current. */
+ for (i = 0; i < SIT9531X_DBG_STATUS_READS; i++) {
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_STATUS_DATA,
+ &v);
+ if (rc)
+ goto relock;
+ }
+
+ /*
+ * Canonicalise like the table entries: a differential pair fed
+ * through its N-lane code is the P-lane pin.
+ */
+ v = sit9531x_prio_src_canon(sitdev, v & SIT9531X_DBG_ROUTED_SRC_MASK);
+ *ref = sit9531x_hw_src_input(v);
+
+relock:
+ /*
+ * Lock the debug registers again. A read that failed is reported
+ * ahead of a lock that did; a lock that failed on its own leaves the
+ * page open to a stray write and is reported too.
+ */
+ lock_rc = sit9531x_write_pll_u8(sitdev, pll_idx, SIT9531X_PLL_REG_DEBUG,
+ SIT9531X_PLL_DEBUG_LOCK);
+
+ return rc ? rc : lock_rc;
+}
+
+/*
+ * sit9531x_chan_state_fetch - read PLL channel status from hardware
+ *
+ * Reads whether the PLL runs, its lock and holdover state, its mode, the
+ * active selection and its priority table.
+ */
+static int sit9531x_chan_state_fetch(struct sit9531x_dev *sitdev, u8 pll_idx)
+{
+ struct sit9531x_chan *chan = &sitdev->chan[pll_idx];
+ u8 status, outer_lol, inner_lol, ho_freeze, pll_status_1;
+ u8 srcs[SIT9531X_PRIO_MAX_SLOTS];
+ u8 routed = SIT9531X_REF_INVALID;
+ bool active, tracking;
+ int rc;
+
+ /*
+ * Whether the PLL is running at all. The loss-of-lock bit read
+ * below is driven by the PLL itself, so on one the loaded
+ * configuration leaves unused it simply stays clear and would
+ * otherwise read as a lock.
+ */
+ rc = sit9531x_pll_is_active(sitdev, pll_idx, &active);
+ if (rc)
+ return rc;
+
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_STATUS, &status);
+ if (rc)
+ return rc;
+
+ rc = sit9531x_read_u8(sitdev, SIT9531X_REG_OUTER_LOL_STATUS,
+ &outer_lol);
+ if (rc)
+ return rc;
+
+ rc = sit9531x_read_u8(sitdev, SIT9531X_REG_PLL_INNER_LOL_STATUS,
+ &inner_lol);
+ if (rc)
+ return rc;
+
+ rc = sit9531x_read_u8(sitdev, SIT9531X_REG_HO_FREEZE_STATUS,
+ &ho_freeze);
+ if (rc)
+ return rc;
+
+ /*
+ * The reference the device feeds the PLL, read only while the PLL
+ * tracks one -- running, locked, outer loop on, not frozen -- which
+ * is the only case the pin state reports it. In free-run the PLL
+ * is fed nothing and the status bus is not consulted.
+ */
+ tracking = active && !(outer_lol & BIT(pll_idx)) &&
+ !(status & SIT9531X_PLL_STATUS_OUTER_DIS) &&
+ !(ho_freeze & BIT(pll_idx));
+ if (tracking) {
+ rc = sit9531x_chan_routed_ref_read(sitdev, pll_idx, &routed);
+ if (rc)
+ return rc;
+ }
+
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx, SIT9531X_PLL_REG_STATUS_1,
+ &pll_status_1);
+ if (rc)
+ return rc;
+
+ /*
+ * Which sources this PLL may select. The table is configuration and
+ * changes only through the driver, but reading it back keeps the
+ * membership the pin state getters report tied to the hardware
+ * instead of to a value the driver maintains on the side.
+ */
+ rc = sit9531x_prio_table_read(sitdev, pll_idx, srcs);
+ if (rc)
+ return rc;
+
+ sit9531x_prio_mask_build(sitdev, pll_idx, srcs);
+
+ /*
+ * The configured priorities come from the table the first time it
+ * is read, and again whenever it no longer matches what the driver
+ * last wrote: something else -- a profile reload, a direct I2C
+ * tool -- rewrote it, and that table is now the configuration.
+ */
+ if (!chan->seen_valid ||
+ memcmp(srcs, chan->seen_srcs, sizeof(chan->seen_srcs)))
+ sit9531x_prio_cfg_seed(sitdev, pll_idx, srcs);
+
+ /* STATUS_1_GENERIC reports loss of lock, so invert it. */
+ chan->active = active;
+ chan->locked = active && !(outer_lol & BIT(pll_idx));
+ chan->mode = !!(status & SIT9531X_PLL_STATUS_OUTER_DIS);
+ chan->routed_ref = routed;
+ chan->inner_lol = !!(inner_lol & BIT(pll_idx));
+ chan->ho_freeze = !!(ho_freeze & BIT(pll_idx));
+ chan->ho_valid = !!(pll_status_1 & SIT9531X_PLL_STATUS_1_HO_VALID);
+
+ return 0;
+}
+
+/*
+ * sit9531x_out_state_fetch - read output status from hardware
+ *
+ * Takes the output PLL association from the PLL page output map
+ * registers into out->routed / out->pll_idx, and the current drive state
+ * from the Hi-Z force bits into out->enabled. The two are separate:
+ * routing decides whether the output gets a DPLL pin at all,
+ * while a muted but routed output keeps its pin and reports
+ * DPLL_PIN_STATE_DISCONNECTED until it is un-muted.
+ */
+static int sit9531x_out_state_fetch(struct sit9531x_dev *sitdev, u8 index)
+{
+ struct sit9531x_out *out = &sitdev->out[index];
+ u8 map_lo, map_hi, slot, bit, claimed;
+ int pll_idx;
+ u16 mask;
+ bool muted;
+ int rc;
+
+ slot = sitdev->info->clkout_map[index];
+
+ rc = sit9531x_output_mode_fetch(sitdev, index);
+ if (rc)
+ return rc;
+
+ rc = sit9531x_output_forced_hiz(sitdev, index, &muted);
+ if (rc)
+ return rc;
+
+ sitdev->out[index].state_stale = false;
+
+ /*
+ * Each PLL page holds the PLL's output-enable mask, twelve bits:
+ * bits 0-7 in OUT_MAP_LO and bits 8-11 in OUT_MAP_HI[3:0]. PLLA and
+ * PLLB keep OUTn at bit n; PLLC and PLLD keep the outputs in mirrored
+ * order, OUTn at bit 11 - n, as SiTime's procedure scripts read
+ * them. The index is the physical output the pin drives, not
+ * the driver's logical one (translated above via the chip-info
+ * clkout_map[]: identity on SiT95316, non-contiguous on SiT95317).
+ *
+ * An output belongs to one PLL. Take the first that claims it, and
+ * say so if another does too.
+ */
+ claimed = 0;
+ for (pll_idx = 0; pll_idx < SIT9531X_NUM_PLLS; pll_idx++) {
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_OUT_MAP_LO, &map_lo);
+ if (rc)
+ return rc;
+
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_OUT_MAP_HI, &map_hi);
+ if (rc)
+ return rc;
+
+ mask = map_lo | (map_hi & GENMASK(3, 0)) << 8;
+ bit = pll_idx < 2 ? slot : SIT9531X_MAX_OUTPUTS - 1 - slot;
+ if (mask & BIT(bit))
+ claimed |= BIT(pll_idx);
+ }
+
+ if (!claimed) {
+ /* Output not mapped to any PLL */
+ out->pll_idx = 0;
+ out->routed = false;
+ out->enabled = false;
+ return 0;
+ }
+
+ out->pll_idx = __ffs(claimed);
+ out->routed = true;
+ out->enabled = !muted;
+
+ if (claimed & (claimed - 1))
+ dev_warn(sitdev->dev,
+ "OUT%u is in the output-enable mask of more than one PLL (0x%x); taking PLL%c\n",
+ slot, claimed, 'A' + out->pll_idx);
+
+ return 0;
+}
+
+/*
+ * sit9531x_ref_pll_mask_fetch - seed the input-to-PLL usage masks
+ *
+ * ref->pll_mask is the refcount the disconnect path uses to decide when
+ * an input receiver may be powered down: the physical input is only
+ * disabled once the last DPLL has released it. It therefore has to
+ * start out matching the hardware. Without this pass every mask starts
+ * at zero, and disconnecting an input from one DPLL drops the mask to
+ * zero and disables a receiver the other DPLLs are still locked to.
+ *
+ * An input is counted for a PLL when it appears in that PLL's Page-1
+ * priority table, which is exactly the condition the connect and
+ * disconnect callbacks maintain. Sources that are not physical inputs
+ * (OCXO, INTSYNC) and reserved codes are skipped.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+static int sit9531x_ref_pll_mask_fetch(struct sit9531x_dev *sitdev)
+{
+ u8 srcs[SIT9531X_PRIO_MAX_SLOTS];
+ u8 pll_idx, slot, index;
+ int rc;
+
+ for (pll_idx = 0; pll_idx < SIT9531X_NUM_PLLS; pll_idx++) {
+ rc = sit9531x_prio_table_read(sitdev, pll_idx, srcs);
+ if (rc)
+ return rc;
+
+ for (slot = 0; slot < SIT9531X_PRIO_MAX_SLOTS; slot++) {
+ index = sit9531x_hw_src_input(srcs[slot]);
+ if (index >= sitdev->info->num_inputs)
+ continue;
+
+ /*
+ * On a differentially configured pair only the P lane
+ * has a DPLL pin, so that is the lane the connect and
+ * disconnect callbacks account for. Fold an N-lane
+ * table entry onto its P lane, otherwise the count
+ * would land on a lane nothing ever releases. The
+ * signaling mode is already known here:
+ * sit9531x_input_mode_fetch() runs first.
+ */
+ if (sit9531x_input_is_n(index) &&
+ sitdev->ref[index].sig_mode == SIT9531X_MODE_DE)
+ index--;
+
+ sitdev->ref[index].pll_mask |= BIT(pll_idx);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * sit9531x_dev_state_fetch - read all hardware state at startup
+ *
+ * Called once during probe to populate the initial state cache.
+ */
+static int sit9531x_dev_state_fetch(struct sit9531x_dev *sitdev)
+{
+ int rc;
+ u8 i;
+
+ /* Detect SE/DE configuration before any per-lane access */
+ for (i = 0; i < sitdev->info->num_inputs / 2; i++) {
+ rc = sit9531x_input_mode_fetch(sitdev, i);
+ if (rc) {
+ dev_err(sitdev->dev,
+ "Failed to fetch CLKIN%u mode: %d\n", i, rc);
+ return rc;
+ }
+ }
+
+ for (i = 0; i < sitdev->info->num_inputs; i++) {
+ rc = sit9531x_ref_state_fetch(sitdev, i);
+ if (rc) {
+ dev_err(sitdev->dev,
+ "Failed to fetch input %u state: %d\n", i, rc);
+ return rc;
+ }
+ }
+
+ rc = sit9531x_input_mon_fetch(sitdev);
+ if (rc) {
+ dev_err(sitdev->dev,
+ "Failed to read the input clock monitors: %d\n", rc);
+ return rc;
+ }
+
+ /*
+ * The priority-table read walks the Page-1 registers, so it runs
+ * with multiop_lock held like every other multi-register sequence.
+ * Nothing can race with it here -- the DPLLs are not registered and
+ * the monitor is not running yet -- but the page handling stays
+ * serialized the same way as at runtime.
+ */
+ mutex_lock(&sitdev->multiop_lock);
+ rc = sit9531x_ref_pll_mask_fetch(sitdev);
+ mutex_unlock(&sitdev->multiop_lock);
+ if (rc) {
+ dev_err(sitdev->dev,
+ "Failed to fetch input priority tables: %d\n", rc);
+ return rc;
+ }
+
+ mutex_lock(&sitdev->multiop_lock);
+ rc = sit9531x_intsync_src_detect(sitdev);
+ mutex_unlock(&sitdev->multiop_lock);
+ if (rc) {
+ dev_err(sitdev->dev,
+ "Failed to detect INTSYNC source: %d\n", rc);
+ return rc;
+ }
+
+ for (i = 0; i < sitdev->info->num_outputs; i++) {
+ bool clamped;
+ s32 phase_ps;
+
+ rc = sit9531x_out_state_fetch(sitdev, i);
+ if (rc) {
+ dev_err(sitdev->dev,
+ "Failed to fetch output %u state: %d\n", i, rc);
+ return rc;
+ }
+
+ /*
+ * The delay registers are part of the profile the chip loads
+ * before probe, so an output can already carry one. Seeding
+ * the cache from the device is what lets a request of 0 ps
+ * clear it: the core drops a request equal to what the
+ * getter reports, and a cache that started at zero would
+ * make clearing a programmed delay impossible. An output
+ * the configuration does not route has no Fvco to decode
+ * against, which is not an error here. A delay beyond the
+ * range is reported as its end but not armed: re-timing it
+ * on a rate change would write that end over the profile's
+ * delay.
+ */
+ mutex_lock(&sitdev->multiop_lock);
+ rc = sit9531x_output_phase_read(sitdev, i, &phase_ps, &clamped);
+ mutex_unlock(&sitdev->multiop_lock);
+ if (!rc) {
+ sitdev->out[i].phase_adj = phase_ps;
+ sitdev->out[i].phase_armed = !clamped && phase_ps;
+ } else if (rc != -ENODATA) {
+ dev_err(sitdev->dev,
+ "Failed to read output %u delay: %d\n",
+ i, rc);
+ return rc;
+ }
+ }
+
+ /*
+ * The channel fetch walks the priority table and the debug status
+ * bus, multi-register sequences that expect the lock; nothing races
+ * with them yet, but they are serialized the same way as at runtime.
+ */
+ mutex_lock(&sitdev->multiop_lock);
+ for (i = 0; i < SIT9531X_NUM_PLLS; i++) {
+ rc = sit9531x_chan_state_fetch(sitdev, i);
+ if (rc)
+ break;
+ }
+ mutex_unlock(&sitdev->multiop_lock);
+ if (rc) {
+ dev_err(sitdev->dev, "Failed to fetch PLL%c state: %d\n",
+ 'A' + i, rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static void sit9531x_dev_ref_states_update(struct sit9531x_dev *sitdev)
+{
+ int i, rc;
+
+ for (i = 0; i < sitdev->info->num_inputs; i++) {
+ rc = sit9531x_ref_state_fetch(sitdev, i);
+ if (rc)
+ dev_warn(sitdev->dev,
+ "Failed to get REF%u status: %d\n", i, rc);
+ }
+
+ rc = sit9531x_input_mon_fetch(sitdev);
+ if (rc)
+ dev_warn(sitdev->dev,
+ "Failed to read the input clock monitors: %d\n", rc);
+}
+
+/*
+ * sit9531x_ref_pll_mask_rebuild - re-derive the input receiver refcounts
+ *
+ * ref->pll_mask decides when an input receiver may be powered down, and
+ * the connect and disconnect paths maintain it by hand. A request that
+ * failed part way through leaves it describing a table the device does
+ * not hold, and nothing else corrected it: a later disconnect could then
+ * drop the count to zero and gate an input another PLL is still locked
+ * to. Re-derive every mask from the tables the poll has just read. No
+ * extra bus traffic -- sit9531x_chan_state_fetch() refreshed the masks
+ * this reads immediately before.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+static void sit9531x_ref_pll_mask_rebuild(struct sit9531x_dev *sitdev)
+{
+ u8 pll_idx, src, index;
+
+ for (index = 0; index < sitdev->info->num_inputs; index++)
+ sitdev->ref[index].pll_mask = 0;
+
+ for (pll_idx = 0; pll_idx < SIT9531X_NUM_PLLS; pll_idx++) {
+ u16 mask = sitdev->chan[pll_idx].prio_mask;
+
+ for (src = 0; src < SIT9531X_PRIO_NUM_SRC; src++) {
+ if (!(mask & BIT(src)))
+ continue;
+
+ index = sit9531x_hw_src_input(src);
+ if (index < sitdev->info->num_inputs)
+ sitdev->ref[index].pll_mask |= BIT(pll_idx);
+ }
+ }
+}
+
+/*
+ * Release a forced holdover that a table write owes. The write forces
+ * holdover and releases it at the end; when the release failed, nothing
+ * else in the driver clears the bit -- a retry of the same request finds
+ * the table already as asked and does not run the sequence again -- so
+ * the poll retries it each tick until it lands.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+static void sit9531x_ho_release_retry(struct sit9531x_dev *sitdev, u8 pll_idx)
+{
+ struct sit9531x_chan *chan = &sitdev->chan[pll_idx];
+
+ if (!chan->ho_release_pending)
+ return;
+
+ if (sit9531x_update_pll_u8(sitdev, pll_idx, SIT9531X_PLL_REG_HO_CTRL,
+ BIT(SIT9531X_PLL_HO_FORCE_BIT), 0))
+ return;
+
+ chan->ho_release_pending = false;
+ dev_info(sitdev->dev, "PLL%c: forced holdover released\n",
+ 'A' + pll_idx);
+}
+
+static void sit9531x_dev_chan_states_update(struct sit9531x_dev *sitdev)
+{
+ int i, rc;
+
+ for (i = 0; i < SIT9531X_NUM_PLLS; i++) {
+ sit9531x_ho_release_retry(sitdev, i);
+ rc = sit9531x_chan_state_fetch(sitdev, i);
+ if (rc)
+ dev_warn(sitdev->dev,
+ "Failed to get PLL%c state: %d\n",
+ 'A' + i, rc);
+ }
+
+ sit9531x_ref_pll_mask_rebuild(sitdev);
+}
+
+/*
+ * sit9531x_dev_periodic_work - periodic hardware state polling
+ * @work: kthread_work pointer
+ *
+ * Polls hardware state at SIT9531X_STATUS_POLL_MS intervals.
+ * Updates reference and channel states, then delegates change
+ * detection to sit9531x_dpll_changes_check() for each registered DPLL.
+ */
+static void sit9531x_dev_periodic_work(struct kthread_work *work)
+{
+ struct sit9531x_dev *sitdev = container_of(work, struct sit9531x_dev,
+ work.work);
+ struct sit9531x_dpll *sitdpll;
+ int rc;
+
+ /*
+ * Update the cached ref[]/chan[] arrays under multiop_lock so the
+ * fetches are serialized against the DPLL callbacks that read
+ * these fields and against the chip's page selector.
+ *
+ * The lock is then dropped before sit9531x_dpll_changes_check(),
+ * which calls dpll_pin_change_ntf() / dpll_device_change_ntf().
+ * Those notification helpers take DPLL-subsystem locks that are
+ * already held when our callbacks are invoked from netlink
+ * context, and nesting multiop_lock around them would invert the
+ * lock order. changes_check() reads the cache published above,
+ * which is already consistent.
+ */
+ mutex_lock(&sitdev->multiop_lock);
+ sit9531x_dev_ref_states_update(sitdev);
+ sit9531x_dev_chan_states_update(sitdev);
+ mutex_unlock(&sitdev->multiop_lock);
+
+ list_for_each_entry(sitdpll, &sitdev->dplls, list)
+ sit9531x_dpll_changes_check(sitdpll);
+
+ /*
+ * Acknowledge the chip's notification latches after the tick has
+ * read and acted on them. Without this, the W1C bits remain set
+ * and -- on boards that wire INTRB -- the line stays asserted,
+ * re-firing the threaded handler back to back. The helper writes
+ * W1C bits across page 0 and page 6 and must run under
+ * multiop_lock to serialize the page selector against userspace
+ * dpll ops. Failure is non-fatal: status was already consumed
+ * for this tick and the next tick re-processes whatever stayed
+ * latched.
+ */
+ mutex_lock(&sitdev->multiop_lock);
+ rc = sit9531x_clear_notifications(sitdev);
+ mutex_unlock(&sitdev->multiop_lock);
+ if (rc)
+ dev_warn_ratelimited(sitdev->dev,
+ "Failed to clear notifications: %d\n",
+ rc);
+
+ /* Run twice a second */
+ kthread_queue_delayed_work(sitdev->kworker, &sitdev->work,
+ msecs_to_jiffies(SIT9531X_STATUS_POLL_MS));
+}
+
+/*
+ * sit9531x_irq_thread_fn - threaded IRQ handler for the chip's INTRB line
+ *
+ * Triggered when the chip asserts INTRB (and only when DT wires up the
+ * client interrupt; absent property == handler never installed). The
+ * action mirrors a periodic-work tick: queue an immediate run so status
+ * registers are read and DPLL changes_check fires without waiting for
+ * the next poll deadline. Polling continues to run as a fallback.
+ */
+static irqreturn_t sit9531x_irq_thread_fn(int irq, void *data)
+{
+ struct sit9531x_dev *sitdev = data;
+ int rc;
+
+ /*
+ * Acknowledge the chip's notification latches from the threaded
+ * handler itself. With IRQF_ONESHOT the line is unmasked on
+ * return, so deferring the W1C clear to the async kworker would
+ * let a still-asserted INTRB re-fire immediately (interrupt storm).
+ * Clear here, then kick the poll worker to read state and run
+ * changes_check.
+ */
+ mutex_lock(&sitdev->multiop_lock);
+ rc = sit9531x_clear_notifications(sitdev);
+ mutex_unlock(&sitdev->multiop_lock);
+ if (rc) {
+ dev_warn_ratelimited(sitdev->dev,
+ "IRQ: failed to clear notifications: %d\n",
+ rc);
+ /*
+ * The latch was not acknowledged, so with IRQF_ONESHOT the
+ * still-asserted line re-enters this handler as soon as it
+ * returns. Returning IRQ_NONE leaves that to the spurious
+ * detector, which needs roughly 100000 interrupts and resets
+ * its count every tenth of a second -- unreachable when each
+ * pass costs an I2C timeout. Give up on the line instead:
+ * the periodic poll reads the same state without it, so the
+ * driver keeps working on a board whose INTRB cannot be
+ * acknowledged.
+ */
+ if (++sitdev->irq_ack_fails < SIT9531X_IRQ_ACK_TRIES)
+ return IRQ_NONE;
+
+ dev_err(sitdev->dev,
+ "IRQ %d disabled: notifications cannot be cleared, polling only\n",
+ irq);
+ disable_irq_nosync(irq);
+ return IRQ_NONE;
+ }
+
+ sitdev->irq_ack_fails = 0;
+
+ kthread_mod_delayed_work(sitdev->kworker, &sitdev->work, 0);
+ return IRQ_HANDLED;
+}
+
+/*
+ * The driver reports every PLL as selecting by priority. A profile can
+ * put a PLL in manual active select instead (PLL_CONFIG1F_PLL bit 6), and
+ * the mode userspace sees would then be wrong; say so rather than
+ * reconfigure what the profile chose. The warning also says what the PLL
+ * then follows: its active selection, or with MISCINNER_PLL bit 5 the
+ * manual input select -- the input-select pins, or the input code that
+ * GPIO_INPUT_FUNC_CTRL5..8 holds when its bit 4 is set.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+static void sit9531x_manual_sel_report(struct sit9531x_dev *sitdev)
+{
+ u8 cfg, misc, sel, i;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ for (i = 0; i < SIT9531X_NUM_PLLS; i++) {
+ if (sit9531x_read_pll_u8(sitdev, i, SIT9531X_PLL_REG_CONFIG1F,
+ &cfg))
+ return;
+ if (!(cfg & SIT9531X_PLL_CONFIG1F_MANUAL_SEL))
+ continue;
+
+ if (sit9531x_read_pll_u8(sitdev, i, SIT9531X_PLL_REG_MISCINNER,
+ &misc))
+ return;
+ if (!(misc & SIT9531X_PLL_MISCINNER_MAN_IN_SEL)) {
+ dev_warn(sitdev->dev,
+ "PLL%c: the profile selects its reference manually, following its active selection; it is reported as automatic\n",
+ 'A' + i);
+ continue;
+ }
+
+ if (sit9531x_read_u8(sitdev, SIT9531X_REG_MAN_IN_SEL(i), &sel))
+ return;
+ if (sel & SIT9531X_MAN_IN_SEL_FROM_REG) {
+ sel = FIELD_GET(SIT9531X_MAN_IN_SEL_MASK, sel);
+ dev_warn(sitdev->dev,
+ "PLL%c: the profile selects its reference manually, input code %u from the register; it is reported as automatic\n",
+ 'A' + i, sel);
+ } else {
+ dev_warn(sitdev->dev,
+ "PLL%c: the profile selects its reference manually, from the input-select pins; it is reported as automatic\n",
+ 'A' + i);
+ }
+ }
+}
+
+/*
+ * Report what the device loaded from its EEPROM, and warn if it does not
+ * look like a healthy load.
+ *
+ * A profile that failed to load leaves the part running something other
+ * than what the board was designed around -- dividers, output routing
+ * and priority tables all differ -- while every register still reads
+ * back a plausible value. Naming the profile and saying whether the
+ * load was clean turns that into something visible at startup instead of
+ * something inferred from measurements later.
+ *
+ * This only reports. Boards in this family may have their
+ * configuration pushed over I2C rather than held in an EEPROM, and there
+ * the CRC pair means nothing, so a mismatch is not grounds for refusing
+ * to drive the device.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+static void sit9531x_eeprom_state_report(struct sit9531x_dev *sitdev)
+{
+ u32 rec_crc = 0, cal_crc = 0, prof_id = 0;
+ u8 notif, v;
+ int rc, i;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ /* Profile id: three bytes, least significant first. */
+ for (i = 2; i >= 0; i--) {
+ rc = sit9531x_read_u8(sitdev, SIT9531X_REG_PROFILE_ID + i, &v);
+ if (rc)
+ return;
+ prof_id = prof_id << 8 | v;
+ }
+
+ dev_info(sitdev->dev, "profile id %u\n", prof_id);
+
+ /* Both CRCs: four bytes, most significant first. */
+ for (i = 0; i < 4; i++) {
+ rc = sit9531x_read_u8(sitdev, SIT9531X_REG_REC_CRC + i, &v);
+ if (rc)
+ return;
+ rec_crc = rec_crc << 8 | v;
+
+ rc = sit9531x_read_u8(sitdev, SIT9531X_REG_CAL_CRC + i, &v);
+ if (rc)
+ return;
+ cal_crc = cal_crc << 8 | v;
+ }
+
+ rc = sit9531x_read_u8(sitdev, SIT9531X_REG_EEPROM_NOTIF, ¬if);
+ if (rc)
+ return;
+
+ /*
+ * A clean load leaves the read-done bit set and every defect bit
+ * clear. Both CRCs zero means no EEPROM read happened at all:
+ * boards in this family may take their configuration over I2C
+ * instead of an EEPROM, and there the CRC pair means nothing, so
+ * that case is not a mismatch worth warning about.
+ */
+ if (!rec_crc && !cal_crc)
+ dev_dbg(sitdev->dev,
+ "no EEPROM profile (configuration pushed over I2C)\n");
+ else if (rec_crc != cal_crc)
+ dev_warn(sitdev->dev,
+ "EEPROM CRC mismatch: stored %08x, computed %08x\n",
+ rec_crc, cal_crc);
+ else if (notif != SIT9531X_EEPROM_READ_DONE)
+ dev_warn(sitdev->dev,
+ "EEPROM read reported defects (notify %02x)\n",
+ notif);
+ else
+ dev_dbg(sitdev->dev, "EEPROM profile loaded, CRC %08x\n",
+ cal_crc);
+}
+
+/*
+ * Report which PLLs came up, and flag the one case that is a real
+ * inconsistency rather than a configuration choice.
+ *
+ * A PLL the loaded configuration leaves unused never reaches its active
+ * state, which is normal and not worth a warning. A PLL that has
+ * outputs routed to it and is still not active is different: something
+ * that is meant to be generating clocks is not running, and every value
+ * read from it -- lock state, phase, frequency offset -- describes a
+ * stopped loop. Say so once at startup rather than leaving it to be
+ * discovered through measurements that quietly read as zero.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+static void sit9531x_pll_states_report(struct sit9531x_dev *sitdev)
+{
+ unsigned int idx, i;
+ bool routed;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ for (i = 0; i < SIT9531X_NUM_PLLS; i++) {
+ if (sitdev->chan[i].active) {
+ dev_dbg(sitdev->dev, "PLL%c active\n", 'A' + i);
+ continue;
+ }
+
+ routed = false;
+ for (idx = 0; idx < sitdev->info->num_outputs; idx++) {
+ const struct sit9531x_out *out;
+
+ out = sit9531x_out_state_get(sitdev, idx);
+ if (out->routed && out->pll_idx == i) {
+ routed = true;
+ break;
+ }
+ }
+
+ if (routed)
+ dev_warn(sitdev->dev,
+ "PLL%c drives outputs but is not in its active state\n",
+ 'A' + i);
+ else
+ dev_dbg(sitdev->dev, "PLL%c unused by the loaded configuration\n",
+ 'A' + i);
+ }
+}
+
+/*
+ * sit9531x_input_pin_is_registrable - check if an input pin is registrable
+ *
+ * Split out so input-model changes stay local to this helper.
+ *
+ * Return: true if the input pin should be registered, false otherwise
+ */
+static bool sit9531x_input_pin_is_registrable(struct sit9531x_dev *sitdev,
+ u8 index)
+{
+ if (index >= sitdev->info->num_inputs)
+ return false;
+
+ /*
+ * The N lane of a differentially-configured pair is not a
+ * standalone input and is skipped (zl3073x model).
+ */
+ if (sit9531x_input_is_n(index) &&
+ sitdev->ref[index].sig_mode == SIT9531X_MODE_DE)
+ return false;
+
+ return true;
+}
+
+/*
+ * Warn about a pin node in the firmware description whose reg names no
+ * pin this device can have: an input lane or an output the variant does
+ * not have, or the N lane of a pair the configuration runs differential.
+ * Nodes are looked up from the pins (sit9531x_pin_props_get()), so such a
+ * node would otherwise have its label and frequencies dropped without a
+ * word. The binding bounds reg per variant; this catches what reaches
+ * the driver unvalidated, and the pair mode, which only the loaded
+ * configuration decides. An output that exists but that no PLL drives
+ * gets no pin either and is not reported: which outputs a configuration
+ * uses is not a fault in the description.
+ */
+static void sit9531x_pin_nodes_check(struct sit9531x_dev *sitdev)
+{
+ struct fwnode_handle *pins, *node;
+ bool found;
+ u32 reg;
+ u8 i;
+
+ pins = device_get_named_child_node(sitdev->dev, "input-pins");
+ fwnode_for_each_child_node(pins, node) {
+ if (fwnode_property_read_u32(node, "reg", ®))
+ continue;
+ if (reg < sitdev->info->num_inputs &&
+ sit9531x_input_pin_is_registrable(sitdev, reg))
+ continue;
+ dev_warn(sitdev->dev,
+ "input-pins/%pfwP: reg %u is %s, node ignored\n",
+ node, reg,
+ reg < sitdev->info->num_inputs ?
+ "the N lane of a differential pair" :
+ "not an input lane");
+ }
+ fwnode_handle_put(pins);
+
+ pins = device_get_named_child_node(sitdev->dev, "output-pins");
+ fwnode_for_each_child_node(pins, node) {
+ if (fwnode_property_read_u32(node, "reg", ®))
+ continue;
+ found = false;
+ for (i = 0; i < sitdev->info->num_outputs; i++) {
+ if (sitdev->info->clkout_map[i] == reg) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ dev_warn(sitdev->dev,
+ "output-pins/%pfwP: reg %u is not an output of %s, node ignored\n",
+ node, reg, sitdev->info->name);
+ }
+ fwnode_handle_put(pins);
+}
+
+/*
+ * sit9531x_dev_start - start normal operation
+ *
+ * Fetches initial hardware state, registers all DPLL devices and
+ * their pins, and starts the periodic monitoring thread.
+ */
+int sit9531x_dev_start(struct sit9531x_dev *sitdev)
+{
+ struct sit9531x_dpll *sitdpll;
+ int rc;
+
+ /* Fetch device state */
+ rc = sit9531x_dev_state_fetch(sitdev);
+ if (rc)
+ return rc;
+
+ mutex_lock(&sitdev->multiop_lock);
+ sit9531x_eeprom_state_report(sitdev);
+ sit9531x_manual_sel_report(sitdev);
+ sit9531x_pll_states_report(sitdev);
+ mutex_unlock(&sitdev->multiop_lock);
+
+ sit9531x_pin_nodes_check(sitdev);
+
+ list_for_each_entry(sitdpll, &sitdev->dplls, list) {
+ rc = sit9531x_dpll_register(sitdpll);
+ if (rc) {
+ dev_err_probe(sitdev->dev, rc,
+ "Failed to register DPLL%u\n",
+ sitdpll->id);
+ goto err_unregister;
+ }
+ }
+
+ kthread_queue_delayed_work(sitdev->kworker, &sitdev->work, 0);
+
+ return 0;
+
+err_unregister:
+ /*
+ * Unregister what did register. The caller frees the list on this
+ * path, so leaving a DPLL registered would hand the subsystem a
+ * pointer to freed memory.
+ */
+ list_for_each_entry_continue_reverse(sitdpll, &sitdev->dplls, list)
+ sit9531x_dpll_unregister(sitdpll);
+
+ return rc;
+}
+
+/*
+ * sit9531x_dev_stop - stop normal operation
+ *
+ * Cancels the monitoring thread and unregisters all DPLL devices
+ * and their pins.
+ */
+void sit9531x_dev_stop(struct sit9531x_dev *sitdev)
+{
+ struct sit9531x_dpll *sitdpll;
+
+ kthread_cancel_delayed_work_sync(&sitdev->work);
+
+ list_for_each_entry(sitdpll, &sitdev->dplls, list) {
+ if (sitdpll->dpll_dev)
+ sit9531x_dpll_unregister(sitdpll);
+ }
+}
+
+static struct sit9531x_dpll_pin *
+sit9531x_dpll_pin_alloc(struct sit9531x_dpll *sitdpll,
+ enum dpll_pin_direction dir, u8 id)
+{
+ struct sit9531x_dpll_pin *pin;
+
+ pin = kzalloc_obj(*pin, GFP_KERNEL);
+ if (!pin)
+ return ERR_PTR(-ENOMEM);
+
+ pin->dpll = sitdpll;
+ pin->dir = dir;
+ pin->id = id;
+
+ return pin;
+}
+
+/*
+ * sit9531x_dpll_pin_register - register a DPLL pin with the subsystem
+ * @index: absolute pin index for clock_id namespace
+ *
+ * Gets pin properties from firmware, creates or gets a dpll_pin,
+ * and registers it with the parent DPLL device.
+ */
+static int sit9531x_dpll_pin_register(struct sit9531x_dpll_pin *pin,
+ u32 index)
+{
+ struct sit9531x_dpll *sitdpll = pin->dpll;
+ struct sit9531x_pin_props *props;
+ const struct dpll_pin_ops *ops;
+ int rc;
+
+ props = sit9531x_pin_props_get(sitdpll->dev, pin->dir, pin->id);
+ if (IS_ERR(props))
+ return PTR_ERR(props);
+
+ strscpy(pin->label, props->package_label, sizeof(pin->label));
+ pin->fwnode = fwnode_handle_get(props->fwnode);
+
+ pin->dpll_pin = dpll_pin_get(sitdpll->dev->clock_id, index,
+ THIS_MODULE, &props->dpll_props,
+ &pin->tracker);
+ if (IS_ERR(pin->dpll_pin)) {
+ rc = PTR_ERR(pin->dpll_pin);
+ goto err_pin_get;
+ }
+ dpll_pin_fwnode_set(pin->dpll_pin, props->fwnode);
+
+ ops = sit9531x_dpll_pin_ops_get(pin);
+
+ rc = dpll_pin_register(sitdpll->dpll_dev, pin->dpll_pin, ops, pin);
+ if (rc)
+ goto err_register;
+
+ sit9531x_pin_props_put(props);
+
+ return 0;
+
+err_register:
+ dpll_pin_put(pin->dpll_pin, &pin->tracker);
+err_pin_get:
+ /* dpll_pin_get() left an ERR_PTR here. */
+ pin->dpll_pin = NULL;
+ fwnode_handle_put(pin->fwnode);
+ pin->fwnode = NULL;
+ sit9531x_pin_props_put(props);
+
+ return rc;
+}
+
+static void sit9531x_dpll_pin_unregister(struct sit9531x_dpll_pin *pin)
+{
+ struct sit9531x_dpll *sitdpll = pin->dpll;
+ struct dpll_pin *dpll_pin = pin->dpll_pin;
+ const struct dpll_pin_ops *ops;
+
+ ops = sit9531x_dpll_pin_ops_get(pin);
+
+ /*
+ * Clear the pointer before the subsystem is told to drop the pin.
+ * A callback tests this field to tell a pin on its way out from a
+ * live one, and it runs under the subsystem's device lock, which
+ * dpll_pin_unregister() takes as well. Clearing first means such a
+ * callback either sees NULL and leaves the pin alone, or still sees
+ * the pin and finishes before the unregister can proceed. Clearing
+ * afterwards leaves a window where the field still reads as live
+ * and the pin behind it is already gone.
+ */
+ WRITE_ONCE(pin->dpll_pin, NULL);
+
+ dpll_pin_unregister(sitdpll->dpll_dev, dpll_pin, ops, pin);
+ dpll_pin_put(dpll_pin, &pin->tracker);
+
+ fwnode_handle_put(pin->fwnode);
+ pin->fwnode = NULL;
+}
+
+static void sit9531x_dpll_pins_unregister(struct sit9531x_dpll *sitdpll)
+{
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+ struct sit9531x_dpll_pin *pin, *next;
+ LIST_HEAD(dead);
+
+ /*
+ * Take the pins out of the subsystem first. That call takes the
+ * DPLL device lock, which the driver's own callbacks are already
+ * under when they take multiop_lock, so it must not run with
+ * multiop_lock held or the two orders cross.
+ */
+ list_for_each_entry(pin, &sitdpll->pins, list)
+ sit9531x_dpll_pin_unregister(pin);
+
+ /*
+ * Then detach the list under multiop_lock, which every callback that
+ * walks it holds, so once the splice returns no callback can reach
+ * these nodes. The poll walks the list as well without holding the
+ * lock throughout, but it is not running here: sit9531x_dev_stop()
+ * cancels it before unregistering, and the IRQ that could queue it
+ * again is released before that, its devm action having been added
+ * after the DPLLs were set up. On the registration error path the
+ * poll has not been queued yet. So the nodes are safe to free.
+ */
+ mutex_lock(&sitdev->multiop_lock);
+ list_splice_init(&sitdpll->pins, &dead);
+ mutex_unlock(&sitdev->multiop_lock);
+
+ list_for_each_entry_safe(pin, next, &dead, list) {
+ list_del(&pin->list);
+ kfree(pin);
+ }
+}
+
+/*
+ * sit9531x_dpll_pin_is_registrable - check if a pin should be registered
+ * @dir: pin direction
+ * @index: pin hardware index
+ *
+ * For input pins: delegate to sit9531x_input_pin_is_registrable().
+ * For output pins: the pin is registrable if this DPLL is routed to it,
+ * whether or not it is currently driving.
+ *
+ * Return: true if pin should be registered, false otherwise
+ */
+static bool sit9531x_dpll_pin_is_registrable(struct sit9531x_dpll *sitdpll,
+ enum dpll_pin_direction dir,
+ u8 index)
+{
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+
+ if (dir == DPLL_PIN_DIRECTION_INPUT) {
+ if (index == SIT9531X_MAX_INPUTS)
+ return true;
+ if (index == SIT9531X_INTSYNC_PIN_ID)
+ return true;
+
+ return sit9531x_input_pin_is_registrable(sitdev, index);
+ }
+
+ if (index == SIT9531X_INTSYNC_OUT_PIN_ID)
+ return true;
+
+ if (index >= sitdev->info->num_outputs)
+ return false;
+
+ return sitdev->out[index].pll_idx == sitdpll->id &&
+ sitdev->out[index].routed;
+}
+
+/*
+ * sit9531x_dpll_pins_register - register all registrable pins
+ *
+ * Enumerates all possible input and output pins, checks registrability,
+ * and registers each one. Input pins come first, then output pins,
+ * with input pins first, then output pins.
+ */
+static int sit9531x_dpll_pins_register(struct sit9531x_dpll *sitdpll)
+{
+ struct sit9531x_dpll_pin *pin;
+ enum dpll_pin_direction dir;
+ u8 id, index;
+ int rc;
+
+ for (index = 0; index < SIT9531X_NUM_PINS_TOTAL; index++) {
+ if (index < SIT9531X_NUM_INPUT_PINS) {
+ id = index;
+ dir = DPLL_PIN_DIRECTION_INPUT;
+ } else {
+ id = index - SIT9531X_NUM_INPUT_PINS;
+ dir = DPLL_PIN_DIRECTION_OUTPUT;
+ }
+
+ if (!sit9531x_dpll_pin_is_registrable(sitdpll, dir, id))
+ continue;
+
+ pin = sit9531x_dpll_pin_alloc(sitdpll, dir, id);
+ if (IS_ERR(pin)) {
+ rc = PTR_ERR(pin);
+ goto error;
+ }
+
+ rc = sit9531x_dpll_pin_register(pin, index);
+ if (rc) {
+ kfree(pin);
+ goto error;
+ }
+
+ /*
+ * Pins registered before this one are already reachable from
+ * netlink, and the walks over this list run under
+ * multiop_lock, so the insertion does too. The baseline the
+ * poll compares the pin against is taken under the same
+ * lock, from the cache the create notification just reported.
+ */
+ mutex_lock(&sitdpll->dev->multiop_lock);
+ sit9531x_dpll_pin_baseline(pin);
+ list_add(&pin->list, &sitdpll->pins);
+ mutex_unlock(&sitdpll->dev->multiop_lock);
+ }
+
+ return 0;
+
+error:
+ sit9531x_dpll_pins_unregister(sitdpll);
+ return rc;
+}
+
+static int sit9531x_dpll_device_register(struct sit9531x_dpll *sitdpll)
+{
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+ int rc;
+
+ sitdpll->ops = sit9531x_dpll_device_ops;
+
+ sitdpll->dpll_dev = dpll_device_get(sitdev->clock_id, sitdpll->id,
+ THIS_MODULE, &sitdpll->tracker);
+ if (IS_ERR(sitdpll->dpll_dev)) {
+ rc = PTR_ERR(sitdpll->dpll_dev);
+ sitdpll->dpll_dev = NULL;
+ return rc;
+ }
+
+ rc = dpll_device_register(sitdpll->dpll_dev,
+ sit9531x_prop_dpll_type_get(sitdev,
+ sitdpll->id),
+ &sitdpll->ops, sitdpll);
+ if (rc) {
+ dpll_device_put(sitdpll->dpll_dev, &sitdpll->tracker);
+ sitdpll->dpll_dev = NULL;
+ }
+
+ return rc;
+}
+
+static void sit9531x_dpll_device_unregister(struct sit9531x_dpll *sitdpll)
+{
+ dpll_device_unregister(sitdpll->dpll_dev, &sitdpll->ops, sitdpll);
+ dpll_device_put(sitdpll->dpll_dev, &sitdpll->tracker);
+ sitdpll->dpll_dev = NULL;
+}
+
+/*
+ * sit9531x_dpll_alloc - allocate a DPLL device structure
+ * @sitdev: parent device
+ * @ch: PLL channel number (0-3)
+ *
+ * Return: pointer to allocated DPLL on success, error pointer on error
+ */
+struct sit9531x_dpll *sit9531x_dpll_alloc(struct sit9531x_dev *sitdev, u8 ch)
+{
+ struct sit9531x_dpll *sitdpll;
+
+ sitdpll = kzalloc_obj(*sitdpll, GFP_KERNEL);
+ if (!sitdpll)
+ return ERR_PTR(-ENOMEM);
+
+ sitdpll->dev = sitdev;
+ sitdpll->id = ch;
+ sitdpll->lock_status = DPLL_LOCK_STATUS_UNLOCKED;
+ sitdpll->lock_status_error = DPLL_LOCK_STATUS_ERROR_NONE;
+ INIT_LIST_HEAD(&sitdpll->pins);
+
+ return sitdpll;
+}
+
+/*
+ * sit9531x_dpll_free - deallocate a DPLL device structure
+ * @sitdpll: DPLL to free
+ */
+void sit9531x_dpll_free(struct sit9531x_dpll *sitdpll)
+{
+ kfree(sitdpll);
+}
+
+/*
+ * sit9531x_dpll_register - register DPLL device and all its pins
+ *
+ * Registers the DPLL device with the subsystem and then registers
+ * all input and output pins that are connected to this PLL.
+ */
+int sit9531x_dpll_register(struct sit9531x_dpll *sitdpll)
+{
+ int rc;
+
+ rc = sit9531x_dpll_device_register(sitdpll);
+ if (rc)
+ return rc;
+
+ sit9531x_dpll_lock_status_baseline(sitdpll);
+
+ rc = sit9531x_dpll_pins_register(sitdpll);
+ if (rc) {
+ sit9531x_dpll_device_unregister(sitdpll);
+ return rc;
+ }
+
+ return 0;
+}
+
+/* sit9531x_dpll_unregister - unregister DPLL device and its pins */
+void sit9531x_dpll_unregister(struct sit9531x_dpll *sitdpll)
+{
+ sit9531x_dpll_pins_unregister(sitdpll);
+ sit9531x_dpll_device_unregister(sitdpll);
+}
+
+static void sit9531x_dpll_list_free(struct sit9531x_dev *sitdev)
+{
+ struct sit9531x_dpll *sitdpll, *next;
+
+ list_for_each_entry_safe(sitdpll, next, &sitdev->dplls, list) {
+ list_del(&sitdpll->list);
+ sit9531x_dpll_free(sitdpll);
+ }
+}
+
+/* Runs only once the device is fully started, see the caller. */
+static void sit9531x_dev_dpll_fini(void *ptr)
+{
+ struct sit9531x_dev *sitdev = ptr;
+
+ sit9531x_dev_stop(sitdev);
+ kthread_destroy_worker(sitdev->kworker);
+ sit9531x_dpll_list_free(sitdev);
+}
+
+static int sit9531x_devm_dpll_init(struct sit9531x_dev *sitdev)
+{
+ struct kthread_worker *kworker;
+ struct sit9531x_dpll *sitdpll;
+ unsigned int i;
+ int rc;
+
+ INIT_LIST_HEAD(&sitdev->dplls);
+ kthread_init_delayed_work(&sitdev->work, sit9531x_dev_periodic_work);
+
+ for (i = 0; i < SIT9531X_NUM_PLLS; i++) {
+ sitdpll = sit9531x_dpll_alloc(sitdev, i);
+ if (IS_ERR(sitdpll)) {
+ rc = dev_err_probe(sitdev->dev, PTR_ERR(sitdpll),
+ "Failed to alloc DPLL%u\n", i);
+ goto err_free_dplls;
+ }
+
+ list_add_tail(&sitdpll->list, &sitdev->dplls);
+ }
+
+ kworker = kthread_run_worker(0, "sit9531x-%s", dev_name(sitdev->dev));
+ if (IS_ERR(kworker)) {
+ rc = PTR_ERR(kworker);
+ goto err_free_dplls;
+ }
+ sitdev->kworker = kworker;
+
+ rc = sit9531x_dev_start(sitdev);
+ if (rc) {
+ rc = dev_err_probe(sitdev->dev, rc, "Failed to start device\n");
+ goto err_destroy_worker;
+ }
+
+ /*
+ * Only now is every field the cleanup touches valid, so this is the
+ * first point at which the action may be registered. On failure it
+ * runs the action itself, which is correct here and only here.
+ */
+ return devm_add_action_or_reset(sitdev->dev, sit9531x_dev_dpll_fini,
+ sitdev);
+
+err_destroy_worker:
+ kthread_destroy_worker(sitdev->kworker);
+err_free_dplls:
+ sit9531x_dpll_list_free(sitdev);
+
+ return rc;
+}
+
+/*
+ * sit9531x_read_variant_id - read chip variant ID byte from hardware
+ * @id: output variant ID byte
+ *
+ * Reads the single-byte variant identification register from Page 0
+ * reg 0x02 (95317 = 0x17, 95316 = 0x31). Reg 0x03 holds a separate
+ * revision byte and is intentionally not consumed here.
+ */
+static int sit9531x_read_variant_id(struct sit9531x_dev *sitdev, u8 *id)
+{
+ return sit9531x_read_u8(sitdev, SIT9531X_REG_VARIANT_ID, id);
+}
+
+static const struct sit9531x_chip_info *sit9531x_match_variant(u8 id)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(sit9531x_chip_ids); i++) {
+ if (sit9531x_chip_ids[i].id == id)
+ return &sit9531x_chip_ids[i];
+ }
+
+ return NULL;
+}
+
+/*
+ * sit9531x_derive_clock_id - build EUI-64 clock identifier
+ *
+ * Generates a deterministic 64-bit identifier from the SiTime OUI,
+ * the I2C bus number and the I2C address. The bus number
+ * disambiguates two same-variant parts at the same address on
+ * different adapters (or behind a mux), which the DPLL core would
+ * otherwise fold onto one set of objects; DT bus aliases keep the
+ * numbering, and with it the clock_id, stable across reboots.
+ *
+ * Return: 64-bit clock identifier
+ */
+static u64 sit9531x_derive_clock_id(struct sit9531x_dev *sitdev)
+{
+ u64 clkid;
+ int adap;
+
+ adap = i2c_adapter_id(sitdev->client->adapter);
+
+ /*
+ * The OUI with its EUI-64 filler takes the top 40 bits. Bus numbers
+ * are not bounded by the number of adapters -- a DT alias or a mux
+ * child can number a bus 256 or above on a board with a handful of
+ * them -- so the bus gets 16 bits. The variant does not need a
+ * field: one address on one bus holds one part.
+ */
+ clkid = SIT9531X_OUI << 24;
+ clkid |= (u64)(adap & 0xffff) << 8;
+ clkid |= (u64)sitdev->client->addr;
+
+ return clkid;
+}
+
+int sit9531x_dev_probe(struct sit9531x_dev *sitdev)
+{
+ struct clk *xtal_clk;
+ u8 variant_id;
+ int rc;
+
+ /*
+ * Fvco = Fref * (DIVN + frac/2^32) with Fref = xtal_freq << doubler,
+ * so every freq_set and phase_adjust path divides by a rate derived
+ * from the XO feeding XIN/XO_CLK. The rate normally comes from a
+ * "clocks" phandle (clock-names = "xtal"). On platforms where the
+ * firmware does not expose the XO through the clock framework, fall
+ * back to a "clock-frequency" device property.
+ */
+ xtal_clk = devm_clk_get_optional_enabled(sitdev->dev, "xtal");
+ if (IS_ERR(xtal_clk))
+ return dev_err_probe(sitdev->dev, PTR_ERR(xtal_clk),
+ "Failed to get xtal clock\n");
+ sitdev->xtal_freq = clk_get_rate(xtal_clk);
+ if (!sitdev->xtal_freq) {
+ u32 freq;
+
+ if (!device_property_read_u32(sitdev->dev, "clock-frequency",
+ &freq))
+ sitdev->xtal_freq = freq;
+ }
+ if (!sitdev->xtal_freq)
+ return dev_err_probe(sitdev->dev, -EINVAL,
+ "no xtal rate: provide clocks=<&xo> + clock-names=\"xtal\", or a clock-frequency property\n");
+
+ /*
+ * Held deasserted, never pulsed: the chip configuration comes from
+ * efuse or an NVM blob applied before probe, and a reset would
+ * discard it. Must precede the first I2C access, as a board that
+ * powers up asserted keeps the chip unreachable until released.
+ */
+ sitdev->reset_gpio = devm_gpiod_get_optional(sitdev->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(sitdev->reset_gpio))
+ return dev_err_probe(sitdev->dev, PTR_ERR(sitdev->reset_gpio),
+ "Failed to request reset gpio\n");
+ if (sitdev->reset_gpio)
+ fsleep(10000); /* internal boot after release */
+
+ rc = sit9531x_read_variant_id(sitdev, &variant_id);
+ if (rc)
+ return rc;
+
+ sitdev->info = sit9531x_match_variant(variant_id);
+ if (!sitdev->info)
+ return dev_err_probe(sitdev->dev, -ENODEV,
+ "Unknown variant ID: 0x%02x\n",
+ variant_id);
+
+ sitdev->clock_id = sit9531x_derive_clock_id(sitdev);
+ sitdev->intsync_src = -1;
+
+ rc = devm_mutex_init(sitdev->dev, &sitdev->multiop_lock);
+ if (rc)
+ return dev_err_probe(sitdev->dev, rc,
+ "Failed to initialize mutex\n");
+
+ dev_info(sitdev->dev, "%s detected, %u inputs, %u outputs\n",
+ sitdev->info->name, sitdev->info->num_inputs,
+ sitdev->info->num_outputs);
+
+ /*
+ * Before the IRQ: the handler reaches sitdev->kworker through
+ * kthread_mod_delayed_work(), so the worker has to exist before an
+ * INTRB assertion can land.
+ */
+ rc = sit9531x_devm_dpll_init(sitdev);
+ if (rc)
+ return rc;
+
+ /* Absent "interrupts" leaves client->irq 0 and the poll in charge. */
+ sitdev->irq = sitdev->client ? sitdev->client->irq : 0;
+ if (sitdev->irq > 0) {
+ rc = devm_request_threaded_irq(sitdev->dev, sitdev->irq,
+ NULL, sit9531x_irq_thread_fn,
+ IRQF_ONESHOT,
+ dev_name(sitdev->dev), sitdev);
+ if (rc)
+ return dev_err_probe(sitdev->dev, rc,
+ "Failed to request IRQ %d\n",
+ sitdev->irq);
+ }
+
+ return 0;
+}
+
+static int sit9531x_i2c_probe(struct i2c_client *client)
+{
+ struct sit9531x_dev *sitdev;
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(client, &sit9531x_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(regmap),
+ "Failed to initialize regmap\n");
+
+ sitdev = devm_kzalloc(&client->dev, sizeof(*sitdev), GFP_KERNEL);
+ if (!sitdev)
+ return -ENOMEM;
+
+ sitdev->dev = &client->dev;
+ sitdev->client = client;
+ sitdev->regmap = regmap;
+ i2c_set_clientdata(client, sitdev);
+
+ return sit9531x_dev_probe(sitdev);
+}
+
+static const struct of_device_id sit9531x_of_match[] = {
+ { .compatible = "sitime,sit95316" },
+ { .compatible = "sitime,sit95317" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sit9531x_of_match);
+
+/*
+ * The poll worker is not freezable and would keep issuing paged I2C
+ * transfers into a suspended adapter, where i2c_transfer() fails and a
+ * tick landing mid-suspend could tear a paged sequence between the
+ * page-selector write and the register access. Park the worker (and
+ * the IRQ that kicks it) across suspend and take a fresh sample on
+ * resume.
+ */
+static int sit9531x_suspend(struct device *dev)
+{
+ struct sit9531x_dev *sitdev = dev_get_drvdata(dev);
+
+ if (sitdev->irq > 0)
+ disable_irq(sitdev->irq);
+ kthread_cancel_delayed_work_sync(&sitdev->work);
+
+ return 0;
+}
+
+static int sit9531x_resume(struct device *dev)
+{
+ struct sit9531x_dev *sitdev = dev_get_drvdata(dev);
+ int rc;
+ u8 i;
+
+ sit9531x_page_cache_drop(sitdev);
+
+ /*
+ * A part that lost power across suspend comes back with its
+ * profile's INTSYNC routing and output mutes, not with what the
+ * caches say, and the poll watches neither. Read them back here;
+ * a cache that cannot be read is left for the getter's read-through.
+ * Nothing races this: the poll is parked and the IRQ disabled.
+ */
+ mutex_lock(&sitdev->multiop_lock);
+ rc = sit9531x_intsync_src_detect(sitdev);
+ if (rc)
+ dev_warn(sitdev->dev,
+ "INTSYNC source not re-detected on resume: %d\n", rc);
+ for (i = 0; i < sitdev->info->num_outputs; i++) {
+ rc = sit9531x_output_state_refresh(sitdev, i);
+ if (rc) {
+ dev_warn(sitdev->dev,
+ "Output %u mute state not read on resume: %d\n",
+ i, rc);
+ sitdev->out[i].state_stale = true;
+ }
+ }
+ mutex_unlock(&sitdev->multiop_lock);
+
+ if (sitdev->irq > 0)
+ enable_irq(sitdev->irq);
+ kthread_queue_delayed_work(sitdev->kworker, &sitdev->work, 0);
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(sit9531x_pm_ops,
+ sit9531x_suspend, sit9531x_resume);
+
+static struct i2c_driver sit9531x_i2c_driver = {
+ .driver = {
+ .name = "sit9531x",
+ .of_match_table = sit9531x_of_match,
+ .pm = pm_sleep_ptr(&sit9531x_pm_ops),
+ },
+ .probe = sit9531x_i2c_probe,
+};
+module_i2c_driver(sit9531x_i2c_driver);
+
+MODULE_AUTHOR("Ali Rouhi <arouhi@sitime.com>");
+MODULE_AUTHOR("Oleg Zadorozhnyi <Oleg.Zadorozhnyi@devoxsoftware.com>");
+MODULE_DESCRIPTION("SiTime SiT9531x DPLL subsystem driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/dpll/sit9531x/core.h b/drivers/dpll/sit9531x/core.h
new file mode 100644
index 0000000000000..a5b343033a415
--- /dev/null
+++ b/drivers/dpll/sit9531x/core.h
@@ -0,0 +1,453 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * SiTime SiT9531x DPLL core driver
+ *
+ * Copyright (C) 2026 SiTime Corp.
+ * Author: Ali Rouhi <arouhi@sitime.com>
+ * Author: Oleg Zadorozhnyi <Oleg.Zadorozhnyi@devoxsoftware.com>
+ *
+ * Device structure, register access helpers, and core function
+ * declarations.
+ */
+
+#ifndef _SIT9531X_CORE_H
+#define _SIT9531X_CORE_H
+
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kthread.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#include "regs.h"
+
+#define SIT9531X_NUM_PLLS 4
+#define SIT9531X_MAX_INPUTS 8
+#define SIT9531X_NUM_INPUT_PAIRS (SIT9531X_MAX_INPUTS / 2)
+#define SIT9531X_MAX_OUTPUTS 12
+/* Output phase-adjust range advertised to the core, +/-1 ms in ps */
+#define SIT9531X_OUT_PHASE_ADJ_MAX_PS 1000000000
+/*
+ * INTSYNC (the inter-PLL sync net) is modeled as two pins. The
+ * destination PLL that locks to INTSYNC sees an input pin
+ * (SIT9531X_INTSYNC_PIN_ID, in the input id namespace after the physical
+ * inputs and the xtal); the source PLL that drives INTSYNC sees an output
+ * pin (SIT9531X_INTSYNC_OUT_PIN_ID, appended after the physical outputs).
+ */
+#define SIT9531X_INTSYNC_PIN_ID (SIT9531X_MAX_INPUTS + 1)
+#define SIT9531X_INTSYNC_OUT_PIN_ID SIT9531X_MAX_OUTPUTS
+#define SIT9531X_STATUS_POLL_MS 500
+
+/* routed_ref value when the PLL is fed no registered input */
+#define SIT9531X_REF_INVALID 0xFF
+
+/* SiTime IEEE OUI for EUI-64 generation */
+#define SIT9531X_OUI 0x0090C2FFFEULL
+
+struct sit9531x_dpll;
+
+/*
+ * struct sit9531x_chip_info - chip variant identification
+ * @id: variant ID byte read from register
+ * @num_inputs: number of input clock pins
+ * @num_outputs: number of output clock pins
+ * @name: human-readable variant name
+ * @clkout_map: per-output slot mapping (output index -> physical slot)
+ */
+struct sit9531x_chip_info {
+ u8 id;
+ u8 num_inputs;
+ u8 num_outputs;
+ const char *name;
+ const u8 *clkout_map;
+};
+
+/*
+ * enum sit9531x_signal_mode - input signal electrical mode
+ * @SIT9531X_MODE_SE: single-ended
+ * @SIT9531X_MODE_DE: differential
+ */
+enum sit9531x_signal_mode {
+ SIT9531X_MODE_SE = 0,
+ SIT9531X_MODE_DE,
+};
+
+/*
+ * struct sit9531x_ref - input reference state
+ * @freq: configured frequency in Hz
+ * @enabled: the lane's receiver is on
+ * @pll_mask: bitmask of PLLs this input feeds (bit 0 = PLLA)
+ * @los: the clock monitor reports loss of signal on the lane
+ * @qual_fail: the clock monitor reports a frequency drift on the
+ * lane while it still has signal
+ * @sig_mode: signal mode of the pair this lane belongs to
+ * (detected from CLKINx_INPUT_MODE at probe)
+ */
+struct sit9531x_ref {
+ u64 freq;
+ bool enabled;
+ u8 pll_mask;
+ bool los;
+ bool qual_fail;
+ enum sit9531x_signal_mode sig_mode;
+};
+
+/*
+ * struct sit9531x_out - output state
+ * @freq: rate the output is running at, in Hz
+ * @enabled: output is driving, i.e. not forced into Hi-Z
+ * @cmos: output is wired single-ended; the Hi-Z pairs that
+ * speak for it are the two CMOS pad ones, not the
+ * differential
+ * @state_stale: the cached mute state could not be confirmed against
+ * hardware and has to be read back before it is reported
+ * @routed: output is mapped to @pll_idx by the initial
+ * configuration; an unrouted output has no DPLL pin
+ * @pll_idx: PLL driving this output (0-3)
+ * @phase_stale: the programmed delay may differ from @phase_adj
+ * @flush_pending: the programmed delay is committed but the flush
+ * that applies it failed, so the output still realizes
+ * @phase_adj; the next request for that delay runs the
+ * flush although the registers already hold it
+ * @phase_armed: a non-zero delay is programmed, so a rate change
+ * has to re-time it; a profile delay beyond the
+ * advertised range is not, since what the cache holds
+ * for it is the end of the range, not the delay
+ * @phase_adj: phase adjust the delay registers actually realize,
+ * i.e. the last request quantized to whole VCO cycles
+ * plus 30 ps fine steps, in the request's sign
+ */
+struct sit9531x_out {
+ u64 freq;
+ bool enabled;
+ bool cmos;
+ bool state_stale;
+ bool routed;
+ u8 pll_idx;
+ s32 phase_adj;
+ bool phase_armed;
+ bool phase_stale;
+ bool flush_pending;
+};
+
+/*
+ * struct sit9531x_chan - per-PLL channel state
+ * @active: PLL has reached its active state; a PLL the loaded
+ * configuration leaves unused never does, and its
+ * loss-of-lock bit stays clear because nothing drives it
+ * @locked: PLL is active and its outer loop reports lock
+ * @mode: 0 = sync (outer loop enabled), 1 = free-run
+ * @routed_ref: logical input index of the reference the device
+ * feeds the PLL while it tracks one (the INTSYNC net
+ * maps to SIT9531X_INTSYNC_PIN_ID); SIT9531X_REF_INVALID
+ * when the PLL tracks nothing -- free-run, frozen,
+ * unlocked -- or the source code names no registered
+ * input
+ * @inner_lol: PLL inner loop loss-of-lock detected
+ * @ho_freeze: holdover freeze active
+ * @ho_valid: holdover memory acquired, i.e. the holdover window
+ * holds a valid estimate to fall back on
+ * @ho_release_pending: a table write forced holdover and could not release
+ * it; the poll retries the release until it lands
+ * @prio_srcs: cached copy of the priority table, one source code
+ * per slot; refreshed together with @prio_mask, it is
+ * what a rebuilt table is compared against
+ * @prio_last: first slot each source occupies, plus one (0 = the
+ * source is not in the table); refreshed from the same
+ * scan as @prio_mask, so the two never disagree
+ * @prio_mask: bit per canonical source present in this PLL's
+ * priority table, i.e. the sources it may select. Read
+ * back from the table by the periodic worker and
+ * refreshed by every table write, so it tracks the
+ * hardware rather than the driver's intent
+ * @cfg_prio: priority configured for each canonical source,
+ * whether or not it is in the table; the table is
+ * built from it, so taking a source out and putting it
+ * back does not change anyone's priority
+ * @cfg_known: bit per canonical source that has a @cfg_prio
+ * @seen_srcs: the table as the driver last wrote or seeded
+ * @cfg_prio from; a read-back that differs means
+ * something else rewrote it, and @cfg_prio is
+ * re-seeded from the hardware
+ * @seen_valid: @seen_srcs holds a table
+ */
+struct sit9531x_chan {
+ bool active;
+ bool locked;
+ u8 mode;
+ u8 routed_ref;
+ bool inner_lol;
+ bool ho_freeze;
+ bool ho_valid;
+ bool ho_release_pending;
+ u8 prio_srcs[SIT9531X_PRIO_MAX_SLOTS];
+ u8 prio_last[SIT9531X_PRIO_NUM_SRC];
+ u16 prio_mask;
+ u8 cfg_prio[SIT9531X_PRIO_NUM_SRC];
+ u16 cfg_known;
+ u8 seen_srcs[SIT9531X_PRIO_MAX_SLOTS];
+ bool seen_valid;
+};
+
+/*
+ * struct sit9531x_dev - SiT9531x device instance
+ * @info: detected chip variant info
+ * @dev: parent device
+ * @client: I2C client backing @regmap
+ * @regmap: paged register map of the device
+ * @dplls: DPLL devices registered for this chip
+ * @multiop_lock: mutex for multi-register atomic operations
+ * @ref: array of input reference states
+ * @out: array of output states
+ * @chan: array of per-PLL channel states
+ * @xtal_freq: crystal oscillator frequency in Hz
+ * @kworker: kthread worker for periodic polling
+ * @work: delayed work for periodic state checks
+ * @clock_id: IEEE 1588 EUI-64 clock identifier
+ * @reset_gpio: optional reset line (DT "reset-gpios"), NULL if absent
+ * @irq: optional INTRB IRQ number (from DT "interrupts" via the
+ * I2C client), 0 if no IRQ is wired
+ * @intsync_src: PLL index currently sourcing inter-PLL
+ * synchronization (INTSYNC), or -1 when disabled
+ * @intsync_partial: @intsync_src holds only part of the source
+ * configuration, left by an enable or a disable
+ * that stopped part way
+ * @irq_ack_fails: consecutive failures to acknowledge the
+ * notification latches from the interrupt handler
+ * @fvco_band_warned: bit per PLL whose Fref * DIVN has been reported as
+ * outside its VCO band; written under @multiop_lock
+ */
+struct sit9531x_dev {
+ struct device *dev;
+ struct i2c_client *client;
+ struct regmap *regmap;
+ const struct sit9531x_chip_info *info;
+ /* Serializes multi-step register sequences */
+ struct mutex multiop_lock;
+
+ /* Hardware state */
+ u8 irq_ack_fails;
+ u8 fvco_band_warned;
+ struct sit9531x_ref ref[SIT9531X_MAX_INPUTS + 1]; /* +1 for xtal */
+ struct sit9531x_out out[SIT9531X_MAX_OUTPUTS];
+ struct sit9531x_chan chan[SIT9531X_NUM_PLLS];
+ u32 xtal_freq;
+
+ /* DPLL channels */
+ struct list_head dplls;
+
+ /* Monitor */
+ struct kthread_worker *kworker;
+ struct kthread_delayed_work work;
+
+ /* Device identity */
+ u64 clock_id;
+
+ /* Optional DT-described GPIO / IRQ lines */
+ struct gpio_desc *reset_gpio;
+ int irq;
+
+ /* Inter-PLL synchronization state */
+ s8 intsync_src;
+ bool intsync_partial;
+
+};
+
+extern const struct regmap_config sit9531x_regmap_config;
+
+/* ---- Core lifecycle ---- */
+int sit9531x_dev_probe(struct sit9531x_dev *sitdev);
+int sit9531x_dev_start(struct sit9531x_dev *sitdev);
+void sit9531x_dev_stop(struct sit9531x_dev *sitdev);
+
+/* ---- Register access ---- */
+int sit9531x_read_u8(struct sit9531x_dev *sitdev, unsigned int reg,
+ u8 *val);
+int sit9531x_write_u8(struct sit9531x_dev *sitdev, unsigned int reg,
+ u8 val);
+int sit9531x_read_pll_u8(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u8 offset, u8 *val);
+int sit9531x_write_pll_u8(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u8 offset, u8 val);
+int sit9531x_update_pll_u8(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u8 offset, u8 mask, u8 val);
+
+/* ---- Input enable/disable ---- */
+int sit9531x_input_disable(struct sit9531x_dev *sitdev, u8 index);
+int sit9531x_input_enable(struct sit9531x_dev *sitdev, u8 index);
+
+/* ---- Input priority ---- */
+bool sit9531x_input_prio_present(struct sit9531x_dev *sitdev,
+ u8 pll_idx, u8 input_idx);
+int sit9531x_input_prio_set(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u8 input_idx, u8 prio);
+int sit9531x_input_prio_get(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u8 input_idx, u8 *prio);
+int sit9531x_input_prio_remove(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u8 input_idx);
+int sit9531x_input_prio_add(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u8 input_idx);
+
+/* ---- Output enable/disable (Hi-Z control) ---- */
+int sit9531x_output_disable(struct sit9531x_dev *sitdev, u8 index);
+int sit9531x_output_enable(struct sit9531x_dev *sitdev, u8 index);
+int sit9531x_output_state_refresh(struct sit9531x_dev *sitdev,
+ u8 out_idx);
+
+/* ---- Output frequency ---- */
+int sit9531x_output_freq_set(struct sit9531x_dev *sitdev, u8 out_idx,
+ u8 pll_idx, u64 frequency);
+int sit9531x_output_freq_get(struct sit9531x_dev *sitdev, u8 out_idx,
+ u64 *frequency);
+
+/* ---- Output phase adjust (PRG_RST_DELAY register-based) ---- */
+int sit9531x_output_phase_read(struct sit9531x_dev *sitdev, u8 out_idx,
+ s32 *phase_ps, bool *clamped);
+int sit9531x_output_phase_adjust_set(struct sit9531x_dev *sitdev,
+ u8 out_idx, s32 phase_ps);
+
+/* ---- Notification clear ---- */
+int sit9531x_clear_notifications(struct sit9531x_dev *sitdev);
+
+/* ---- INTSYNC (inter-PLL synchronization) ---- */
+int sit9531x_intsync_enable(struct sit9531x_dev *sitdev, u8 src_pll_idx);
+int sit9531x_intsync_disable(struct sit9531x_dev *sitdev, u8 src_pll_idx);
+int sit9531x_intsync_src_detect(struct sit9531x_dev *sitdev);
+
+/* ---- Phase offset (TDC readback) ---- */
+int sit9531x_chan_routed_ref_read(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u8 *ref);
+int sit9531x_input_mon_fetch(struct sit9531x_dev *sitdev);
+int sit9531x_phase_offset_read(struct sit9531x_dev *sitdev, u8 pll_idx,
+ s64 *phase_offset);
+
+/* ---- State helpers ---- */
+
+/*
+ * sit9531x_pll_page - get register page for PLL index
+ * @pll_idx: PLL index (0 = PLLA, 3 = PLLD)
+ */
+static inline u8 sit9531x_pll_page(u8 pll_idx)
+{
+ return SIT9531X_PAGE_PLLA + pll_idx;
+}
+
+/*
+ * Logical input pins are interleaved: even index = P lane, odd
+ * index = N lane of pair index/2 (IN0P, IN0N, IN1P, IN1N, ...).
+ * Index SIT9531X_MAX_INPUTS is the XO input.
+ */
+
+/*
+ * sit9531x_input_pair - get input pair number for a logical input index
+ * @index: logical input pin index
+ */
+static inline u8 sit9531x_input_pair(u8 index)
+{
+ return index >> 1;
+}
+
+/*
+ * sit9531x_input_is_n - check if a logical input index is an N lane
+ * @index: logical input pin index
+ */
+static inline bool sit9531x_input_is_n(u8 index)
+{
+ return index & 1;
+}
+
+/*
+ * sit9531x_input_hw_src - translate logical input index to source encoding
+ * @index: logical input pin index
+ *
+ * The priority table and CLK_ACTIVESEL registers use a non-contiguous
+ * source encoding: 0-3 = CLK0P..CLK3P, 5 = OCXO, 6 = INTSYNC,
+ * 7-10 = CLK0N..CLK3N.
+ */
+static inline u8 sit9531x_input_hw_src(u8 index)
+{
+ if (index == SIT9531X_MAX_INPUTS)
+ return SIT9531X_PRIO_SRC_OCXO;
+ if (index == SIT9531X_INTSYNC_PIN_ID)
+ return SIT9531X_PRIO_SRC_INTSYNC;
+ if (sit9531x_input_is_n(index))
+ return SIT9531X_PRIO_SRC_N_BASE + sit9531x_input_pair(index);
+ return sit9531x_input_pair(index);
+}
+
+/*
+ * sit9531x_hw_src_input - translate source encoding to logical input index
+ * @src: 4-bit hardware source encoding
+ *
+ * Return: logical input index (INTSYNC maps to SIT9531X_INTSYNC_PIN_ID),
+ * or SIT9531X_REF_INVALID if @src is a reserved value
+ */
+static inline u8 sit9531x_hw_src_input(u8 src)
+{
+ if (src < SIT9531X_NUM_INPUT_PAIRS)
+ return src * 2;
+ if (src == SIT9531X_PRIO_SRC_OCXO)
+ return SIT9531X_MAX_INPUTS;
+ if (src == SIT9531X_PRIO_SRC_INTSYNC)
+ return SIT9531X_INTSYNC_PIN_ID;
+ if (src >= SIT9531X_PRIO_SRC_N_BASE &&
+ src < SIT9531X_PRIO_SRC_N_BASE + SIT9531X_NUM_INPUT_PAIRS)
+ return (src - SIT9531X_PRIO_SRC_N_BASE) * 2 + 1;
+ return SIT9531X_REF_INVALID;
+}
+
+/*
+ * sit9531x_prio_src_usable - does a priority slot name a reference?
+ * @src: 4-bit hardware source encoding held in a table slot
+ *
+ * Codes 4 and 11 address a fifth input pair this part does not have, and
+ * a value outside the encoding is not a source either, so a slot holding
+ * one of them names nothing the device can select. Canonicalizing an N
+ * lane to its pair first would not change the answer -- both lanes are
+ * references -- so this needs no device state.
+ *
+ * Return: true when the code resolves to a reference the device can use
+ */
+static inline bool sit9531x_prio_src_usable(u8 src)
+{
+ return sit9531x_hw_src_input(src) != SIT9531X_REF_INVALID;
+}
+
+/*
+ * sit9531x_ref_state_get - get reference state by index
+ * @index: logical input index
+ *
+ * Return: pointer to the cached input reference state
+ */
+static inline const struct sit9531x_ref *
+sit9531x_ref_state_get(const struct sit9531x_dev *sitdev, u8 index)
+{
+ return &sitdev->ref[index];
+}
+
+/*
+ * sit9531x_out_state_get - get output state by index
+ * @index: logical output index
+ *
+ * Return: pointer to the cached output state
+ */
+static inline const struct sit9531x_out *
+sit9531x_out_state_get(const struct sit9531x_dev *sitdev, u8 index)
+{
+ return &sitdev->out[index];
+}
+
+/*
+ * sit9531x_chan_state_get - get channel state by PLL index
+ *
+ * Return: pointer to the cached per-PLL channel state
+ */
+static inline const struct sit9531x_chan *
+sit9531x_chan_state_get(const struct sit9531x_dev *sitdev, u8 pll_idx)
+{
+ return &sitdev->chan[pll_idx];
+}
+
+#endif /* _SIT9531X_CORE_H */
diff --git a/drivers/dpll/sit9531x/dpll.c b/drivers/dpll/sit9531x/dpll.c
new file mode 100644
index 0000000000000..4c86ee9e79804
--- /dev/null
+++ b/drivers/dpll/sit9531x/dpll.c
@@ -0,0 +1,1731 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SiTime SiT9531x DPLL subsystem callbacks and registration
+ *
+ * Copyright (C) 2026 SiTime Corp.
+ * Author: Ali Rouhi <arouhi@sitime.com>
+ * Author: Oleg Zadorozhnyi <Oleg.Zadorozhnyi@devoxsoftware.com>
+ *
+ * DPLL device ops, pin ops (separate input/output), pin registration,
+ * and periodic change detection.
+ */
+
+#include <linux/dpll.h>
+#include <linux/err.h>
+#include <linux/kthread.h>
+#include <linux/list.h>
+#include <linux/netlink.h>
+#include <linux/slab.h>
+
+#include "core.h"
+#include "dpll.h"
+#include "prop.h"
+#include "regs.h"
+
+static bool sit9531x_dpll_is_input_pin(const struct sit9531x_dpll_pin *pin)
+{
+ return pin->dir == DPLL_PIN_DIRECTION_INPUT;
+}
+
+static bool
+sit9531x_dpll_is_intsync_pin(const struct sit9531x_dpll_pin *pin)
+{
+ return sit9531x_dpll_is_input_pin(pin) &&
+ pin->id == SIT9531X_INTSYNC_PIN_ID;
+}
+
+static bool
+sit9531x_dpll_is_intsync_src_pin(const struct sit9531x_dpll_pin *pin)
+{
+ return !sit9531x_dpll_is_input_pin(pin) &&
+ pin->id == SIT9531X_INTSYNC_OUT_PIN_ID;
+}
+
+static bool
+sit9531x_dpll_is_xo_pin(const struct sit9531x_dpll_pin *pin)
+{
+ return sit9531x_dpll_is_input_pin(pin) &&
+ pin->id == SIT9531X_MAX_INPUTS;
+}
+
+/*
+ * The pins the poll watches: the selection-role pins -- regular references
+ * and the INTSYNC destination -- whose state, operational state and
+ * priority can move without a request: the device selects on its own, the
+ * monitors follow the signal, and a table rewritten behind the driver
+ * re-seeds the priorities. Outputs (incl. the INTSYNC source) change only
+ * through their own set callback and the XO is permanently connected.
+ */
+static bool
+sit9531x_dpll_is_selection_pin(const struct sit9531x_dpll_pin *pin)
+{
+ return sit9531x_dpll_is_input_pin(pin) &&
+ !sit9531x_dpll_is_xo_pin(pin);
+}
+
+/*
+ * Status of a PLL whose outer loop is frozen in holdover.
+ *
+ * The interface has a PLL report holdover for a hold forced by
+ * disconnecting every input only when it was LOCKED_HO_ACQ before, that
+ * is, when it has a valid holdover estimate to run on; without one it
+ * stays UNLOCKED. Removing the last source from a PLL's table forces
+ * exactly that hold, so a frozen PLL that lists no source is in holdover
+ * only while its memory is valid. A PLL frozen with sources still listed
+ * lost its lock, and is in holdover on the device's say-so.
+ */
+static enum dpll_lock_status
+sit9531x_dpll_frozen_status(const struct sit9531x_chan *chan)
+{
+ if (!chan->prio_mask && !chan->ho_valid)
+ return DPLL_LOCK_STATUS_UNLOCKED;
+
+ return DPLL_LOCK_STATUS_HOLDOVER;
+}
+
+/*
+ * The cached state this reports comes from the outer loss-of-lock byte
+ * (page 0, reg 0x06), the PLL mode bit (PLL page, reg 0x31), inner LOL
+ * (reg 0x92), the holdover freeze byte (reg 0x0A), the per-PLL
+ * holdover-valid bit (PLL page, reg 0x06) and the priority table.
+ */
+static int
+sit9531x_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv,
+ enum dpll_lock_status *status,
+ enum dpll_lock_status_error *status_error,
+ struct netlink_ext_ack *extack)
+{
+ struct sit9531x_dpll *sitdpll = dpll_priv;
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+ const struct sit9531x_chan *chan;
+
+ if (status_error)
+ *status_error = DPLL_LOCK_STATUS_ERROR_NONE;
+
+ chan = sit9531x_chan_state_get(sitdev, sitdpll->id);
+
+ mutex_lock(&sitdev->multiop_lock);
+
+ if (!chan->active) {
+ /*
+ * A PLL the loaded configuration leaves unused never reaches
+ * its active state. Nothing drives its loss-of-lock bit, so
+ * without this it would report a lock it does not have.
+ */
+ *status = DPLL_LOCK_STATUS_UNLOCKED;
+ } else if (chan->inner_lol) {
+ /*
+ * The core publishes the error detail only for the unlocked
+ * and holdover states, so an inner loss of lock reported
+ * under a locked status would never reach userspace. An
+ * inner loop that is not locked is not a locked PLL.
+ */
+ *status = chan->ho_freeze ? sit9531x_dpll_frozen_status(chan) :
+ DPLL_LOCK_STATUS_UNLOCKED;
+ } else if (chan->mode) {
+ /*
+ * Free-run: the outer loop is disabled, so the PLL tracks no
+ * reference at all and its loss-of-lock bit means nothing.
+ * That is what UNLOCKED describes -- "not yet locked to any
+ * valid input (or was forced by user)".
+ */
+ *status = DPLL_LOCK_STATUS_UNLOCKED;
+ } else if (chan->ho_freeze) {
+ /*
+ * Tested before the lock: the freeze and the outer
+ * loss-of-lock bit are read independently, and a PLL frozen
+ * in holdover is not tracking a reference whatever its
+ * loss-of-lock bit says -- the pin-state contract below
+ * counts a frozen PLL as tracking nothing for the same
+ * reason.
+ */
+ *status = sit9531x_dpll_frozen_status(chan);
+ } else if (chan->locked) {
+ /*
+ * HO_ACQ is locked *and* holdover memory acquired, so it needs
+ * the holdover-valid bit rather than following from the lock.
+ */
+ if (chan->ho_valid)
+ *status = DPLL_LOCK_STATUS_LOCKED_HO_ACQ;
+ else
+ *status = DPLL_LOCK_STATUS_LOCKED;
+ } else {
+ *status = DPLL_LOCK_STATUS_UNLOCKED;
+ }
+
+ /* Report inner LOL as an error condition */
+ if (status_error && chan->inner_lol)
+ *status_error = DPLL_LOCK_STATUS_ERROR_UNDEFINED;
+
+ mutex_unlock(&sitdev->multiop_lock);
+
+ return 0;
+}
+
+/*
+ * sit9531x_dpll_lock_status_baseline - seed the status the poll compares to
+ *
+ * Called once the device is registered. The create notification carried
+ * the lock status the cache holds, so that is the baseline the first tick
+ * compares against; taken from the placeholder the allocation left, every
+ * PLL that is locked or in holdover would look like it had just changed.
+ */
+void sit9531x_dpll_lock_status_baseline(struct sit9531x_dpll *sitdpll)
+{
+ sit9531x_dpll_lock_status_get(sitdpll->dpll_dev, sitdpll,
+ &sitdpll->lock_status,
+ &sitdpll->lock_status_error, NULL);
+}
+
+/*
+ * Mode
+ * ====
+ * enum dpll_mode differentiates how a DPLL selects an input: AUTOMATIC
+ * has the device pick the highest-priority one, MANUAL has userspace
+ * request one. The device only implements the former through this
+ * driver, so AUTOMATIC is the only mode advertised.
+ *
+ * With a single mode there is nothing to switch, so no .mode_set: the core
+ * answers a mode request with -EOPNOTSUPP. Free-run -- the outer loop
+ * disabled through PLL page reg 0x31[5] -- is not a mode in those terms,
+ * because no input is selected either way. It is reported through lock
+ * status instead, and entered and left through the chip-specific tool
+ * rather than over netlink.
+ *
+ * The device could implement real MANUAL: PLL_CONFIG1F_PLL (PLL page reg
+ * 0x1F) bit 6 switches a PLL from priority-based to manual active select,
+ * and with MISCINNER_PLL (reg 0x18) bit 5 the PLL follows a manual input
+ * select -- the input-select pins, or with GPIO_INPUT_FUNC_CTRL5..8
+ * (page 0, regs 0xE8-0xEB) bit 4 the register's own low nibble -- which
+ * pins one reference while the loop keeps running. Wiring that up would
+ * let .state_on_dpll_set() accept CONNECTED, but regs 0x18 and 0x1F carry
+ * GUI-generated configuration in their other bits, so it is left out.
+ * Advertising MANUAL and then refusing the one request MANUAL exists for
+ * is the worse of the two incomplete answers, and after merge it would be
+ * ABI. A profile that sets manual select anyway is reported at probe,
+ * since the mode this driver reports would then be wrong.
+ */
+static int
+sit9531x_dpll_mode_get(const struct dpll_device *dpll, void *dpll_priv,
+ enum dpll_mode *mode, struct netlink_ext_ack *extack)
+{
+ *mode = DPLL_MODE_AUTOMATIC;
+
+ return 0;
+}
+
+static int
+sit9531x_dpll_supported_modes_get(const struct dpll_device *dpll,
+ void *dpll_priv, unsigned long *modes,
+ struct netlink_ext_ack *extack)
+{
+ __set_bit(DPLL_MODE_AUTOMATIC, modes);
+
+ return 0;
+}
+
+const struct dpll_device_ops sit9531x_dpll_device_ops = {
+ .lock_status_get = sit9531x_dpll_lock_status_get,
+ .mode_get = sit9531x_dpll_mode_get,
+ .supported_modes_get = sit9531x_dpll_supported_modes_get,
+ /* temp_get not available -- SiT9531x has no on-die temp sensor */
+};
+
+/*
+ * Pin-state contract
+ * ==================
+ * The five pin ops tables below fall into three roles, and only the first
+ * has a selection state machine. Each state_on_dpll callback implements
+ * the rules for its role and nothing else, so the tables cannot drift
+ * apart the way five independent encodings of this did.
+ *
+ * SELECTION role -- physical input pins, INTSYNC destination pin.
+ * The state is what userspace asked for; what the device is doing with
+ * the pin is the operational state. Predicates, all evaluated under
+ * multiop_lock:
+ * M source is present in THIS PLL's hardware priority table
+ * S chan->routed_ref == this pin's id (the reference the device
+ * reports feeding the PLL)
+ * L chan->locked && !chan->mode && !chan->ho_freeze
+ * (tracking a reference: outer loop running, locked, not frozen)
+ * N the input lane's clock monitor reports loss of signal
+ * Q the lane's monitor reports a frequency drift, with signal
+ * state get:
+ * SELECTABLE M
+ * DISCONNECTED !M
+ * operstate get:
+ * ACTIVE S && L && !N
+ * NO_SIGNAL N
+ * QUAL_FAILED !N && Q && !(S && L)
+ * STANDBY otherwise
+ * set:
+ * DISCONNECTED remove from this PLL's table; a physical input also
+ * releases this DPLL's claim and powers the shared
+ * receiver down on the last release
+ * SELECTABLE add to this PLL's table; a physical input powers the
+ * receiver up and takes the claim, in that order
+ * CONNECTED -EOPNOTSUPP -- the device selects by priority and has
+ * no mode that pins one reference (see "Mode" above)
+ * other -EINVAL
+ *
+ * S is not the active selection the driver writes to the table: when
+ * the selected source loses its signal the device falls back to another
+ * listed source on its own and leaves the selection naming the dead one,
+ * so the reference the device reports feeding the PLL is read instead,
+ * and after such a fallback the pin the PLL moved to is the active one.
+ * The ACTIVE test needs L as well as S because being fed to the PLL is
+ * not proof the loop uses it: a free-running, frozen or unlocked PLL
+ * follows nothing. It needs !N because the device names what it feeds
+ * the PLL, not what the PLL is locked to, and a lane without signal is
+ * not that whatever the lock bit still says. The INTSYNC destination
+ * has no monitor, so N and Q never hold for it.
+ *
+ * M is read from the hardware priority table, not from ref->pll_mask,
+ * which is only the shared-receiver refcount and says nothing about one
+ * DPLL's eligibility. Priority is kept by the driver per source and
+ * PLL, independent of M, so a pin reports the same priority whether it
+ * is connected or not.
+ *
+ * DRIVE role -- output pins, INTSYNC source pin.
+ * Is this pin or net being driven? Nothing is selected here, so:
+ * CONNECTED pin or net is driven
+ * DISCONNECTED pin is muted (Hi-Z), or this PLL does not drive it
+ * SELECTABLE -EINVAL on set, never reported by get
+ *
+ * FIXED role -- XO pin. Always CONNECTED; it cannot be routed.
+ */
+
+/*
+ * Report a selection-role pin's state on this DPLL. @pin_id is a logical
+ * input index, SIT9531X_INTSYNC_PIN_ID for the INTSYNC destination.
+ *
+ * Membership comes from chan->prio_mask, which is the priority table read
+ * back from the chip -- not a record of what the driver asked for. The
+ * getter runs on every poll for every input pin of every DPLL, so it takes
+ * the mask the worker refreshed rather than rescanning the table over I2C
+ * each time; table writes refresh it too, so a get right after a set does
+ * not report the old membership.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+static void
+sit9531x_dpll_selection_state_get(struct sit9531x_dev *sitdev,
+ const struct sit9531x_dpll *sitdpll,
+ u8 pin_id, enum dpll_pin_state *state)
+{
+ const struct sit9531x_chan *chan;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ chan = sit9531x_chan_state_get(sitdev, sitdpll->id);
+
+ if (chan->prio_mask & BIT(sit9531x_input_hw_src(pin_id)))
+ *state = DPLL_PIN_STATE_SELECTABLE;
+ else
+ *state = DPLL_PIN_STATE_DISCONNECTED;
+}
+
+/*
+ * Does the device feed a locked PLL from the INTSYNC net?
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+static bool sit9531x_dpll_intsync_tracked(struct sit9531x_dev *sitdev)
+{
+ const struct sit9531x_chan *chan;
+ u8 pll;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ for (pll = 0; pll < SIT9531X_NUM_PLLS; pll++) {
+ chan = sit9531x_chan_state_get(sitdev, pll);
+ if (chan->routed_ref == SIT9531X_INTSYNC_PIN_ID &&
+ chan->locked && !chan->ho_freeze)
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Does a selection-role pin carry a signal? A physical input has the
+ * device's LOS monitor; the INTSYNC net has no monitor, and what stands in
+ * for one is the driver's record of a PLL driving it. A partial source
+ * configuration does not count: the source pin reports it as no
+ * connection, and the net is not confirmed driven.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+static bool
+sit9531x_dpll_selection_no_signal(struct sit9531x_dev *sitdev, u8 pin_id)
+{
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ if (pin_id == SIT9531X_INTSYNC_PIN_ID) {
+ /*
+ * The driver knows the net is driven when it enabled the
+ * source itself or found the configuration it would have
+ * written. A profile can drive the net with a configuration
+ * of its own; then a PLL the device feeds from the net and
+ * holds locked is the evidence that the net carries a signal.
+ */
+ if (sitdev->intsync_src >= 0 && !sitdev->intsync_partial)
+ return false;
+ return !sit9531x_dpll_intsync_tracked(sitdev);
+ }
+
+ return pin_id < sitdev->info->num_inputs &&
+ sit9531x_ref_state_get(sitdev, pin_id)->los;
+}
+
+/*
+ * Is this the reference the PLL is tracking now? See the S && L && !N
+ * predicate in the pin-state contract. This is also what gates the
+ * measurements taken against the active reference.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+static bool
+sit9531x_dpll_selection_active(struct sit9531x_dev *sitdev,
+ const struct sit9531x_dpll *sitdpll, u8 pin_id)
+{
+ const struct sit9531x_chan *chan;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ chan = sit9531x_chan_state_get(sitdev, sitdpll->id);
+
+ if (chan->routed_ref != pin_id || !chan->locked || chan->mode ||
+ chan->ho_freeze)
+ return false;
+
+ /*
+ * Fed to the PLL is not locked to: the device names the source it
+ * routes, and a source that lost its signal is one the PLL is about
+ * to be moved off, whatever the lock bit still says.
+ */
+ if (sit9531x_dpll_selection_no_signal(sitdev, pin_id))
+ return false;
+
+ return true;
+}
+
+/*
+ * Report a selection-role pin's operational state on this DPLL.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+static void
+sit9531x_dpll_selection_operstate_get(struct sit9531x_dev *sitdev,
+ const struct sit9531x_dpll *sitdpll,
+ u8 pin_id,
+ enum dpll_pin_operstate *operstate)
+{
+ const struct sit9531x_ref *ref;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ if (sit9531x_dpll_selection_active(sitdev, sitdpll, pin_id)) {
+ *operstate = DPLL_PIN_OPERSTATE_ACTIVE;
+ return;
+ }
+
+ /*
+ * Standby is a qualified source the PLL is not using. A net nobody
+ * drives is no more that than a lane in LOS is.
+ */
+ if (sit9531x_dpll_selection_no_signal(sitdev, pin_id)) {
+ *operstate = DPLL_PIN_OPERSTATE_NO_SIGNAL;
+ return;
+ }
+
+ if (pin_id < sitdev->info->num_inputs) {
+ ref = sit9531x_ref_state_get(sitdev, pin_id);
+ if (ref->qual_fail) {
+ *operstate = DPLL_PIN_OPERSTATE_QUAL_FAILED;
+ return;
+ }
+ }
+
+ *operstate = DPLL_PIN_OPERSTATE_STANDBY;
+}
+
+static int
+sit9531x_dpll_input_pin_operstate_on_dpll_get(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_pin_operstate *state,
+ struct netlink_ext_ack *extack)
+{
+ struct sit9531x_dpll_pin *dpin = pin_priv;
+ struct sit9531x_dpll *sitdpll = dpll_priv;
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+
+ mutex_lock(&sitdev->multiop_lock);
+ sit9531x_dpll_selection_operstate_get(sitdev, sitdpll, dpin->id,
+ state);
+ mutex_unlock(&sitdev->multiop_lock);
+
+ return 0;
+}
+
+static int
+sit9531x_dpll_input_pin_direction_get(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_pin_direction *direction,
+ struct netlink_ext_ack *extack)
+{
+ *direction = DPLL_PIN_DIRECTION_INPUT;
+ return 0;
+}
+
+/*
+ * sit9531x_dpll_input_pin_frequency_get - read input pin frequency
+ *
+ * Returns the rate the board wired to the input, the first entry of its
+ * supported-frequencies-hz; an input has no frequency setter. An input
+ * the firmware lists no rate for is registered without this callback
+ * (sit9531x_dpll_pin_ops_get()), so its attribute is absent rather than
+ * zero.
+ */
+static int
+sit9531x_dpll_input_pin_frequency_get(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv, u64 *frequency,
+ struct netlink_ext_ack *extack)
+{
+ struct sit9531x_dpll_pin *dpin = pin_priv;
+ struct sit9531x_dpll *sitdpll = dpll_priv;
+ const struct sit9531x_ref *ref;
+
+ ref = sit9531x_ref_state_get(sitdpll->dev, dpin->id);
+ *frequency = ref->freq;
+
+ return 0;
+}
+
+/*
+ * sit9531x_dpll_input_pin_state_on_dpll_get - get input pin DPLL state
+ *
+ * Selection role; see the pin-state contract above.
+ */
+static int
+sit9531x_dpll_input_pin_state_on_dpll_get(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_pin_state *state,
+ struct netlink_ext_ack *extack)
+{
+ struct sit9531x_dpll_pin *dpin = pin_priv;
+ struct sit9531x_dpll *sitdpll = dpll_priv;
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+
+ mutex_lock(&sitdev->multiop_lock);
+ sit9531x_dpll_selection_state_get(sitdev, sitdpll, dpin->id, state);
+ mutex_unlock(&sitdev->multiop_lock);
+
+ return 0;
+}
+
+/*
+ * sit9531x_dpll_input_pin_state_on_dpll_set - set input pin DPLL state
+ *
+ * Enables or disables the physical input receiver via Page 0x02
+ * force/state registers (sit9531x_input_disable/enable()) and updates
+ * this DPLL's Page 1 priority table so the state is honoured by the
+ * PLL's automatic reference selection, not just at the input buffer.
+ * Selection role; see the pin-state contract above for the states.
+ *
+ * The priority table is per PLL, so it is always updated for this DPLL.
+ * A single physical input feeds every DPLL, so the hardware receiver is
+ * only cut off once the last DPLL has released it: ref->pll_mask tracks
+ * which DPLLs currently claim the input, and the physical disable
+ * happens on the transition to an empty mask.
+ */
+static int
+sit9531x_dpll_input_pin_state_on_dpll_set(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_pin_state state,
+ struct netlink_ext_ack *extack)
+{
+ struct sit9531x_dpll_pin *dpin = pin_priv;
+ struct sit9531x_dpll *sitdpll = dpll_priv;
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+ struct sit9531x_ref *ref = &sitdev->ref[dpin->id];
+ u8 hw_src = sit9531x_input_hw_src(dpin->id);
+ u8 pll_bit = BIT(sitdpll->id);
+ bool enabled_here = false;
+ int rc;
+
+ mutex_lock(&sitdev->multiop_lock);
+
+ switch (state) {
+ case DPLL_PIN_STATE_DISCONNECTED:
+ rc = sit9531x_input_prio_remove(sitdev, sitdpll->id, hw_src);
+ /*
+ * The table write, the latch and the holdover release are
+ * three steps behind one return code, so ask the table what
+ * actually happened rather than reading the errno as "no
+ * change". A source that is gone from the table has been
+ * released whatever else failed.
+ */
+ if (rc && sit9531x_input_prio_present(sitdev, sitdpll->id,
+ hw_src))
+ break;
+ ref->pll_mask &= ~pll_bit;
+ /*
+ * The receiver is shared, so the last DPLL to let go turns it
+ * off. That has to happen even when the table rewrite
+ * reported an error, or the input stays powered with nothing
+ * tracking it; the first error is the one returned.
+ */
+ if (!ref->pll_mask) {
+ int off_rc = sit9531x_input_disable(sitdev, dpin->id);
+
+ if (off_rc && !rc)
+ rc = off_rc;
+ }
+ break;
+ case DPLL_PIN_STATE_CONNECTED:
+ /*
+ * CONNECTED asks for this input and no other, which the
+ * device cannot be told to do: it selects by priority and the
+ * manual-active-select path is not wired up (see "Mode").
+ * Refuse instead of quietly behaving like SELECTABLE.
+ */
+ NL_SET_ERR_MSG(extack,
+ "Device selects its reference by priority; use selectable");
+ rc = -EOPNOTSUPP;
+ break;
+ case DPLL_PIN_STATE_SELECTABLE:
+ /*
+ * Gate the receiver on whenever it is off, not only when this
+ * DPLL holds no claim yet. The two are tracked separately --
+ * the claim comes from the priority table, the receiver from
+ * the force bits -- so a PLL that already lists the input can
+ * still find it powered down, and skipping the enable would
+ * report success for a reference that cannot reach the loop.
+ */
+ if (!ref->enabled) {
+ rc = sit9531x_input_enable(sitdev, dpin->id);
+ if (rc)
+ break;
+ enabled_here = true;
+ }
+ rc = sit9531x_input_prio_add(sitdev, sitdpll->id, hw_src);
+ if (rc && !sit9531x_input_prio_present(sitdev, sitdpll->id,
+ hw_src)) {
+ /*
+ * Undo only what this request did. A receiver the
+ * loaded configuration had already turned on is not
+ * this request's to turn off.
+ */
+ if (enabled_here)
+ sit9531x_input_disable(sitdev, dpin->id);
+ break;
+ }
+ /*
+ * Claim the input for this DPLL only once it is both enabled
+ * and present in the priority table. Setting the mask before
+ * prio_add would leak the claim if prio_add failed, keeping the
+ * shared input receiver powered even after every DPLL released
+ * it.
+ */
+ ref->pll_mask |= pll_bit;
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&sitdev->multiop_lock);
+
+ /*
+ * Leave the messages the switch already set in place; only a failure
+ * that came from the hardware path still needs one.
+ */
+ if (rc == -ENOSPC)
+ NL_SET_ERR_MSG(extack,
+ "Priority table is full of unique sources on this PLL");
+ else if (rc && rc != -EOPNOTSUPP && rc != -EINVAL)
+ NL_SET_ERR_MSG(extack, "Failed to set input pin state");
+
+ return rc;
+}
+
+/*
+ * sit9531x_dpll_input_pin_prio_get - read input pin priority
+ *
+ * Reports the priority sit9531x_input_prio_get() keeps for the source on
+ * this PLL, connected or not; no register is read.
+ */
+static int
+sit9531x_dpll_input_pin_prio_get(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv, u32 *prio,
+ struct netlink_ext_ack *extack)
+{
+ struct sit9531x_dpll_pin *dpin = pin_priv;
+ struct sit9531x_dpll *sitdpll = dpll_priv;
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+ u8 slot;
+ int rc;
+
+ mutex_lock(&sitdev->multiop_lock);
+ rc = sit9531x_input_prio_get(sitdev, sitdpll->id,
+ sit9531x_input_hw_src(dpin->id), &slot);
+ mutex_unlock(&sitdev->multiop_lock);
+ if (rc)
+ return rc;
+
+ /*
+ * dpin->prio is not touched here: it is the poll's baseline for
+ * spotting a change to notify, and a get refreshing it would hide
+ * the change from the poll.
+ */
+ *prio = slot;
+ return 0;
+}
+
+/*
+ * sit9531x_dpll_input_pin_prio_set - set input pin priority
+ *
+ * Records the priority and, for a pin in this PLL's table, rewrites the
+ * Page 1 table in priority order (sit9531x_input_prio_set()). The other
+ * pins keep their priorities, so only the named pin changes and the core
+ * notifies it. A pin that is not in the table keeps the priority for when
+ * it is connected.
+ */
+static int
+sit9531x_dpll_input_pin_prio_set(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv, u32 prio,
+ struct netlink_ext_ack *extack)
+{
+ struct sit9531x_dpll_pin *dpin = pin_priv;
+ struct sit9531x_dpll *sitdpll = dpll_priv;
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+ int rc;
+
+ if (dpin->dir != DPLL_PIN_DIRECTION_INPUT) {
+ NL_SET_ERR_MSG(extack, "Priority applies only to input pins");
+ return -EINVAL;
+ }
+
+ if (prio > U8_MAX) {
+ NL_SET_ERR_MSG(extack, "Priority out of range (0-255)");
+ return -EINVAL;
+ }
+
+ mutex_lock(&sitdev->multiop_lock);
+ rc = sit9531x_input_prio_set(sitdev, sitdpll->id,
+ sit9531x_input_hw_src(dpin->id),
+ (u8)prio);
+ if (!rc)
+ dpin->prio = prio;
+ mutex_unlock(&sitdev->multiop_lock);
+
+ if (rc) {
+ NL_SET_ERR_MSG(extack, "Failed to set input priority");
+ return rc;
+ }
+
+ return 0;
+}
+
+/*
+ * sit9531x_dpll_input_pin_phase_offset_get - phase offset of a reference
+ *
+ * What this reports, and what it deliberately does not:
+ *
+ * The ABI defines the attribute as the phase difference between the signal
+ * on a pin and its parent DPLL device, so this is the loop's own residual
+ * error, sampled with the loop closed. On a locked DPLL it therefore
+ * trends small -- that is the measurement, not an artefact of it. The
+ * documentation describes the reported value as one that may be averaged
+ * over prior measurements, which suits a closed-loop residual and not a
+ * one-shot open-loop capture; the core publishes whatever this callback
+ * returns, so the averaging, if any, would be this driver's to do.
+ *
+ * The chip can also measure the reference against the local oscillator
+ * with the outer loop's correction frozen, which is a different quantity
+ * and the one the documented phase-difference procedure produces. That
+ * needs the digital loop filter held (and, on the 1PPS PLL, the automatic
+ * phase- and frequency-lock helpers held off), which leaves the PLL
+ * undisciplined until it is released. A netlink read must not do that,
+ * so that measurement is not offered here at all; it belongs to a caller
+ * that can own the freeze and restore it.
+ *
+ * Precondition, which this callback cannot create: the TDC compares
+ * against a signal the PLL drives, so a PLL driving no output with its
+ * zero-delay buffer off has nothing to measure. SiTime clock
+ * engineering confirms this is a property of the hardware, not of
+ * SiTime's TDC measurement procedure, which satisfies it by mapping a
+ * spare output and restarting the PLL -- side effects that do not belong
+ * in a getter, so a reading taken in that state is simply not meaningful.
+ *
+ * Non-selected pins and a PLL with no programmed divider report zero
+ * rather than an error: the DPLL core propagates any error from this
+ * callback and fails the whole pin dump with it. The core has no per-pin
+ * "no data" for phase offset, as it has -ENODATA for the fractional
+ * frequency offset, so it is a value or no callback at all.
+ */
+static int
+sit9531x_dpll_input_pin_phase_offset_get(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv, s64 *phase_offset,
+ struct netlink_ext_ack *extack)
+{
+ struct sit9531x_dpll_pin *dpin = pin_priv;
+ struct sit9531x_dpll *sitdpll = dpll_priv;
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+ bool drives = false;
+ s64 offset;
+ u8 routed, i;
+ int rc;
+
+ mutex_lock(&sitdev->multiop_lock);
+
+ /*
+ * The on-chip TDC is a per-PLL resource that always measures the
+ * phase difference between the VCO and the PLL's currently
+ * selected reference; it cannot be pointed at an arbitrary input,
+ * so an input that is not the active reference reports 0 rather
+ * than the active reference's value.
+ *
+ * The sample needs both: the PLL tracking this pin as the poll last
+ * saw it (locked, outer loop running, not frozen), and the device
+ * still feeding it this pin, read now -- the device moves on its
+ * own, and a cache up to a poll period old could attribute a live
+ * measurement to the pin the PLL used to be fed. The device is read
+ * again after the sample for the same reason: it can switch during
+ * the dozen transfers the sample takes.
+ *
+ * The TDC also compares against a signal the PLL drives, so a PLL
+ * with no routed, driving output has nothing to measure and reports
+ * 0 like any other pin without a reading. Output state is never
+ * polled, so a mute or unmute whose read-back failed is read
+ * through here first, as the output's own getter does, rather than
+ * taken from a cache that was left describing the state before it.
+ */
+ for (i = 0; i < sitdev->info->num_outputs; i++) {
+ if (!sitdev->out[i].routed ||
+ sitdev->out[i].pll_idx != sitdpll->id)
+ continue;
+ if (sitdev->out[i].state_stale) {
+ rc = sit9531x_output_state_refresh(sitdev, i);
+ if (rc) {
+ mutex_unlock(&sitdev->multiop_lock);
+ NL_SET_ERR_MSG(extack,
+ "Output mute state could not be read back");
+ return rc;
+ }
+ }
+ if (sitdev->out[i].enabled)
+ drives = true;
+ }
+
+ /*
+ * The lane's signal is read live as well: a loss since the last poll
+ * makes the device fall back on its own, and a sample taken then
+ * would be credited to a pin the PLL is no longer on.
+ */
+ rc = sit9531x_input_mon_fetch(sitdev);
+ if (rc) {
+ mutex_unlock(&sitdev->multiop_lock);
+ NL_SET_ERR_MSG(extack,
+ "Failed to read the input clock monitors");
+ return rc;
+ }
+
+ if (!drives ||
+ !sit9531x_dpll_selection_active(sitdev, sitdpll, dpin->id)) {
+ mutex_unlock(&sitdev->multiop_lock);
+ *phase_offset = 0;
+ return 0;
+ }
+
+ rc = sit9531x_chan_routed_ref_read(sitdev, sitdpll->id, &routed);
+ if (!rc && routed == dpin->id) {
+ rc = sit9531x_phase_offset_read(sitdev, sitdpll->id, &offset);
+ if (!rc)
+ rc = sit9531x_chan_routed_ref_read(sitdev, sitdpll->id,
+ &routed);
+ if (!rc && routed != dpin->id)
+ rc = -ENODATA;
+ } else if (!rc) {
+ rc = -ENODATA;
+ }
+ mutex_unlock(&sitdev->multiop_lock);
+
+ /*
+ * -ENODATA means no reading: the PLL has no known VCO rate, or the
+ * device moved the PLL off this pin around the sample. Report 0 so a
+ * full pin-get dump does not fail over it. Every other errno,
+ * -ENODEV from a vanished adapter included, is a failure.
+ */
+ if (rc == -ENODATA) {
+ *phase_offset = 0;
+ return 0;
+ }
+ if (rc) {
+ NL_SET_ERR_MSG(extack, "TDC phase readback failed");
+ return rc;
+ }
+
+ *phase_offset = offset;
+ return 0;
+}
+
+static const struct dpll_pin_ops sit9531x_dpll_input_pin_ops = {
+ .direction_get = sit9531x_dpll_input_pin_direction_get,
+ .frequency_get = sit9531x_dpll_input_pin_frequency_get,
+ .state_on_dpll_get = sit9531x_dpll_input_pin_state_on_dpll_get,
+ .state_on_dpll_set = sit9531x_dpll_input_pin_state_on_dpll_set,
+ .operstate_on_dpll_get = sit9531x_dpll_input_pin_operstate_on_dpll_get,
+ .prio_get = sit9531x_dpll_input_pin_prio_get,
+ .prio_set = sit9531x_dpll_input_pin_prio_set,
+ .phase_offset_get = sit9531x_dpll_input_pin_phase_offset_get,
+};
+
+/*
+ * The same without frequency_get, for an input whose firmware node lists
+ * no supported-frequencies-hz (or that has no node at all): there is no
+ * rate to report, and the core fails a whole pin dump on an error from
+ * any one callback, so the attribute is left out rather than reported
+ * as 0 Hz.
+ */
+static const struct dpll_pin_ops sit9531x_dpll_input_pin_norate_ops = {
+ .direction_get = sit9531x_dpll_input_pin_direction_get,
+ .state_on_dpll_get = sit9531x_dpll_input_pin_state_on_dpll_get,
+ .state_on_dpll_set = sit9531x_dpll_input_pin_state_on_dpll_set,
+ .operstate_on_dpll_get = sit9531x_dpll_input_pin_operstate_on_dpll_get,
+ .prio_get = sit9531x_dpll_input_pin_prio_get,
+ .prio_set = sit9531x_dpll_input_pin_prio_set,
+ .phase_offset_get = sit9531x_dpll_input_pin_phase_offset_get,
+};
+
+/*
+ * INTSYNC pin ops
+ *
+ * INTSYNC is the chip's inter-PLL sync net: one PLL drives it and other
+ * PLLs may lock to it instead of to an external reference. The two
+ * roles are exposed as two separate pins so neither overloads the other:
+ *
+ * - a source (output) pin registered on every DPLL. Connecting it on a
+ * DPLL makes that DPLL drive INTSYNC; only one DPLL may drive it at a
+ * time. It has no priority ops -- driving the net is not a reference
+ * selection.
+ * - a destination (input) pin registered on every DPLL. Connecting it
+ * on a DPLL makes that DPLL eligible to lock to INTSYNC as a
+ * reference, so it carries the priority ops.
+ */
+
+/* ---- INTSYNC source (output) pin ---- */
+
+/* The INTSYNC source pin is an output; its direction_get is defined below. */
+static int
+sit9531x_dpll_output_pin_direction_get(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_pin_direction *direction,
+ struct netlink_ext_ack *extack);
+
+/*
+ * Does this PLL drive the INTSYNC net with a complete configuration?
+ * What the source pin reports as CONNECTED.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+static bool
+sit9531x_dpll_intsync_src_connected(const struct sit9531x_dev *sitdev,
+ const struct sit9531x_dpll *sitdpll)
+{
+ return sitdev->intsync_src == sitdpll->id && !sitdev->intsync_partial;
+}
+
+static int
+sit9531x_dpll_intsync_src_state_on_dpll_get(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_pin_state *state,
+ struct netlink_ext_ack *extack)
+{
+ struct sit9531x_dpll *sitdpll = dpll_priv;
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+
+ mutex_lock(&sitdev->multiop_lock);
+ if (sit9531x_dpll_intsync_src_connected(sitdev, sitdpll))
+ *state = DPLL_PIN_STATE_CONNECTED;
+ else
+ *state = DPLL_PIN_STATE_DISCONNECTED;
+ mutex_unlock(&sitdev->multiop_lock);
+
+ return 0;
+}
+
+/*
+ * sit9531x_dpll_intsync_src_state_on_dpll_set - drive INTSYNC from a PLL
+ *
+ * CONNECTED -> this PLL drives the INTSYNC net
+ * DISCONNECTED -> stop driving INTSYNC if this PLL drives it
+ *
+ * SELECTABLE is rejected: driving the net is an explicit output routing,
+ * not an automatic-selection candidate, matching the regular output pin.
+ */
+static int
+sit9531x_dpll_intsync_src_state_on_dpll_set(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_pin_state state,
+ struct netlink_ext_ack *extack)
+{
+ struct sit9531x_dpll_pin *dpin = pin_priv;
+ struct sit9531x_dpll *sitdpll = dpll_priv;
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+ int rc = 0, detect_rc = 0;
+ bool changed = false;
+ bool was_connected, moved;
+ u8 hw_src;
+
+ mutex_lock(&sitdev->multiop_lock);
+
+ was_connected = sit9531x_dpll_intsync_src_connected(sitdev, sitdpll);
+
+ switch (state) {
+ case DPLL_PIN_STATE_CONNECTED:
+ /*
+ * A partial configuration on this PLL is not a connection:
+ * run the enable again so a retry after a failed one repairs
+ * the device instead of reporting success over it.
+ */
+ if (was_connected)
+ break;
+ if (sitdev->intsync_src >= 0 &&
+ sitdev->intsync_src != sitdpll->id) {
+ NL_SET_ERR_MSG(extack,
+ "INTSYNC is already sourced by another PLL");
+ rc = -EBUSY;
+ break;
+ }
+ /*
+ * A PLL that already lists INTSYNC among its references must
+ * not also drive it: the destination side refuses the mirror
+ * of this, and without the check here the net could be routed
+ * back into the PLL feeding it.
+ */
+ hw_src = sit9531x_input_hw_src(SIT9531X_INTSYNC_PIN_ID);
+ if (sit9531x_input_prio_present(sitdev, sitdpll->id, hw_src)) {
+ NL_SET_ERR_MSG(extack,
+ "PLL selects INTSYNC as a reference; it cannot drive it");
+ rc = -EBUSY;
+ break;
+ }
+ rc = sit9531x_intsync_enable(sitdev, sitdpll->id);
+ changed = true;
+ break;
+ case DPLL_PIN_STATE_DISCONNECTED:
+ if (sitdev->intsync_src != sitdpll->id)
+ break;
+ rc = sit9531x_intsync_disable(sitdev, sitdpll->id);
+ changed = true;
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ /*
+ * Record what was done before confirming it, and only when this PLL
+ * actually changed: the core forwards a request for the state the
+ * pin is already in, and a DISCONNECTED on a PLL that never drove the
+ * net must not erase the owner. The refresh below leaves the cache
+ * untouched when a read fails, and a cache that wrongly says nobody
+ * drives the net would let a second PLL be configured to drive it.
+ */
+ if (changed && !rc) {
+ sitdev->intsync_src = state == DPLL_PIN_STATE_CONNECTED ?
+ sitdpll->id : -1;
+ sitdev->intsync_partial = false;
+ }
+
+ /*
+ * Re-scan hardware after a transition so the cache follows a
+ * partially failed enable or disable as closely as possible.
+ */
+ if (changed)
+ detect_rc = sit9531x_intsync_src_detect(sitdev);
+ /*
+ * A failed enable that the scan still finds on this PLL is partial
+ * even when every register reads the enable pattern: the small
+ * update that commits it may be the step that failed. Mark it, so
+ * a retried connect runs the enable again.
+ *
+ * When the scan itself failed, nothing says how far the enable and
+ * its rollback got, and the net may be driven. Record this PLL as
+ * the partial owner rather than leave the cache saying nobody drives
+ * the net, which would let a second PLL be configured on top of it.
+ * A disconnect on this PLL runs the disable and a scan that settle
+ * the record.
+ */
+ if (changed && rc && state == DPLL_PIN_STATE_CONNECTED &&
+ (detect_rc || sitdev->intsync_src == sitdpll->id)) {
+ sitdev->intsync_src = sitdpll->id;
+ sitdev->intsync_partial = true;
+ }
+ /*
+ * The refresh only re-reads what the device now shows. Failing
+ * the request because that read hit a bus error would tell
+ * userspace the enable did not happen when it did.
+ */
+ if (detect_rc)
+ dev_warn(sitdev->dev,
+ "INTSYNC source cache not refreshed: %d\n",
+ detect_rc);
+
+ /*
+ * What the pin reports may have moved although the request failed:
+ * a disable that stopped at its last step leaves nothing driving
+ * the net, a failed enable leaves a partial owner. The core
+ * notifies only a request that succeeded and the poll does not
+ * watch this pin, so announce it here, as the output setter does.
+ */
+ moved = sit9531x_dpll_intsync_src_connected(sitdev, sitdpll) !=
+ was_connected;
+
+ mutex_unlock(&sitdev->multiop_lock);
+
+ if (rc) {
+ struct dpll_pin *dpll_pin;
+
+ if (rc != -EBUSY && rc != -EINVAL && rc != -EOPNOTSUPP)
+ NL_SET_ERR_MSG(extack,
+ "Failed to set INTSYNC source state");
+ /*
+ * The core's lock is held here, as the helper requires.
+ * Teardown clears the handle before it unregisters the pin;
+ * see the output state setter.
+ */
+ dpll_pin = READ_ONCE(dpin->dpll_pin);
+ if (moved && dpll_pin)
+ __dpll_pin_change_ntf(dpll_pin);
+ }
+
+ return rc;
+}
+
+static const struct dpll_pin_ops sit9531x_dpll_intsync_src_pin_ops = {
+ .direction_get = sit9531x_dpll_output_pin_direction_get,
+ .state_on_dpll_get = sit9531x_dpll_intsync_src_state_on_dpll_get,
+ .state_on_dpll_set = sit9531x_dpll_intsync_src_state_on_dpll_set,
+};
+
+/* ---- INTSYNC destination (input) pin ---- */
+
+/*
+ * sit9531x_dpll_intsync_dst_state_on_dpll_get - INTSYNC reference state
+ *
+ * Selection role, so the contract above decides this exactly as it does
+ * for a physical input: the priority table is the eligibility record, and
+ * whether a source PLL happens to be driving the net right now is no more
+ * a state than a momentary LOS is on an external reference. The one
+ * addition is that the PLL driving INTSYNC is never its own destination.
+ */
+static int
+sit9531x_dpll_intsync_dst_state_on_dpll_get(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_pin_state *state,
+ struct netlink_ext_ack *extack)
+{
+ struct sit9531x_dpll *sitdpll = dpll_priv;
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+
+ mutex_lock(&sitdev->multiop_lock);
+ if (sitdev->intsync_src == sitdpll->id)
+ *state = DPLL_PIN_STATE_DISCONNECTED;
+ else
+ sit9531x_dpll_selection_state_get(sitdev, sitdpll,
+ SIT9531X_INTSYNC_PIN_ID,
+ state);
+ mutex_unlock(&sitdev->multiop_lock);
+
+ return 0;
+}
+
+/*
+ * sit9531x_dpll_intsync_dst_state_on_dpll_set - lock a PLL to INTSYNC
+ *
+ * Selection role, so this accepts and refuses what a physical input does,
+ * CONNECTED included: the device pins no reference on request whichever
+ * source is asked for. INTSYNC is an internal net with no physical
+ * receiver, so only the per-PLL priority table is touched; the source pin
+ * controls generation.
+ */
+static int
+sit9531x_dpll_intsync_dst_state_on_dpll_set(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_pin_state state,
+ struct netlink_ext_ack *extack)
+{
+ struct sit9531x_dpll *sitdpll = dpll_priv;
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+ u8 hw_src = sit9531x_input_hw_src(SIT9531X_INTSYNC_PIN_ID);
+ int rc;
+
+ mutex_lock(&sitdev->multiop_lock);
+
+ switch (state) {
+ case DPLL_PIN_STATE_DISCONNECTED:
+ rc = sit9531x_input_prio_remove(sitdev, sitdpll->id, hw_src);
+ break;
+ case DPLL_PIN_STATE_CONNECTED:
+ NL_SET_ERR_MSG(extack,
+ "Device selects its reference by priority; use selectable");
+ rc = -EOPNOTSUPP;
+ break;
+ case DPLL_PIN_STATE_SELECTABLE:
+ if (sitdev->intsync_src == sitdpll->id) {
+ NL_SET_ERR_MSG(extack,
+ "PLL cannot lock to the INTSYNC it drives");
+ rc = -EINVAL;
+ break;
+ }
+ rc = sit9531x_input_prio_add(sitdev, sitdpll->id, hw_src);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&sitdev->multiop_lock);
+
+ if (rc == -ENOSPC)
+ NL_SET_ERR_MSG(extack,
+ "Priority table is full of unique sources on this PLL");
+ else if (rc && rc != -EINVAL && rc != -EOPNOTSUPP)
+ NL_SET_ERR_MSG(extack, "Failed to set INTSYNC input state");
+
+ return rc;
+}
+
+static int
+sit9531x_dpll_intsync_dst_operstate_on_dpll_get(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_pin_operstate *state,
+ struct netlink_ext_ack *extack)
+{
+ struct sit9531x_dpll *sitdpll = dpll_priv;
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+
+ mutex_lock(&sitdev->multiop_lock);
+ sit9531x_dpll_selection_operstate_get(sitdev, sitdpll,
+ SIT9531X_INTSYNC_PIN_ID,
+ state);
+ mutex_unlock(&sitdev->multiop_lock);
+
+ return 0;
+}
+
+/*
+ * Do not add .frequency_get / the generic input state getter here: the
+ * destination pin id is SIT9531X_INTSYNC_PIN_ID, one past the end of the
+ * ref[] array (INTSYNC is an internal net with no ref[] entry). The ops
+ * below only ever key on chan[] and the priority table, never ref[id].
+ */
+static const struct dpll_pin_ops sit9531x_dpll_intsync_dst_pin_ops = {
+ .direction_get = sit9531x_dpll_input_pin_direction_get,
+ .state_on_dpll_get = sit9531x_dpll_intsync_dst_state_on_dpll_get,
+ .state_on_dpll_set = sit9531x_dpll_intsync_dst_state_on_dpll_set,
+ .operstate_on_dpll_get =
+ sit9531x_dpll_intsync_dst_operstate_on_dpll_get,
+ .prio_get = sit9531x_dpll_input_pin_prio_get,
+ .prio_set = sit9531x_dpll_input_pin_prio_set,
+};
+
+/*
+ * XO (crystal oscillator) pin ops
+ *
+ * The XO is the chip's internal reference oscillator that feeds every
+ * PLL. It is exposed so userspace can see the on-chip reference, but it
+ * cannot be routed or disconnected, so it is reported permanently
+ * connected and offers no state_on_dpll_set / prio ops.
+ */
+
+static int
+sit9531x_dpll_xo_pin_state_on_dpll_get(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_pin_state *state,
+ struct netlink_ext_ack *extack)
+{
+ *state = DPLL_PIN_STATE_CONNECTED;
+ return 0;
+}
+
+static const struct dpll_pin_ops sit9531x_dpll_xo_pin_ops = {
+ .direction_get = sit9531x_dpll_input_pin_direction_get,
+ .frequency_get = sit9531x_dpll_input_pin_frequency_get,
+ .state_on_dpll_get = sit9531x_dpll_xo_pin_state_on_dpll_get,
+};
+
+static int
+sit9531x_dpll_output_pin_direction_get(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_pin_direction *direction,
+ struct netlink_ext_ack *extack)
+{
+ *direction = DPLL_PIN_DIRECTION_OUTPUT;
+ return 0;
+}
+
+/*
+ * sit9531x_dpll_output_pin_frequency_get - read output pin frequency
+ *
+ * Reads the DIVO divider back from the chip and computes the live
+ * frequency as Fvco / DIVO. Falls back to the cached value only when
+ * the output is not resolvable through the divider chain (e.g. not
+ * mapped to a PLL), so transport/register errors still surface.
+ */
+static int
+sit9531x_dpll_output_pin_frequency_get(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv, u64 *frequency,
+ struct netlink_ext_ack *extack)
+{
+ struct sit9531x_dpll_pin *dpin = pin_priv;
+ struct sit9531x_dpll *sitdpll = dpll_priv;
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+ int rc;
+
+ mutex_lock(&sitdev->multiop_lock);
+ rc = sit9531x_output_freq_get(sitdev, dpin->id, frequency);
+ if (rc == -ENODATA)
+ *frequency = sit9531x_out_state_get(sitdev, dpin->id)->freq;
+ mutex_unlock(&sitdev->multiop_lock);
+
+ return rc == -ENODATA ? 0 : rc;
+}
+
+/*
+ * sit9531x_dpll_output_pin_frequency_set - set output pin frequency
+ *
+ * computes DIVO = Fvco / frequency and writes the
+ * 34-bit output divider to the output system registers via
+ * sit9531x_output_freq_set().
+ */
+static int
+sit9531x_dpll_output_pin_frequency_set(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv, u64 frequency,
+ struct netlink_ext_ack *extack)
+{
+ struct sit9531x_dpll_pin *dpin = pin_priv;
+ struct sit9531x_dpll *sitdpll = dpll_priv;
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+ u8 actual_pll;
+ int rc;
+
+ /*
+ * Read the PLL that drives this output from its OUT_MAP state
+ * (populated by out_state_fetch from the chip's OUT_MAP registers).
+ * That is the index the output register programming below is keyed
+ * by; the output is registered under the DPLL matching this PLL.
+ */
+ actual_pll = sitdev->out[dpin->id].pll_idx;
+
+ mutex_lock(&sitdev->multiop_lock);
+ rc = sit9531x_output_freq_set(sitdev, dpin->id, actual_pll,
+ frequency);
+ mutex_unlock(&sitdev->multiop_lock);
+
+ if (rc)
+ NL_SET_ERR_MSG(extack, "Output frequency set failed");
+
+ return rc;
+}
+
+/*
+ * sit9531x_dpll_output_pin_state_on_dpll_get - get output pin state
+ *
+ * reports CONNECTED when the output is driven and
+ * DISCONNECTED when it has been muted via sit9531x_output_disable().
+ */
+static int
+sit9531x_dpll_output_pin_state_on_dpll_get(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_pin_state *state,
+ struct netlink_ext_ack *extack)
+{
+ struct sit9531x_dpll_pin *dpin = pin_priv;
+ struct sit9531x_dpll *sitdpll = dpll_priv;
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+ const struct sit9531x_out *out;
+ int rc;
+
+ /*
+ * A mute whose read-back failed left the cache unconfirmed; there is
+ * no poll of output state to correct it, so read it here rather than
+ * report a value that may predate the request.
+ */
+ if (sitdev->out[dpin->id].state_stale) {
+ mutex_lock(&sitdev->multiop_lock);
+ rc = sit9531x_output_state_refresh(sitdev, dpin->id);
+ mutex_unlock(&sitdev->multiop_lock);
+ if (rc) {
+ NL_SET_ERR_MSG(extack,
+ "Output mute state could not be read back");
+ return rc;
+ }
+ }
+
+ out = sit9531x_out_state_get(sitdev, dpin->id);
+ *state = out->enabled ? DPLL_PIN_STATE_CONNECTED
+ : DPLL_PIN_STATE_DISCONNECTED;
+ return 0;
+}
+
+/*
+ * sit9531x_dpll_output_pin_state_on_dpll_set - mute/un-mute an output
+ *
+ * Forces Hi-Z on the output through the Hi-Z force/state register pairs
+ * of its slot, the differential and the single-ended one.
+ * CONNECTED -> enable (release the override, the device drives it)
+ * DISCONNECTED -> disable (force Hi-Z)
+ */
+static int
+sit9531x_dpll_output_pin_state_on_dpll_set(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_pin_state state,
+ struct netlink_ext_ack *extack)
+{
+ struct sit9531x_dpll_pin *dpin = pin_priv;
+ struct sit9531x_dpll *sitdpll = dpll_priv;
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+ bool enable = state == DPLL_PIN_STATE_CONNECTED;
+ bool was_enabled, changed;
+ int rc;
+
+ if (!enable && state != DPLL_PIN_STATE_DISCONNECTED)
+ return -EINVAL;
+
+ mutex_lock(&sitdev->multiop_lock);
+
+ /*
+ * The core forwards a request for the state the output is already
+ * in. The Hi-Z pairs say what the pad does now, so read them and
+ * answer such a request from them rather than run the programming
+ * sequence, which holds the output loops unlocked for the 100 ms its
+ * commit takes and would change nothing. A read that fails leaves
+ * the request to the sequence and its own read-back.
+ */
+ if (!sit9531x_output_state_refresh(sitdev, dpin->id) &&
+ sitdev->out[dpin->id].enabled == enable) {
+ mutex_unlock(&sitdev->multiop_lock);
+ return 0;
+ }
+
+ was_enabled = sitdev->out[dpin->id].enabled;
+
+ if (enable)
+ rc = sit9531x_output_enable(sitdev, dpin->id);
+ else
+ rc = sit9531x_output_disable(sitdev, dpin->id);
+
+ /*
+ * A read-back that failed left the cache unconfirmed, and the
+ * writes before it most likely landed: count that as a change too,
+ * and let the getter's read-through fill in the state when the
+ * notification is built.
+ */
+ changed = sitdev->out[dpin->id].enabled != was_enabled ||
+ sitdev->out[dpin->id].state_stale;
+
+ mutex_unlock(&sitdev->multiop_lock);
+
+ if (rc) {
+ struct dpll_pin *dpll_pin;
+
+ NL_SET_ERR_MSG(extack, "Failed to set output pin state");
+ /*
+ * The core notifies only a request that succeeded, and the
+ * poll does not watch outputs. A failed request whose
+ * read-back shows the output did change, or could not show
+ * whether it did, still has to be announced, or subscribers
+ * keep the old state for good. The core's lock is held here,
+ * as the helper requires.
+ *
+ * Teardown clears the pin's handle without that lock before
+ * it unregisters the pin, and this callback spends the
+ * sequence's 100 ms under it. Read the handle once: NULL is
+ * a pin on its way out, with nobody left to tell, and a
+ * handle still set stays valid until this callback returns.
+ */
+ dpll_pin = READ_ONCE(dpin->dpll_pin);
+ if (changed && dpll_pin)
+ __dpll_pin_change_ntf(dpll_pin);
+ }
+
+ return rc;
+}
+
+/*
+ * sit9531x_dpll_output_pin_phase_adjust_get - read output phase adjustment
+ *
+ * Returns what the delay registers hold, i.e. the value
+ * sit9531x_output_phase_adjust_set() programmed after quantization, read
+ * from the cache unless a failed request left it unconfirmed.
+ */
+static int
+sit9531x_dpll_output_pin_phase_adjust_get(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv, s32 *phase_adjust,
+ struct netlink_ext_ack *extack)
+{
+ struct sit9531x_dpll_pin *dpin = pin_priv;
+ struct sit9531x_dpll *sitdpll = dpll_priv;
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+ int rc;
+
+ mutex_lock(&sitdev->multiop_lock);
+ /*
+ * A request whose writes reached the device but whose commit or
+ * phase flush failed left the cache describing the delay before it.
+ * There is no poll of the delay registers to correct that, so read
+ * them here rather than report a value the output is not using.
+ */
+ if (sitdev->out[dpin->id].phase_stale) {
+ bool clamped;
+ s32 phase_ps;
+
+ rc = sit9531x_output_phase_read(sitdev, dpin->id, &phase_ps,
+ &clamped);
+ if (rc) {
+ mutex_unlock(&sitdev->multiop_lock);
+ NL_SET_ERR_MSG(extack,
+ "Output delay could not be read back");
+ return rc;
+ }
+ sitdev->out[dpin->id].phase_adj = phase_ps;
+ sitdev->out[dpin->id].phase_armed = !clamped && phase_ps;
+ sitdev->out[dpin->id].phase_stale = false;
+ }
+ *phase_adjust = sit9531x_out_state_get(sitdev, dpin->id)->phase_adj;
+ mutex_unlock(&sitdev->multiop_lock);
+
+ return 0;
+}
+
+/*
+ * sit9531x_dpll_output_pin_phase_adjust_set - set output phase adjustment
+ *
+ * Programs the per-output PRG_RST_DELAY registers for deterministic
+ * phase offset; see sit9531x_output_phase_adjust_set() in core.c.
+ */
+static int
+sit9531x_dpll_output_pin_phase_adjust_set(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv, s32 phase_adjust,
+ struct netlink_ext_ack *extack)
+{
+ struct sit9531x_dpll_pin *dpin = pin_priv;
+ struct sit9531x_dpll *sitdpll = dpll_priv;
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+ int rc;
+
+ mutex_lock(&sitdev->multiop_lock);
+ rc = sit9531x_output_phase_adjust_set(sitdev, dpin->id, phase_adjust);
+ mutex_unlock(&sitdev->multiop_lock);
+
+ if (rc) {
+ NL_SET_ERR_MSG(extack, "Phase adjust failed");
+ return rc;
+ }
+
+ return 0;
+}
+
+static const struct dpll_pin_ops sit9531x_dpll_output_pin_ops = {
+ .direction_get = sit9531x_dpll_output_pin_direction_get,
+ .frequency_get = sit9531x_dpll_output_pin_frequency_get,
+ .frequency_set = sit9531x_dpll_output_pin_frequency_set,
+ .state_on_dpll_get = sit9531x_dpll_output_pin_state_on_dpll_get,
+ .state_on_dpll_set = sit9531x_dpll_output_pin_state_on_dpll_set,
+ .phase_adjust_get = sit9531x_dpll_output_pin_phase_adjust_get,
+ .phase_adjust_set = sit9531x_dpll_output_pin_phase_adjust_set,
+};
+
+const struct dpll_pin_ops *
+sit9531x_dpll_pin_ops_get(const struct sit9531x_dpll_pin *pin)
+{
+ if (!sit9531x_dpll_is_input_pin(pin)) {
+ if (sit9531x_dpll_is_intsync_src_pin(pin))
+ return &sit9531x_dpll_intsync_src_pin_ops;
+ return &sit9531x_dpll_output_pin_ops;
+ }
+ if (sit9531x_dpll_is_intsync_pin(pin))
+ return &sit9531x_dpll_intsync_dst_pin_ops;
+ if (sit9531x_dpll_is_xo_pin(pin))
+ return &sit9531x_dpll_xo_pin_ops;
+ /*
+ * Seeded from the firmware node before the pin is registered and
+ * never written afterwards, so register and unregister agree.
+ */
+ if (!sit9531x_ref_state_get(pin->dpll->dev, pin->id)->freq)
+ return &sit9531x_dpll_input_pin_norate_ops;
+ return &sit9531x_dpll_input_pin_ops;
+}
+
+/*
+ * The state, operational state and priority of a selection-role pin as
+ * the cache has them now: the baseline at registration, and what the poll
+ * compares it against on every tick.
+ *
+ * Caller must hold sitdev->multiop_lock.
+ */
+static void sit9531x_dpll_pin_snapshot(struct sit9531x_dpll *sitdpll,
+ const struct sit9531x_dpll_pin *pin,
+ enum dpll_pin_state *state,
+ enum dpll_pin_operstate *operstate,
+ u8 *prio)
+{
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+ u8 id = pin->id;
+
+ if (id == SIT9531X_INTSYNC_PIN_ID && sitdev->intsync_src == sitdpll->id)
+ *state = DPLL_PIN_STATE_DISCONNECTED;
+ else
+ sit9531x_dpll_selection_state_get(sitdev, sitdpll, id, state);
+ sit9531x_dpll_selection_operstate_get(sitdev, sitdpll, id, operstate);
+ if (sit9531x_input_prio_get(sitdev, sitdpll->id,
+ sit9531x_input_hw_src(id), prio))
+ *prio = pin->prio;
+}
+
+/*
+ * sit9531x_dpll_pin_baseline - seed what the poll compares a pin against
+ *
+ * Called right after the pin is registered, under multiop_lock, so the
+ * poll's first comparison is against what the create notification
+ * carried. A baseline taken on the first tick instead would absorb
+ * whatever moved between registration and that tick -- a lane losing its
+ * signal, a table rewritten and re-seeded -- and leave userspace with the
+ * snapshot from registration until the next change.
+ */
+void sit9531x_dpll_pin_baseline(struct sit9531x_dpll_pin *pin)
+{
+ struct sit9531x_dpll *sitdpll = pin->dpll;
+
+ lockdep_assert_held(&sitdpll->dev->multiop_lock);
+
+ if (!sit9531x_dpll_is_selection_pin(pin))
+ return;
+
+ sit9531x_dpll_pin_snapshot(sitdpll, pin, &pin->pin_state,
+ &pin->operstate, &pin->prio);
+}
+
+/*
+ * sit9531x_dpll_changes_check - check for state changes and notify
+ *
+ * Called from sit9531x_dev_periodic_work(). Compares current hardware
+ * state against cached values and sends netlink notifications on changes.
+ */
+void sit9531x_dpll_changes_check(struct sit9531x_dpll *sitdpll)
+{
+ enum dpll_lock_status_error status_error = DPLL_LOCK_STATUS_ERROR_NONE;
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+ enum dpll_lock_status lock_status;
+ struct sit9531x_dpll_pin *pin;
+ int rc;
+
+ rc = sit9531x_dpll_lock_status_get(sitdpll->dpll_dev, sitdpll,
+ &lock_status, &status_error, NULL);
+ if (rc) {
+ dev_err(sitdev->dev, "Failed to get DPLL%u lock status: %d\n",
+ sitdpll->id, rc);
+ return;
+ }
+
+ /*
+ * The core publishes the error detail alongside the status, so a
+ * change in either is a change subscribers have to be told about:
+ * an inner loss of lock appearing or clearing while the status
+ * stays UNLOCKED would otherwise be visible only to a later GET.
+ */
+ if (sitdpll->lock_status != lock_status ||
+ sitdpll->lock_status_error != status_error) {
+ sitdpll->lock_status = lock_status;
+ sitdpll->lock_status_error = status_error;
+ dpll_device_change_ntf(sitdpll->dpll_dev);
+ }
+
+ mutex_lock(&sitdev->multiop_lock);
+ list_for_each_entry(pin, &sitdpll->pins, list) {
+ enum dpll_pin_operstate operstate;
+ enum dpll_pin_state state;
+ bool changed;
+ u8 prio;
+
+ if (!sit9531x_dpll_is_selection_pin(pin))
+ continue;
+
+ sit9531x_dpll_pin_snapshot(sitdpll, pin, &state, &operstate,
+ &prio);
+
+ changed = state != pin->pin_state ||
+ operstate != pin->operstate || prio != pin->prio;
+ if (changed)
+ dev_dbg(sitdev->dev,
+ "%s: state %u->%u operstate %u->%u prio %u->%u\n",
+ pin->label, pin->pin_state, state,
+ pin->operstate, operstate, pin->prio, prio);
+
+ pin->pin_state = state;
+ pin->operstate = operstate;
+ pin->prio = prio;
+
+ /*
+ * The notification helper takes DPLL-subsystem locks that the
+ * callbacks run under, so it cannot be called with
+ * multiop_lock held; mark the pin and send after the walk.
+ */
+ if (changed)
+ pin->ntf_pending = true;
+ }
+ mutex_unlock(&sitdev->multiop_lock);
+
+ list_for_each_entry(pin, &sitdpll->pins, list) {
+ if (!pin->ntf_pending)
+ continue;
+ pin->ntf_pending = false;
+ dpll_pin_change_ntf(pin->dpll_pin);
+ }
+}
diff --git a/drivers/dpll/sit9531x/dpll.h b/drivers/dpll/sit9531x/dpll.h
new file mode 100644
index 0000000000000..c2706ced92cd1
--- /dev/null
+++ b/drivers/dpll/sit9531x/dpll.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * SiTime SiT9531x DPLL subsystem interface
+ *
+ * Copyright (C) 2026 SiTime Corp.
+ * Author: Ali Rouhi <arouhi@sitime.com>
+ * Author: Oleg Zadorozhnyi <Oleg.Zadorozhnyi@devoxsoftware.com>
+ *
+ * DPLL device and pin structures, and function declarations for
+ * the DPLL registration and callback layer.
+ */
+
+#ifndef _SIT9531X_DPLL_H
+#define _SIT9531X_DPLL_H
+
+#include <linux/dpll.h>
+#include <linux/list.h>
+#include <linux/types.h>
+
+struct sit9531x_dev;
+
+/* Per-pin DPLL state. */
+struct sit9531x_dpll_pin {
+ struct list_head list;
+ struct sit9531x_dpll *dpll;
+ struct dpll_pin *dpll_pin;
+ dpll_tracker tracker;
+ struct fwnode_handle *fwnode;
+ char label[8]; /* "IN0", "OUT3" */
+ enum dpll_pin_direction dir;
+ u8 id; /* hardware index */
+ u8 prio;
+ enum dpll_pin_state pin_state;
+ enum dpll_pin_operstate operstate;
+ bool ntf_pending;
+};
+
+/* Per-PLL DPLL device state. */
+struct sit9531x_dpll {
+ struct list_head list;
+ struct sit9531x_dev *dev;
+ struct dpll_device *dpll_dev;
+ dpll_tracker tracker;
+ struct dpll_device_ops ops; /* per-instance copy */
+ struct list_head pins;
+ u8 id; /* 0 = PLLA .. 3 = PLLD */
+ enum dpll_lock_status lock_status;
+ enum dpll_lock_status_error lock_status_error;
+};
+
+/* ---- DPLL allocation and registration ---- */
+/*
+ * The callback tables stay with the callbacks; the registration code that
+ * hands them to the subsystem lives next to probe() in core.c.
+ */
+extern const struct dpll_device_ops sit9531x_dpll_device_ops;
+const struct dpll_pin_ops *
+sit9531x_dpll_pin_ops_get(const struct sit9531x_dpll_pin *pin);
+
+struct sit9531x_dpll *sit9531x_dpll_alloc(struct sit9531x_dev *sitdev, u8 ch);
+void sit9531x_dpll_free(struct sit9531x_dpll *sitdpll);
+int sit9531x_dpll_register(struct sit9531x_dpll *sitdpll);
+void sit9531x_dpll_unregister(struct sit9531x_dpll *sitdpll);
+
+/* ---- Periodic change detection ---- */
+void sit9531x_dpll_lock_status_baseline(struct sit9531x_dpll *sitdpll);
+void sit9531x_dpll_pin_baseline(struct sit9531x_dpll_pin *pin);
+void sit9531x_dpll_changes_check(struct sit9531x_dpll *sitdpll);
+
+#endif /* _SIT9531X_DPLL_H */
diff --git a/drivers/dpll/sit9531x/prop.c b/drivers/dpll/sit9531x/prop.c
new file mode 100644
index 0000000000000..3aaafb0efd782
--- /dev/null
+++ b/drivers/dpll/sit9531x/prop.c
@@ -0,0 +1,472 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SiTime SiT9531x firmware node property parsing
+ *
+ * Copyright (C) 2026 SiTime Corp.
+ * Author: Ali Rouhi <arouhi@sitime.com>
+ * Author: Oleg Zadorozhnyi <Oleg.Zadorozhnyi@devoxsoftware.com>
+ *
+ * Retrieves pin properties from Device Tree firmware nodes (or
+ * applies defaults when no firmware node exists).
+ */
+
+#include <linux/dev_printk.h>
+#include <linux/dpll.h>
+#include <linux/err.h>
+#include <linux/fwnode.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "core.h"
+#include "prop.h"
+
+/*
+ * sit9531x_input_pin_label - fill the package label for an input pin
+ *
+ * Split out so input-naming changes stay local to this helper.
+ */
+static void sit9531x_input_pin_label(struct sit9531x_dev *sitdev,
+ struct sit9531x_pin_props *props, u8 id)
+{
+ u8 pair = sit9531x_input_pair(id);
+
+ if (sitdev->ref[id].sig_mode == SIT9531X_MODE_DE)
+ snprintf(props->package_label,
+ sizeof(props->package_label), "IN%u", pair);
+ else
+ snprintf(props->package_label,
+ sizeof(props->package_label), "IN%u%c", pair,
+ sit9531x_input_is_n(id) ? 'N' : 'P');
+}
+
+/*
+ * sit9531x_prop_pin_package_label_set - generate package label
+ * @dir: pin direction
+ * @id: pin index
+ *
+ * Generates a package label string. Output pins are named "OUT0",
+ * "OUT1", ... Input pins are named after the physical pair and lane:
+ * "IN0P", "IN0N", "IN1P", ... for single-ended lanes, or "IN0",
+ * "IN1", ... when the pair is configured differential (the N lane is
+ * not registered in that case).
+ */
+static void
+sit9531x_prop_pin_package_label_set(struct sit9531x_dev *sitdev,
+ struct sit9531x_pin_props *props,
+ enum dpll_pin_direction dir, u8 id)
+{
+ /* The internal INTSYNC pin has a fixed label */
+ if (dir == DPLL_PIN_DIRECTION_INPUT &&
+ id == SIT9531X_INTSYNC_PIN_ID) {
+ strscpy(props->package_label, "INTSYNC",
+ sizeof(props->package_label));
+ props->dpll_props.package_label = props->package_label;
+ return;
+ }
+
+ /* The internal XO reference has a fixed label */
+ if (dir == DPLL_PIN_DIRECTION_INPUT && id == SIT9531X_MAX_INPUTS) {
+ strscpy(props->package_label, "XO",
+ sizeof(props->package_label));
+ props->dpll_props.package_label = props->package_label;
+ return;
+ }
+
+ /* The internal INTSYNC source (output) pin has a fixed label */
+ if (dir == DPLL_PIN_DIRECTION_OUTPUT &&
+ id == SIT9531X_INTSYNC_OUT_PIN_ID) {
+ strscpy(props->package_label, "SYNCOUT",
+ sizeof(props->package_label));
+ props->dpll_props.package_label = props->package_label;
+ return;
+ }
+
+ if (dir == DPLL_PIN_DIRECTION_INPUT)
+ sit9531x_input_pin_label(sitdev, props, id);
+ else
+ /*
+ * Name the chip pin, not the driver's index for it. The
+ * two differ on the variant whose outputs are bonded out
+ * from a subset of the twelve slots, and a package label
+ * that named the index would point at a pin that is not
+ * the one being driven.
+ */
+ snprintf(props->package_label, sizeof(props->package_label),
+ "OUT%u", sitdev->info->clkout_map[id]);
+
+ props->dpll_props.package_label = props->package_label;
+}
+
+/*
+ * sit9531x_prop_pin_fwnode_get - find firmware node for a pin
+ * @dir: pin direction
+ * @id: pin index
+ *
+ * Searches for input-pins/output-pins child nodes in DT, looking for a
+ * child whose "reg" property matches the pin. The binding describes reg
+ * as the hardware index, so an output is matched by the chip slot it
+ * drives rather than by the driver's index for it: on the variant where
+ * the two differ, a board describing the pin it wired would otherwise
+ * have its properties applied to a different one.
+ *
+ * Return: 0 on success, -ENOENT if no firmware node exists
+ */
+static int
+sit9531x_prop_pin_fwnode_get(struct sit9531x_dev *sitdev,
+ struct sit9531x_pin_props *props,
+ enum dpll_pin_direction dir, u8 id)
+{
+ struct fwnode_handle *pins_node, *pin_node;
+ const char *node_name;
+
+ if (dir == DPLL_PIN_DIRECTION_INPUT) {
+ node_name = "input-pins";
+ } else {
+ node_name = "output-pins";
+ if (id < sitdev->info->num_outputs)
+ id = sitdev->info->clkout_map[id];
+ }
+
+ pins_node = device_get_named_child_node(sitdev->dev, node_name);
+ if (!pins_node) {
+ dev_dbg(sitdev->dev, "'%s' sub-node is missing\n", node_name);
+ return -ENOENT;
+ }
+
+ /* Enumerate child pin nodes and find the requested one */
+ fwnode_for_each_child_node(pins_node, pin_node) {
+ u32 reg;
+
+ if (fwnode_property_read_u32(pin_node, "reg", ®))
+ continue;
+
+ if (id == reg)
+ break;
+ }
+
+ fwnode_handle_put(pins_node);
+
+ props->fwnode = pin_node;
+
+ dev_dbg(sitdev->dev, "Firmware node for %s %sfound\n",
+ props->package_label, pin_node ? "" : "NOT ");
+
+ return pin_node ? 0 : -ENOENT;
+}
+
+/*
+ * sit9531x_pin_props_get - get pin properties for a given pin
+ * @dir: pin direction (INPUT or OUTPUT)
+ * @index: pin index
+ *
+ * Allocates a pin properties structure, generates a package label,
+ * looks up the firmware node if available, and reads optional
+ * properties (label, connection-type, supported-frequencies-hz).
+ *
+ * Call sit9531x_pin_props_put() to free the returned structure.
+ *
+ * Return: pointer to pin properties on success, error pointer on error
+ */
+struct sit9531x_pin_props *
+sit9531x_pin_props_get(struct sit9531x_dev *sitdev,
+ enum dpll_pin_direction dir, u8 index)
+{
+ struct dpll_pin_frequency *ranges;
+ struct sit9531x_pin_props *props;
+ int i, j, num_freqs = 0, rc;
+ u64 *freqs = NULL;
+ const char *type;
+ u64 curr_freq;
+
+ props = kzalloc_obj(*props, GFP_KERNEL);
+ if (!props)
+ return ERR_PTR(-ENOMEM);
+
+ if (dir == DPLL_PIN_DIRECTION_INPUT &&
+ index == SIT9531X_INTSYNC_PIN_ID) {
+ /*
+ * INTSYNC destination pin: a PLL locks to the INTSYNC net as a
+ * reference, so it can be connected and re-prioritised.
+ */
+ props->dpll_props.type = DPLL_PIN_TYPE_INT_OSCILLATOR;
+ props->dpll_props.capabilities =
+ DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE |
+ DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE;
+ curr_freq = 0;
+ } else if (dir == DPLL_PIN_DIRECTION_OUTPUT &&
+ index == SIT9531X_INTSYNC_OUT_PIN_ID) {
+ /*
+ * INTSYNC source pin: a PLL drives the INTSYNC net. It can be
+ * connected/disconnected but carries no priority (driving the
+ * net is not a reference selection) and no frequency.
+ */
+ props->dpll_props.type = DPLL_PIN_TYPE_INT_OSCILLATOR;
+ props->dpll_props.capabilities =
+ DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE;
+ curr_freq = 0;
+ } else if (dir == DPLL_PIN_DIRECTION_INPUT &&
+ index == SIT9531X_MAX_INPUTS) {
+ /* The XO reference is fixed: no state or priority control. */
+ props->dpll_props.type = DPLL_PIN_TYPE_INT_OSCILLATOR;
+ props->dpll_props.capabilities = 0;
+ sitdev->ref[index].freq = sitdev->xtal_freq;
+ curr_freq = sitdev->xtal_freq;
+ } else if (dir == DPLL_PIN_DIRECTION_INPUT) {
+ props->dpll_props.type = DPLL_PIN_TYPE_EXT;
+ props->dpll_props.capabilities =
+ DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE |
+ DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE;
+ curr_freq = sitdev->ref[index].freq;
+ } else {
+ /*
+ * A synthesized clock output is an external connection with
+ * no more specific meaning; a board that knows better says
+ * so through the pin's connection-type property below.
+ */
+ props->dpll_props.type = DPLL_PIN_TYPE_EXT;
+ props->dpll_props.capabilities =
+ DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE;
+ curr_freq = sitdev->out[index].freq;
+
+ /*
+ * Allow phase-adjust over a +/-1 ms window. The subsystem
+ * rejects pin_set(phase-adjust, X) when X falls outside
+ * [min, max], so leaving these at 0 silently blocks every
+ * netlink call. The device holds a delay anywhere within
+ * the output period, so the bound is the s32 picosecond
+ * attribute, not the hardware; 1 ms is a round figure below
+ * it that costs nothing. Only outputs get a range: input
+ * pins have no .phase_adjust_set, and advertising one there
+ * would promise userspace something every set would refuse.
+ */
+ props->dpll_props.phase_range.min =
+ -SIT9531X_OUT_PHASE_ADJ_MAX_PS;
+ props->dpll_props.phase_range.max =
+ SIT9531X_OUT_PHASE_ADJ_MAX_PS;
+ /*
+ * The fine step is 30 ps, but requests are accepted at 1 ps
+ * resolution and rounded to the nearest achievable delay, so
+ * advertise the request granularity, not the hardware step.
+ */
+ props->dpll_props.phase_gran = 1;
+ }
+
+ /* Generate package label */
+ sit9531x_prop_pin_package_label_set(sitdev, props, dir, index);
+
+ /*
+ * Both INTSYNC pins are internal to the chip and have no board-level
+ * wiring, so they take no properties from the firmware node.
+ */
+ if (dir == DPLL_PIN_DIRECTION_INPUT &&
+ (index == SIT9531X_INTSYNC_PIN_ID ||
+ index == SIT9531X_MAX_INPUTS))
+ goto skip_fwnode_props;
+ if (dir == DPLL_PIN_DIRECTION_OUTPUT &&
+ index == SIT9531X_INTSYNC_OUT_PIN_ID)
+ goto skip_fwnode_props;
+
+ rc = sit9531x_prop_pin_fwnode_get(sitdev, props, dir, index);
+ if (rc)
+ goto skip_fwnode_props;
+
+ /* Look for "label" property -> board label */
+ fwnode_property_read_string(props->fwnode, "label",
+ &props->dpll_props.board_label);
+
+ /* Look for "connection-type" property -> pin type enum */
+ if (!fwnode_property_read_string(props->fwnode, "connection-type",
+ &type)) {
+ if (!strcmp(type, "ext"))
+ props->dpll_props.type = DPLL_PIN_TYPE_EXT;
+ else if (!strcmp(type, "gnss"))
+ props->dpll_props.type = DPLL_PIN_TYPE_GNSS;
+ else if (!strcmp(type, "int"))
+ props->dpll_props.type = DPLL_PIN_TYPE_INT_OSCILLATOR;
+ else if (!strcmp(type, "synce"))
+ props->dpll_props.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
+ else if (!strcmp(type, "mux"))
+ props->dpll_props.type = DPLL_PIN_TYPE_MUX;
+ else
+ dev_warn(sitdev->dev,
+ "Unknown pin type '%s'\n", type);
+ }
+
+ num_freqs = fwnode_property_count_u64(props->fwnode,
+ "supported-frequencies-hz");
+ if (num_freqs <= 0) {
+ num_freqs = 0;
+ goto skip_fwnode_props;
+ }
+
+ freqs = kcalloc(num_freqs, sizeof(*freqs), GFP_KERNEL);
+ if (!freqs) {
+ rc = -ENOMEM;
+ goto err_alloc_freqs;
+ }
+
+ rc = fwnode_property_read_u64_array(props->fwnode,
+ "supported-frequencies-hz",
+ freqs, num_freqs);
+ if (rc) {
+ dev_warn(sitdev->dev,
+ "failed to parse supported-frequencies-hz for %s: %d\n",
+ props->package_label, rc);
+ goto err_alloc_ranges;
+ }
+
+ /*
+ * Seed the runtime ref->freq with the first DT-listed supported
+ * frequency: an input's rate is a board fact the device cannot be
+ * asked for, so firmware is the only source. An output is left to
+ * the read-back below, which knows what the divider is actually
+ * doing.
+ */
+ if (num_freqs > 0 && dir == DPLL_PIN_DIRECTION_INPUT &&
+ index != SIT9531X_MAX_INPUTS)
+ curr_freq = freqs[0];
+
+ /*
+ * An output's current rate is the one its divider produces, so read
+ * it rather than assume the first entry of a list of the rates the
+ * board supports is the one in force. A rate taken from firmware
+ * that the part is not running would be reported as current and,
+ * worse, used as the output period the phase adjust quantizes
+ * against. An output the configuration does not route has no rate
+ * to read, which is not an error.
+ */
+ if (dir == DPLL_PIN_DIRECTION_OUTPUT &&
+ index < sitdev->info->num_outputs) {
+ u64 hw_freq;
+
+ mutex_lock(&sitdev->multiop_lock);
+ rc = sit9531x_output_freq_get(sitdev, index, &hw_freq);
+ mutex_unlock(&sitdev->multiop_lock);
+ if (!rc)
+ curr_freq = hw_freq;
+ }
+
+skip_fwnode_props:
+ /* Neither INTSYNC pin carries a frequency attribute */
+ if (dir == DPLL_PIN_DIRECTION_INPUT &&
+ index == SIT9531X_INTSYNC_PIN_ID)
+ return props;
+ if (dir == DPLL_PIN_DIRECTION_OUTPUT &&
+ index == SIT9531X_INTSYNC_OUT_PIN_ID)
+ return props;
+
+ /*
+ * Advertise only concrete values from firmware plus current runtime
+ * value. For outputs without a firmware list, publish one wide range as
+ * an explicit fallback because those pins do support frequency_set.
+ */
+ ranges = kcalloc(num_freqs + 2, sizeof(*ranges), GFP_KERNEL);
+ if (!ranges) {
+ rc = -ENOMEM;
+ goto err_alloc_ranges;
+ }
+
+ /*
+ * Publish the seeded rate only once the pin is certain to be built.
+ * The allocation above is the last thing that can fail, and a call
+ * that reports failure must not leave the device's cached rate
+ * changed behind it.
+ */
+ if (curr_freq) {
+ if (dir == DPLL_PIN_DIRECTION_INPUT)
+ sitdev->ref[index].freq = curr_freq;
+ else
+ sitdev->out[index].freq = curr_freq;
+ }
+
+ j = 0;
+
+ /* Current frequency first, when known. */
+ if (curr_freq) {
+ struct dpll_pin_frequency f = DPLL_PIN_FREQUENCY(curr_freq);
+
+ ranges[j++] = f;
+ }
+
+ for (i = 0; i < num_freqs; i++) {
+ struct dpll_pin_frequency freq = DPLL_PIN_FREQUENCY(freqs[i]);
+
+ if (freqs[i] == curr_freq)
+ continue;
+ ranges[j++] = freq;
+ }
+
+ if (dir == DPLL_PIN_DIRECTION_OUTPUT && num_freqs == 0) {
+ ranges[j].min = 1;
+ ranges[j].max = 1000000000ULL; /* 1 GHz */
+ j++;
+ }
+
+ if (j > 0) {
+ props->dpll_props.freq_supported = ranges;
+ props->dpll_props.freq_supported_num = j;
+ } else {
+ kfree(ranges);
+ props->dpll_props.freq_supported = NULL;
+ props->dpll_props.freq_supported_num = 0;
+ }
+
+ kfree(freqs);
+
+ return props;
+
+err_alloc_ranges:
+ kfree(freqs);
+err_alloc_freqs:
+ fwnode_handle_put(props->fwnode);
+ kfree(props);
+
+ return ERR_PTR(rc);
+}
+
+/*
+ * sit9531x_pin_props_put - release pin properties
+ * @props: pin properties to free
+ */
+void sit9531x_pin_props_put(struct sit9531x_pin_props *props)
+{
+ kfree(props->dpll_props.freq_supported);
+
+ if (props->fwnode)
+ fwnode_handle_put(props->fwnode);
+
+ kfree(props);
+}
+
+/*
+ * sit9531x_prop_dpll_type_get - get DPLL channel type from firmware
+ * @index: DPLL channel index (0-3)
+ *
+ * Reads the "dpll-types" string array property from the firmware node
+ * and returns the corresponding DPLL type enum.
+ *
+ * Return: DPLL type for the given channel (default: DPLL_TYPE_PPS)
+ */
+enum dpll_type
+sit9531x_prop_dpll_type_get(struct sit9531x_dev *sitdev, u8 index)
+{
+ const char *types[SIT9531X_NUM_PLLS];
+ int count;
+
+ count = device_property_read_string_array(sitdev->dev, "dpll-types",
+ types, ARRAY_SIZE(types));
+
+ if (index >= count)
+ return DPLL_TYPE_PPS;
+
+ if (!strcmp(types[index], "pps"))
+ return DPLL_TYPE_PPS;
+ else if (!strcmp(types[index], "eec"))
+ return DPLL_TYPE_EEC;
+
+ dev_warn(sitdev->dev, "Unknown DPLL type '%s', using default\n",
+ types[index]);
+
+ return DPLL_TYPE_PPS;
+}
diff --git a/drivers/dpll/sit9531x/prop.h b/drivers/dpll/sit9531x/prop.h
new file mode 100644
index 0000000000000..a65efdfd9ef8a
--- /dev/null
+++ b/drivers/dpll/sit9531x/prop.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * SiTime SiT9531x firmware node property parsing
+ *
+ * Copyright (C) 2026 SiTime Corp.
+ * Author: Ali Rouhi <arouhi@sitime.com>
+ * Author: Oleg Zadorozhnyi <Oleg.Zadorozhnyi@devoxsoftware.com>
+ */
+
+#ifndef _SIT9531X_PROP_H
+#define _SIT9531X_PROP_H
+
+#include <linux/dpll.h>
+#include <linux/fwnode.h>
+
+struct sit9531x_dev;
+
+/*
+ * struct sit9531x_pin_props - pin properties from firmware
+ * @fwnode: firmware node handle (NULL if no DT node)
+ * @dpll_props: DPLL core pin properties
+ * @package_label: pin package label (e.g. "IN0", "OUT3")
+ */
+struct sit9531x_pin_props {
+ struct fwnode_handle *fwnode;
+ struct dpll_pin_properties dpll_props;
+ char package_label[8];
+};
+
+enum dpll_type sit9531x_prop_dpll_type_get(struct sit9531x_dev *sitdev,
+ u8 index);
+struct sit9531x_pin_props *sit9531x_pin_props_get(struct sit9531x_dev *sitdev,
+ enum dpll_pin_direction dir,
+ u8 index);
+void sit9531x_pin_props_put(struct sit9531x_pin_props *props);
+
+#endif /* _SIT9531X_PROP_H */
diff --git a/drivers/dpll/sit9531x/regs.h b/drivers/dpll/sit9531x/regs.h
new file mode 100644
index 0000000000000..601dafbb21792
--- /dev/null
+++ b/drivers/dpll/sit9531x/regs.h
@@ -0,0 +1,425 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * SiTime SiT9531x register definitions
+ *
+ * Copyright (C) 2026 SiTime Corp.
+ * Author: Ali Rouhi <arouhi@sitime.com>
+ * Author: Oleg Zadorozhnyi <Oleg.Zadorozhnyi@devoxsoftware.com>
+ */
+
+#ifndef _SIT9531X_REGS_H
+#define _SIT9531X_REGS_H
+
+/*
+ * I2C register model:
+ * - Page select register at offset 0xFF, present in every page
+ * - Each page has 256 registers (0x00-0xFF)
+ * - Some pages are paired (e.g. 0x0A/0x1A for PLLA)
+ */
+#define SIT9531X_PAGE_SEL 0xFF
+#define SIT9531X_PAGE_SIZE 0x100
+#define SIT9531X_NUM_PAGES 32
+
+/* Helper macros for page:offset addressing */
+#define SIT9531X_REG(_page, _offset) (((_page) << 8) | (_offset))
+#define SIT9531X_REG_PAGE(_reg) ((_reg) >> 8)
+#define SIT9531X_REG_OFFSET(_reg) ((_reg) & 0xFF)
+
+#define SIT9531X_PAGE_OUTSYS0 0x03
+#define SIT9531X_PAGE_OUTSYS1 0x04
+#define SIT9531X_PAGE_PLLA 0x0A
+#define SIT9531X_PAGE_PLLA_EXT 0x1A
+
+/*
+ * VARIANT_ID is a single byte at page 0 reg 0x02 (95317 = 0x17, 95316 = 0x31).
+ * Reg 0x03 carries an unrelated revision byte and must not be combined into
+ * the variant identifier.
+ */
+#define SIT9531X_REG_VARIANT_ID SIT9531X_REG(0x00, 0x02)
+
+/* DCO trigger pulse timing: minimum 6 ns required by hardware */
+
+/* Page 0 -- PLL inner loop loss-of-lock */
+#define SIT9531X_REG_PLL_INNER_LOL_STATUS SIT9531X_REG(0x00, 0x92)
+#define SIT9531X_REG_PLL_INNER_LOL_NOTIF SIT9531X_REG(0x00, 0x93)
+
+/* Page 0 -- Clock monitor PLL / XO status */
+#define SIT9531X_REG_CMON_NOTIF SIT9531X_REG(0x00, 0x9E)
+
+/* Page 0 -- PLL outer-loop loss-of-lock */
+#define SIT9531X_REG_OUTER_LOL_STATUS SIT9531X_REG(0x00, 0x06)
+#define SIT9531X_REG_OUTER_LOL_NOTIF SIT9531X_REG(0x00, 0x07)
+
+/* Page 0 -- PLL holdover freeze status */
+#define SIT9531X_REG_HO_FREEZE_STATUS SIT9531X_REG(0x00, 0x0A)
+#define SIT9531X_REG_HO_FREEZE_NOTIF SIT9531X_REG(0x00, 0x0B)
+
+/* Page 0 -- INTSYNC (inter-PLL synchronization) global enable */
+#define SIT9531X_REG_INTSYNC_GLOBAL SIT9531X_REG(0x00, 0x40)
+#define SIT9531X_INTSYNC_EN_BIT 6
+
+/*
+ * Priority table: 6 registers per PLL, each holds two priority slots
+ * nibble-packed. The register holding slots 2n and 2n+1 keeps the
+ * earlier slot (CLK_SPARE<2n>SEL_PLL) in [7:4] and the later one in
+ * [3:0].
+ *
+ * Base registers for PLLA: 0x16-0x1B (slots 0-10 plus the
+ * active-reference nibble).
+ * For PLL N: base + 6 * N (e.g. PLLB starts at 0x1C).
+ *
+ * Input source encoding (4-bit value):
+ * 0=IN0P, 1=IN1P, 2=IN2P, 3=IN3P,
+ * 5=OCXO, 6=INTSYNC,
+ * 7=IN0N, 8=IN1N, 9=IN2N, 10=IN3N
+ *
+ * Codes 4 and 11 address a fifth input pair that this part does
+ * not have. They read back as no valid reference.
+ */
+#define SIT9531X_PAGE_PRIOSYS 0x01
+#define SIT9531X_PRIO_BASE_REG 0x16
+#define SIT9531X_PRIO_REGS_PER_PLL 6
+#define SIT9531X_PRIO_SLOTS_PER_REG 2
+/*
+ * 11 priority slots, CLK_SPARE0SEL_PLL through CLK_SPARE10SEL_PLL.
+ * The twelfth nibble of the block is not a slot: it is
+ * CLK_ACTIVESEL_PLL, see SIT9531X_PRIO_ACTIVESEL_OFF below.
+ */
+#define SIT9531X_PRIO_MAX_SLOTS 11
+/* Number of source encodings (0-11), unrelated to the slot count */
+#define SIT9531X_PRIO_NUM_SRC 12
+#define SIT9531X_PRIO_NIBBLE_MASK 0x0F
+#define SIT9531X_PRIO_HI_SHIFT 4
+/* Input source encoding values (see table above) */
+/*
+ * Either of the two codes for the absent fifth pair works as "no source";
+ * the driver writes this one when it empties a slot.
+ */
+#define SIT9531X_PRIO_SRC_NONE 4
+#define SIT9531X_PRIO_SRC_OCXO 5
+#define SIT9531X_PRIO_SRC_INTSYNC 6
+#define SIT9531X_PRIO_SRC_N_BASE 7
+/*
+ * The last register of each PLL's priority block holds, in its low
+ * nibble, the input source the PLL has currently selected as its
+ * active reference (CLK_ACTIVESEL_PLL, same 4-bit encoding as above).
+ */
+#define SIT9531X_PRIO_ACTIVESEL_OFF 5
+
+/*
+ * Page 0 -- PRG_Directives_GENERIC_0, the main system's programming
+ * directive register. Every page carries its own copy of this
+ * register at offset 0x0F with the same bit layout:
+ *
+ * bit 6 proceed to loop lock / active state from the PRG_CMD state
+ * bit 4 update the NVM bank from the efuse contents
+ * bit 3 read the efuse into the volatile registers
+ * bit 2 program the efuse
+ * bit 1 small change update (SIT9531X_SMALL_UPDATE_CMD)
+ * bit 0 escape to the PRG_CMD state
+ *
+ * The NVM bank is a volatile shadow, so bits 4 and 1 are both fine in
+ * a runtime path: bit 1 for a change made in the active state, bit 4
+ * to close a PRG_CMD sequence. Only bit 2 writes non-volatile
+ * storage, and the driver never issues it.
+ */
+#define SIT9531X_REG_GLOBAL_UPDATE SIT9531X_REG(0x00, 0x0F)
+#define SIT9531X_SMALL_UPDATE_CMD 0x02
+
+/* PLL holdover control (PLL page offset) */
+#define SIT9531X_PLL_REG_HO_CTRL 0x6F
+#define SIT9531X_PLL_HO_FORCE_BIT 4
+
+/* One bit per input PAIR (bit 0 = CLKIN0, ..., bit 3 = CLKIN3) */
+#define SIT9531X_REG_IN_DE_FORCE SIT9531X_REG(0x02, 0xE8)
+#define SIT9531X_REG_IN_DE_STATE SIT9531X_REG(0x02, 0xE9)
+#define SIT9531X_REG_IN_SEP_FORCE SIT9531X_REG(0x02, 0xEA)
+#define SIT9531X_REG_IN_SEP_STATE SIT9531X_REG(0x02, 0xEB)
+#define SIT9531X_REG_IN_SEN_FORCE SIT9531X_REG(0x02, 0xF2)
+#define SIT9531X_REG_IN_SEN_STATE SIT9531X_REG(0x02, 0xF3)
+
+/*
+ * One register per input pair at 0x1B + 0x10 * pair
+ * (CLKIN0 = 0x1B, CLKIN1 = 0x2B, CLKIN2 = 0x3B, CLKIN3 = 0x4B).
+ * SE_P_EN/SE_N_EN set means the corresponding lane is configured
+ * single-ended; both clear means the pair runs differential.
+ */
+#define SIT9531X_REG_IN_MODE(_pair) \
+ SIT9531X_REG(0x02, 0x1B + 0x10 * (_pair))
+#define SIT9531X_IN_MODE_SE_P_EN BIT(0)
+#define SIT9531X_IN_MODE_SE_N_EN BIT(1)
+
+/* ---- Page 0x03 (Output System) registers -- Hi-Z control ---- */
+#define SIT9531X_REG_HIZ_DIFF_07_MASK SIT9531X_REG(0x03, 0xF2)
+#define SIT9531X_REG_HIZ_DIFF_07_STATE SIT9531X_REG(0x03, 0xF3)
+#define SIT9531X_REG_HIZ_DIFF_811_MASK SIT9531X_REG(0x03, 0xF4)
+#define SIT9531X_REG_HIZ_DIFF_811_STATE SIT9531X_REG(0x03, 0xF5)
+#define SIT9531X_REG_HIZ_SE_07_MASK SIT9531X_REG(0x03, 0xF8)
+#define SIT9531X_REG_HIZ_SE_07_STATE SIT9531X_REG(0x03, 0xF9)
+#define SIT9531X_REG_HIZ_SE_811_MASK SIT9531X_REG(0x03, 0xFA)
+#define SIT9531X_REG_HIZ_SE_811_STATE SIT9531X_REG(0x03, 0xFB)
+/*
+ * The SE pairs above force the OutP pad of a CMOS output. The OutN pad
+ * has its own: 0xF6/0xF7 for slots 0-7, and for slots 8-11 the upper
+ * nibble of the differential pair, 0xF4/0xF5 bits 7:4.
+ */
+#define SIT9531X_REG_HIZ_SEN_07_MASK SIT9531X_REG(0x03, 0xF6)
+#define SIT9531X_REG_HIZ_SEN_07_STATE SIT9531X_REG(0x03, 0xF7)
+#define SIT9531X_HIZ_SEN_811_SHIFT 4
+
+/*
+ * Output driver configuration. Either CMOS enable means the output
+ * is wired single-ended -- one lane, or both driven as CMOS; with
+ * neither set it is a differential pair.
+ */
+#define SIT9531X_OUT_MISC0_BASE 0x1E
+#define SIT9531X_OUT_MISC0_STRIDE 0x10
+#define SIT9531X_OUT_CMOS_ENP BIT(3)
+#define SIT9531X_OUT_CMOS_ENN BIT(2)
+
+/*
+ * Output divider registers in Pages 3/4. Each output has a 34-bit
+ * integer divider mapped to 5 bytes (LSB at base reg, MSB at base-4).
+ * Outputs 0-5 are on Page 3, outputs 6-11 are on Page 4.
+ *
+ * The base register for slot N within a page is:
+ * clkout_odr_divn_base[slot] = { 0x14, 0x24, 0x34, 0x44, 0x54, 0x64 }
+ *
+ * Layout: base=LSB, base-1, base-2, base-3, base-4[1:0]=MSB.
+ *
+ * Per-chip clkout_map[] translates output index to slot position.
+ */
+#define SIT9531X_PAGE_OUTSYS0_SLOT_MAX 5 /* slots 0-5 on Page 0x03 */
+
+/* Misc output system registers */
+#define SIT9531X_REG_PRG_DIR_GEN SIT9531X_REG(0x03, 0x0F)
+#define SIT9531X_PRG_CMD_STATE 0x01
+#define SIT9531X_UPDATE_NVM 0x10
+#define SIT9531X_LOOP_LOCK 0x40
+
+/* Debug register (same offset, per-page) */
+#define SIT9531X_REG_OUTSYS_DEBUG SIT9531X_REG(0x03, 0xBD)
+#define SIT9531X_DEBUG_UNLOCK_VAL 0xC3
+#define SIT9531X_DEBUG_LOCK_VAL 0x00
+
+/*
+ * Per-output programmable phase delay: 34-bit coarse (in VCO clock
+ * cycles) plus a 3-bit fine field with fixed 30 ps steps. Each output
+ * has a five-byte block PROG6..PROG2:
+ *
+ * base + 0 PROG6 [7:5] OPSTG_VCASC_BUMP (preserve via RMW)
+ * [4:2] PRG_RST_FINE_DELAY[2:0]
+ * [1:0] PRG_RST_DELAY[33:32]
+ * base + 1 PROG5 [7:0] PRG_RST_DELAY[31:24]
+ * base + 2 PROG4 [7:0] PRG_RST_DELAY[23:16]
+ * base + 3 PROG3 [7:0] PRG_RST_DELAY[15:8]
+ * base + 4 PROG2 [7:0] PRG_RST_DELAY[7:0]
+ *
+ * Slots 0-5 are on Page 3, slots 6-11 on Page 4. The block base
+ * within a page is 0x15 + 16 * (slot % 6), where slot is the physical
+ * output slot from clkout_map[], not the logical output index.
+ */
+#define SIT9531X_OUT_PRG_DELAY_BASE 0x15
+#define SIT9531X_OUT_PRG_SLOT_STRIDE 0x10
+#define SIT9531X_OUT_PRG_BYTES 5
+/* bits [7:5], preserve */
+#define SIT9531X_OUT_PRG_OPSTG_MASK 0xE0
+#define SIT9531X_OUT_PRG_FINE_SHIFT 2
+#define SIT9531X_OUT_PRG_FINE_MASK 0x1C /* bits [4:2] */
+#define SIT9531X_OUT_PRG_COARSE_HI_MASK 0x03 /* bits [1:0] */
+/*
+ * The divider takes two VCO cycles to act on a programmed delay and
+ * release the output, so the encoded value carries them and the realized
+ * delay is the register value less that. The reference flow adds the
+ * same two.
+ */
+#define SIT9531X_OUT_PRG_DIVO_CYCLES 2
+
+#define SIT9531X_OUT_PRG_FINE_STEP_PS 30
+#define SIT9531X_OUT_PRG_FINE_MAX 7 /* 3-bit field */
+#define SIT9531X_OUT_PRG_COARSE_BITS 34
+
+/*
+ * On-demand phase-flush fired from a register rather than a GPIO pin.
+ * DIVO_PHASE_SEL_REG selects the in-register trigger source and
+ * DIVO_PHASE_TRIG flushes the output phase when pulsed high then low.
+ * The unrelated OEb trigger pair in bits [7:6] must be preserved.
+ */
+#define SIT9531X_REG_GPIO_FUNC_CTRL1 SIT9531X_REG(0x00, 0x65)
+#define SIT9531X_DIVO_PHASE_SEL_REG BIT(5)
+#define SIT9531X_DIVO_PHASE_TRIG BIT(4)
+
+/* ---- PLL page registers (apply to pages 0x0A-0x0D) ---- */
+#define SIT9531X_PLL_REG_SMALL_UPDATE 0x0F
+
+/* On-demand phase-flush enable (PLL page reg 0x3D bit 7) */
+#define SIT9531X_PLL_REG_PHFL_CTRL 0x3D
+#define SIT9531X_PLL_PHFL_ON_DEMAND_EN BIT(7)
+
+/* Whether the PLL has the phase-flush feature (PLL page reg 0x47 bit 7) */
+#define SIT9531X_PLL_REG_CONFIG47 0x47
+#define SIT9531X_PLL_CONFIG47_PHFL_EN BIT(7)
+
+/* Directives_GENERIC_PLL: the restart bit restarts the PLL, self-clearing */
+#define SIT9531X_PLL_REG_DIRECTIVES 0x05
+#define SIT9531X_PLL_DIRECTIVE_RESTART BIT(0)
+
+/*
+ * Loop-filter coefficients on PLL_PAGE regs 0x10-0x15 (3 normal +
+ * 3 fast-lock) are GUI/NVM-generated by the timing configurator and must not be
+ * reprogrammed at runtime; the register map flags them as
+ * "GUI generated configuration should not change manually".
+ */
+
+/*
+ * Output-enable mask, twelve bits: bits 0-7 in LO, bits 8-11 in HI[3:0].
+ * OUTn is bit n on PLLA and PLLB and bit 11 - n on PLLC and PLLD.
+ */
+#define SIT9531X_PLL_REG_OUT_MAP_HI 0x27
+#define SIT9531X_PLL_REG_OUT_MAP_LO 0x28
+#define SIT9531X_PLL_REG_STATUS 0x31
+
+/* DIVN registers (free-run divider readback) */
+#define SIT9531X_PLL_REG_DIVN_INT 0x30
+#define SIT9531X_PLL_REG_DIVN_NUM 0x32 /* 4 bytes (0x32-0x35) */
+#define SIT9531X_PLL_REG_DIVN_DEN 0x38 /* 4 bytes (0x38-0x3B) */
+
+/*
+ * Signal pathway debug readback -- PLL page. Dig_Sys_ReadCode selects
+ * which point of the pathway is tapped, Dig_Sys_WriteCode carries the
+ * modifiers for that read, Dig_Sys_read7..read0 hold the sampled bytes
+ * and a read of the debug status data register latches a sample. The
+ * TDC phase measurement is one tap among several, reached through read
+ * code 69.
+ */
+#define SIT9531X_PLL_REG_DBG_READ_CODE 0xB3
+#define SIT9531X_PLL_REG_DBG_WRITE_CODE 0xB4
+#define SIT9531X_DBG_LOW_FREQ_CLK_BIT BIT(7)
+#define SIT9531X_PLL_REG_DBG_DATA_0 0xB5 /* [7:0] */
+#define SIT9531X_PLL_REG_DBG_DATA_1 0xB6 /* [15:8] */
+#define SIT9531X_PLL_REG_DBG_DATA_2 0xB7 /* [23:16] */
+#define SIT9531X_PLL_REG_DBG_DATA_3 0xB8 /* [31:24] */
+#define SIT9531X_PLL_REG_DBG_DATA_4 0xB9 /* [34:32] + sign */
+
+/* Read code of the TDC phase tap, and the sign bit of its sample */
+#define SIT9531X_DBG_READ_CODE_TDC 69
+#define SIT9531X_TDC_SIGN_BIT 3
+#define SIT9531X_TDC_MAG_HI_MASK GENMASK(2, 0)
+
+/* DIVN is carried as fixed point, in steps of 1e-12 of a whole divider */
+#define SIT9531X_DIVN_SCALE 1000000000000ULL
+
+#define SIT9531X_PLL_REG_ACTIVE 0x02
+#define SIT9531X_PLL_ACTIVE_BIT BIT(0) /* PLL reached active state */
+
+/* PLL EXT page INTSYNC configuration registers */
+#define SIT9531X_PLL_EXT_PAGE(_idx) (SIT9531X_PAGE_PLLA_EXT + (_idx))
+
+#define SIT9531X_PLL_STATUS_OUTER_DIS BIT(5)
+
+/*
+ * Debug register -- PLL page. Writing the key unlocks the page's debug
+ * registers for writing; writing zero locks them again.
+ */
+#define SIT9531X_PLL_REG_DEBUG 0xBD
+#define SIT9531X_PLL_DEBUG_UNLOCK 0xC3
+#define SIT9531X_PLL_DEBUG_LOCK 0x00
+
+/*
+ * Debug status bus -- PLL page. The address register selects the block
+ * whose status the data register returns. A read of the data register
+ * returns what the previous read latched, so the documented procedures
+ * read it three times and take the last.
+ */
+#define SIT9531X_PLL_REG_DBG_STATUS_ADDR 0xD1
+#define SIT9531X_PLL_REG_DBG_STATUS_DATA 0xD0
+#define SIT9531X_DBG_STATUS_READS 3
+
+/*
+ * Status of the reference routed to the PLL: the low nibble is the
+ * source the input subsystem feeds it, in the priority-table encoding.
+ */
+#define SIT9531X_DBG_STATUS_ROUTED_SRC 0xB0
+#define SIT9531X_DBG_ROUTED_SRC_MASK GENMASK(3, 0)
+
+/*
+ * PLL_CONFIG1F_PLL: bit 6 puts the PLL in manual active select, where it
+ * no longer arbitrates by priority. The other bits are GUI-generated
+ * configuration.
+ */
+#define SIT9531X_PLL_REG_CONFIG1F 0x1F
+#define SIT9531X_PLL_CONFIG1F_MANUAL_SEL BIT(6)
+
+/*
+ * MISCINNER_PLL: in manual active select, bit 5 makes the PLL follow the
+ * manual input select instead of its active selection. The other bits
+ * are GUI-generated configuration.
+ */
+#define SIT9531X_PLL_REG_MISCINNER 0x18
+#define SIT9531X_PLL_MISCINNER_MAN_IN_SEL BIT(5)
+
+/*
+ * GPIO_INPUT_FUNC_CTRL5..8 (page 0), one per PLL: the manual input
+ * select. With bit 4 set the input is the low nibble of this register,
+ * otherwise it comes from the input-select pins. Bits 6:5 are the
+ * active/spare choice of an indirect selection (bit 6 enables bit 5,
+ * which picks the active or the spare clock) and are not decoded here.
+ */
+#define SIT9531X_REG_MAN_IN_SEL(_pll) SIT9531X_REG(0x00, 0xE8 + (_pll))
+#define SIT9531X_MAN_IN_SEL_FROM_REG BIT(4)
+#define SIT9531X_MAN_IN_SEL_MASK GENMASK(3, 0)
+
+/*
+ * Per-PLL status register. HO_VALID says the holdover window holds a
+ * valid frequency estimate, i.e. holdover memory has been acquired; it is
+ * not the same as HO_FREEZE (page 0, reg 0x0A), which says the PLL has
+ * already switched over to holdover.
+ */
+#define SIT9531X_PLL_REG_STATUS_1 0x06
+#define SIT9531X_PLL_STATUS_1_HO_VALID BIT(2)
+
+/*
+ * Page 0x06 clock monitor status, one nibble per input lane: P lanes in
+ * 0x02 (IN0P low, IN1P high) and 0x06 (IN2P, IN3P), N lanes in 0x92
+ * (IN0N, IN1N) and 0x96 (IN2N, IN3N).
+ */
+#define SIT9531X_CLKMON_P_STATUS_01 SIT9531X_REG(0x06, 0x02)
+#define SIT9531X_CLKMON_P_STATUS_23 SIT9531X_REG(0x06, 0x06)
+#define SIT9531X_CLKMON_N_STATUS_01 SIT9531X_REG(0x06, 0x92)
+#define SIT9531X_CLKMON_N_STATUS_23 SIT9531X_REG(0x06, 0x96)
+#define SIT9531X_CLKMON_FINE_DRIFT BIT(0)
+#define SIT9531X_CLKMON_COARSE_DRIFT BIT(1)
+#define SIT9531X_CLKMON_LOSS BIT(2)
+#define SIT9531X_CLKMON_P_NOTIF_01 SIT9531X_REG(0x06, 0x03)
+#define SIT9531X_CLKMON_P_NOTIF_23 SIT9531X_REG(0x06, 0x07)
+
+#define SIT9531X_CLKMON_N_NOTIF_01 SIT9531X_REG(0x06, 0x93)
+#define SIT9531X_CLKMON_N_NOTIF_23 SIT9531X_REG(0x06, 0x97)
+
+/* Per-input bit offsets within clock monitor nibble */
+
+/*
+ * EEPROM profile load, page 0. The device compares the CRC stored in the
+ * EEPROM against the one it computes from what it read; a mismatch means
+ * the profile on the part is not the profile the board expects.
+ * NOTIFY_4 collects the read-done bit and the defect bits, all sticky, so
+ * a healthy load leaves exactly the read-done bit set.
+ */
+/* 4 bytes each, MSB first */
+#define SIT9531X_REG_REC_CRC SIT9531X_REG(0x00, 0x8A)
+#define SIT9531X_REG_CAL_CRC SIT9531X_REG(0x00, 0x8E)
+#define SIT9531X_REG_EEPROM_NOTIF SIT9531X_REG(0x00, 0x97)
+#define SIT9531X_EEPROM_READ_DONE BIT(0)
+
+/* Profile identifier the loaded configuration carries, page 1, 24 bits */
+/* 3 bytes, LSB first */
+#define SIT9531X_REG_PROFILE_ID SIT9531X_REG(0x01, 0x44)
+
+/* Consecutive failed INTRB acknowledgements before the line is given up */
+#define SIT9531X_IRQ_ACK_TRIES 8
+
+/* ---- Variant ID values (one byte at SIT9531X_REG_VARIANT_ID) ---- */
+#define SIT9531X_VARIANT_ID_95317 0x17
+#define SIT9531X_VARIANT_ID_95316 0x31
+
+#endif /* _SIT9531X_REGS_H */
|
| 3/2 | 2026/10/09 21:55 | llm | patch-evaluator |
2mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 120034 output: 22 thoughts: 2463 Thoughts: **Fuzzing Evaluation: SiTime SiT9531x DPLL Driver** Okay, so I'm looking at this patch series adding support for the SiTime SiT9531x DPLL Clock Generator. My immediate thought is, "does this warrant fuzzing with syzkaller?" Let's break it down. First, I see the files touched, and it looks like a proper new driver under `drivers/dpll/sit9531x/`. This is an I2C-controlled clock generator, which immediately raises questions about reachability in a standard fuzzing environment. Can syzkaller even *hit* this code? That's the key. My focus is the reachability gate. Can this I2C device be instantiated within QEMU/syzkaller? My mind flashes to the section regarding reachability: code must be executable in standard virtualized environments. Can I2C devices be probed and controlled via standard methods? I know userspace can create I2C devices via `/sys/bus/i2c/devices/i2c-*/new_device`, which is promising. But does syzkaller have pseudo-syscalls or does I2C probe work? Or does it require physical hardware? Does it work with dummy/emulated I2C buses? I'm thinking, let's look at `sit9531x_dev_probe` and `sit9531x_i2c_probe` in `drivers/dpll/sit9531x/core.c`. I need to understand what this driver *expects* during probe. I see a check for a crystal clock or "clock-frequency" property, which it attempts to read. Instantiating via sysfs *new_device* will not provide this, unless a dedicated devicetree node is created beforehand, but is it possible using `new_device`? Also, the code reads a variant ID from a hardware register over I2C using `regmap_read`. I2C transactions mean a *physical* chip or an emulator. The `i2c-stub` driver likely won't cut it, and reading that register is mandatory. The critical question now is, does this fall under "Structurally unreachable hardware"? According to the fuzzing guidelines, that's a *negative* criterion. My understanding is that the hardware is unreachable, and thus, this doesn't warrant fuzzing. I'll double-check similar DPLL drivers, looking at `drivers/dpll/Makefile`, I see `zl3073x`. Let's investigate that. I need to make sure I haven't missed a way to reach this, or a related technology. |
| 4/2 | 2026/10/09 21:57 | tool | grepper |
0mArgs:{
"Expression": "CONFIG_ZL3073X"
}Results: {
"Output": "drivers/dpll/Makefile=11=obj-$(CONFIG_SIT9531X_DPLL)\t+= sit9531x/\ndrivers/dpll/Makefile:12:obj-$(CONFIG_ZL3073X)\t+= zl3073x/\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\\\n--\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"
} |
| 5/2 | 2026/10/09 21:57 | llm | patch-evaluator |
1mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 123493 output: 33 thoughts: 3194 Thoughts: **Analysis of the Code's Reachability and Worthiness for Fuzzing**
Okay, let's break this down. My goal is to determine if the changes warrant fuzzing. I'm looking at this DPLL subsystem, specifically the `sit9531x` driver. The DPLL core is controlled via generic netlink, and devices register with `dpll_device_register`, enabling user-space interaction via Netlink commands. However, the registration of the `sit9531x` device hinges entirely on the driver probing.
The `sit9531x` driver, as an I2C driver, presents the first significant hurdle. It uses an I2C bus. Now, the question is whether the target architecture, which I'm assuming is the standard QEMU/GCE, *has* the physical hardware or, crucially, whether the I2C setup is achievable in a standard virtual environment.
My initial thought: QEMU and GCE definitely *do not* emulate the specific SiT95316 or SiT95317 chip. These are specialized DPLL clock generators. If I were to try creating an I2C device via sysfs, this will almost instantly fail. The `sit9531x_dev_probe` function first tries to get a clock ("xtal") but it's not possible to get the device properties of this sort of device.
It's clear that on amd64, there won't be a clock named "xtal" associated with a dynamically created I2C client. The device properties won't be set either. `sitdev->xtal_freq` will be zero. The probe function then fails because there's no way to read a valid clock rate. Even if `xtal_freq` wasn't 0 (which is very unlikely) the probe would fail at `sit9531x_read_variant_id` because it attempts to communicate with the SiTime chip via I2C, and without a physical chip answering (or a *very* accurate emulation), the read will error.
From the guidelines I'm reading, this seems to be a case of "Structurally unreachable hardware." The driver requires a specific physical component (a SiTime chip on an I2C bus) not emulated in the standard virtual environments (QEMU/GCE). This falls squarely into the "NEGATIVE CRITERIA" category, regardless of how much logic or assertions are present in the modified code.
To confirm, I'm checking the diff. No changes were made to core subsystems - everything is either documentation, MAINTAINERS, or the *new* driver code. The changes are limited to a new driver for an I2C device on a specific chip.
To be extra certain, I need to understand if this situation is in any way unique. It's clear that drivers of a similar nature exist - such as `drivers/dpll/zl3073x` - and I'm going to check the commit log to see what kind of a driver that is.
In summary, this driver will not probe in a standard QEMU environment, making this code structurally unreachable for fuzzing.
|
| 6/2 | 2026/10/09 21:59 | tool | grepper |
0mArgs:{
"Expression": "ZL3073X",
"PathPrefixes": [
"drivers/dpll/"
]
}Results: {
"Output": "\nFull output is too long, showing 200 out of 532 lines.\nUse more precise expression or PathPrefixes if possible.\n\nNumber of matching lines per file (21 files in total):\ndrivers/dpll/Makefile:1\ndrivers/dpll/zl3073x/Kconfig:5\ndrivers/dpll/zl3073x/Makefile:3\ndrivers/dpll/zl3073x/chan.h:4\ndrivers/dpll/zl3073x/core.c:26\ndrivers/dpll/zl3073x/core.h:14\ndrivers/dpll/zl3073x/devlink.c:1\ndrivers/dpll/zl3073x/devlink.h:3\ndrivers/dpll/zl3073x/dpll.c:22\ndrivers/dpll/zl3073x/dpll.h:3\ndrivers/dpll/zl3073x/flash.h:3\ndrivers/dpll/zl3073x/fw.c:31\ndrivers/dpll/zl3073x/fw.h:3\ndrivers/dpll/zl3073x/i2c.c:1\ndrivers/dpll/zl3073x/out.h:3\ndrivers/dpll/zl3073x/prop.c:1\ndrivers/dpll/zl3073x/prop.h:3\ndrivers/dpll/zl3073x/ref.h:3\ndrivers/dpll/zl3073x/regs.h:25\ndrivers/dpll/zl3073x/spi.c:1\ndrivers/dpll/zl3073x/synth.h:3\n\ndrivers/dpll/Makefile=11=obj-$(CONFIG_SIT9531X_DPLL)\t+= sit9531x/\ndrivers/dpll/Makefile:12:obj-$(CONFIG_ZL3073X)\t+= zl3073x/\n--\ndrivers/dpll/zl3073x/Kconfig-2-\ndrivers/dpll/zl3073x/Kconfig:3:config ZL3073X\ndrivers/dpll/zl3073x/Kconfig-4-\ttristate \"Microchip Azurite DPLL/PTP/SyncE devices\" if COMPILE_TEST\n--\ndrivers/dpll/zl3073x/Kconfig-16-\ndrivers/dpll/zl3073x/Kconfig:17:config ZL3073X_I2C\ndrivers/dpll/zl3073x/Kconfig-18-\ttristate \"I2C bus implementation for Microchip Azurite devices\"\n--\ndrivers/dpll/zl3073x/Kconfig-20-\tselect REGMAP_I2C\ndrivers/dpll/zl3073x/Kconfig:21:\tselect ZL3073X\ndrivers/dpll/zl3073x/Kconfig-22-\thelp\n--\ndrivers/dpll/zl3073x/Kconfig-28-\ndrivers/dpll/zl3073x/Kconfig:29:config ZL3073X_SPI\ndrivers/dpll/zl3073x/Kconfig-30-\ttristate \"SPI bus implementation for Microchip Azurite devices\"\n--\ndrivers/dpll/zl3073x/Kconfig-32-\tselect REGMAP_SPI\ndrivers/dpll/zl3073x/Kconfig:33:\tselect ZL3073X\ndrivers/dpll/zl3073x/Kconfig-34-\thelp\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\\\n--\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.h-2-\ndrivers/dpll/zl3073x/chan.h:3:#ifndef _ZL3073X_CHAN_H\ndrivers/dpll/zl3073x/chan.h:4:#define _ZL3073X_CHAN_H\ndrivers/dpll/zl3073x/chan.h-5-\n--\ndrivers/dpll/zl3073x/chan.h=25=struct zl3073x_chan {\n--\ndrivers/dpll/zl3073x/chan.h-28-\t\tu8\tmode_refsel;\ndrivers/dpll/zl3073x/chan.h:29:\t\tu8\tref_prio[ZL3073X_NUM_REFS / 2];\ndrivers/dpll/zl3073x/chan.h-30-\t);\n--\ndrivers/dpll/zl3073x/chan.h=263=static inline u8 zl3073x_chan_refsel_ref_get(const struct zl3073x_chan *chan)\n--\ndrivers/dpll/zl3073x/chan.h-267-\ndrivers/dpll/zl3073x/chan.h:268:#endif /* _ZL3073X_CHAN_H */\n--\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),\ndrivers/dpll/zl3073x/core.c:28:\tZL_CHIP_INFO(0x0E3B, 3, ZL3073X_FLAG_REF_PHASE_COMP_32),\ndrivers/dpll/zl3073x/core.c:29:\tZL_CHIP_INFO(0x0E93, 1, ZL3073X_FLAG_REF_PHASE_COMP_32),\ndrivers/dpll/zl3073x/core.c:30:\tZL_CHIP_INFO(0x0E94, 2, ZL3073X_FLAG_REF_PHASE_COMP_32),\ndrivers/dpll/zl3073x/core.c:31:\tZL_CHIP_INFO(0x0E95, 3, ZL3073X_FLAG_REF_PHASE_COMP_32),\ndrivers/dpll/zl3073x/core.c:32:\tZL_CHIP_INFO(0x0E96, 4, ZL3073X_FLAG_REF_PHASE_COMP_32),\ndrivers/dpll/zl3073x/core.c:33:\tZL_CHIP_INFO(0x0E97, 5, ZL3073X_FLAG_REF_PHASE_COMP_32),\ndrivers/dpll/zl3073x/core.c:34:\tZL_CHIP_INFO(0x1E93, 1, ZL3073X_FLAG_DIE_TEMP),\ndrivers/dpll/zl3073x/core.c:35:\tZL_CHIP_INFO(0x1E94, 2, ZL3073X_FLAG_DIE_TEMP),\ndrivers/dpll/zl3073x/core.c:36:\tZL_CHIP_INFO(0x1E95, 3, ZL3073X_FLAG_DIE_TEMP),\ndrivers/dpll/zl3073x/core.c:37:\tZL_CHIP_INFO(0x1E96, 4, ZL3073X_FLAG_DIE_TEMP),\ndrivers/dpll/zl3073x/core.c:38:\tZL_CHIP_INFO(0x1E97, 5, ZL3073X_FLAG_DIE_TEMP),\ndrivers/dpll/zl3073x/core.c:39:\tZL_CHIP_INFO(0x1F60, 2, ZL3073X_FLAG_REF_PHASE_COMP_32),\ndrivers/dpll/zl3073x/core.c:40:\tZL_CHIP_INFO(0x2E93, 1, ZL3073X_FLAG_DIE_TEMP),\ndrivers/dpll/zl3073x/core.c:41:\tZL_CHIP_INFO(0x2E94, 2, ZL3073X_FLAG_DIE_TEMP),\ndrivers/dpll/zl3073x/core.c:42:\tZL_CHIP_INFO(0x2E95, 3, ZL3073X_FLAG_DIE_TEMP),\ndrivers/dpll/zl3073x/core.c:43:\tZL_CHIP_INFO(0x2E96, 4, ZL3073X_FLAG_DIE_TEMP),\ndrivers/dpll/zl3073x/core.c:44:\tZL_CHIP_INFO(0x2E97, 5, ZL3073X_FLAG_DIE_TEMP),\ndrivers/dpll/zl3073x/core.c:45:\tZL_CHIP_INFO(0x3FC4, 2, ZL3073X_FLAG_DIE_TEMP),\ndrivers/dpll/zl3073x/core.c-46-};\n--\ndrivers/dpll/zl3073x/core.c=73=const struct regmap_config zl3073x_regmap_config = {\n--\ndrivers/dpll/zl3073x/core.c-81-};\ndrivers/dpll/zl3073x/core.c:82:EXPORT_SYMBOL_NS_GPL(zl3073x_regmap_config, \"ZL3073X\");\ndrivers/dpll/zl3073x/core.c-83-\n--\ndrivers/dpll/zl3073x/core.c=509=zl3073x_dev_state_fetch(struct zl3073x_dev *zldev)\n--\ndrivers/dpll/zl3073x/core.c-518-\ndrivers/dpll/zl3073x/core.c:519:\tfor (i = 0; i \u003c ZL3073X_NUM_REFS; i++) {\ndrivers/dpll/zl3073x/core.c-520-\t\trc = zl3073x_ref_state_fetch(zldev, i);\n--\ndrivers/dpll/zl3073x/core.c-528-\ndrivers/dpll/zl3073x/core.c:529:\tfor (i = 0; i \u003c ZL3073X_NUM_SYNTHS; i++) {\ndrivers/dpll/zl3073x/core.c-530-\t\trc = zl3073x_synth_state_fetch(zldev, i);\n--\ndrivers/dpll/zl3073x/core.c-538-\ndrivers/dpll/zl3073x/core.c:539:\tfor (i = 0; i \u003c ZL3073X_NUM_OUTS; i++) {\ndrivers/dpll/zl3073x/core.c-540-\t\trc = zl3073x_out_state_fetch(zldev, i);\n--\ndrivers/dpll/zl3073x/core.c=563=zl3073x_dev_ref_states_update(struct zl3073x_dev *zldev)\n--\ndrivers/dpll/zl3073x/core.c-566-\ndrivers/dpll/zl3073x/core.c:567:\tfor (i = 0; i \u003c ZL3073X_NUM_REFS; i++) {\ndrivers/dpll/zl3073x/core.c-568-\t\trc = zl3073x_ref_state_update(zldev, i);\n--\ndrivers/dpll/zl3073x/core.c=684=zl3073x_ref_freq_meas_update(struct zl3073x_dev *zldev)\n--\ndrivers/dpll/zl3073x/core.c-692-\t/* Read measured frequencies in Hz (unsigned 32-bit, LSB = 1 Hz) */\ndrivers/dpll/zl3073x/core.c:693:\tfor (i = 0; i \u003c ZL3073X_NUM_REFS; i++) {\ndrivers/dpll/zl3073x/core.c-694-\t\tu32 value;\n--\ndrivers/dpll/zl3073x/core.c=986=int zl3073x_dev_probe(struct zl3073x_dev *zldev)\n--\ndrivers/dpll/zl3073x/core.c-1066-}\ndrivers/dpll/zl3073x/core.c:1067:EXPORT_SYMBOL_NS_GPL(zl3073x_dev_probe, \"ZL3073X\");\ndrivers/dpll/zl3073x/core.c-1068-\n--\ndrivers/dpll/zl3073x/core.h-2-\ndrivers/dpll/zl3073x/core.h:3:#ifndef _ZL3073X_CORE_H\ndrivers/dpll/zl3073x/core.h:4:#define _ZL3073X_CORE_H\ndrivers/dpll/zl3073x/core.h-5-\n--\ndrivers/dpll/zl3073x/core.h=34=enum zl3073x_flags {\ndrivers/dpll/zl3073x/core.h:35:\tZL3073X_FLAG_REF_PHASE_COMP_32_BIT,\ndrivers/dpll/zl3073x/core.h:36:\tZL3073X_FLAG_DIE_TEMP_BIT,\ndrivers/dpll/zl3073x/core.h:37:\tZL3073X_FLAGS_NBITS /* must be last */\ndrivers/dpll/zl3073x/core.h-38-};\ndrivers/dpll/zl3073x/core.h-39-\ndrivers/dpll/zl3073x/core.h:40:#define __ZL3073X_FLAG(name)\tBIT(ZL3073X_FLAG_ ## name ## _BIT)\ndrivers/dpll/zl3073x/core.h:41:#define ZL3073X_FLAG_REF_PHASE_COMP_32\t__ZL3073X_FLAG(REF_PHASE_COMP_32)\ndrivers/dpll/zl3073x/core.h:42:#define ZL3073X_FLAG_DIE_TEMP\t\t__ZL3073X_FLAG(DIE_TEMP)\ndrivers/dpll/zl3073x/core.h-43-\n--\ndrivers/dpll/zl3073x/core.h=76=struct zl3073x_dev {\n--\ndrivers/dpll/zl3073x/core.h-84-\t/* Invariants */\ndrivers/dpll/zl3073x/core.h:85:\tstruct zl3073x_ref\tref[ZL3073X_NUM_REFS];\ndrivers/dpll/zl3073x/core.h:86:\tstruct zl3073x_out\tout[ZL3073X_NUM_OUTS];\ndrivers/dpll/zl3073x/core.h:87:\tstruct zl3073x_synth\tsynth[ZL3073X_NUM_SYNTHS];\ndrivers/dpll/zl3073x/core.h:88:\tstruct zl3073x_chan\tchan[ZL3073X_MAX_CHANNELS];\ndrivers/dpll/zl3073x/core.h-89-\n--\ndrivers/dpll/zl3073x/core.h=181=zl3073x_dev_is_ref_phase_comp_32bit(struct zl3073x_dev *zldev)\ndrivers/dpll/zl3073x/core.h-182-{\ndrivers/dpll/zl3073x/core.h:183:\treturn zldev-\u003einfo-\u003eflags \u0026 ZL3073X_FLAG_REF_PHASE_COMP_32;\ndrivers/dpll/zl3073x/core.h-184-}\n--\ndrivers/dpll/zl3073x/core.h=412=zl3073x_dev_output_pin_is_enabled(struct zl3073x_dev *zldev, u8 id)\n--\ndrivers/dpll/zl3073x/core.h-448-\ndrivers/dpll/zl3073x/core.h:449:#endif /* _ZL3073X_CORE_H */\n--\ndrivers/dpll/zl3073x/devlink.c=293=struct zl3073x_dev *zl3073x_devm_alloc(struct device *dev)\n--\ndrivers/dpll/zl3073x/devlink.c-313-}\ndrivers/dpll/zl3073x/devlink.c:314:EXPORT_SYMBOL_NS_GPL(zl3073x_devm_alloc, \"ZL3073X\");\ndrivers/dpll/zl3073x/devlink.c-315-\n--\ndrivers/dpll/zl3073x/devlink.h-2-\ndrivers/dpll/zl3073x/devlink.h:3:#ifndef _ZL3073X_DEVLINK_H\ndrivers/dpll/zl3073x/devlink.h:4:#define _ZL3073X_DEVLINK_H\ndrivers/dpll/zl3073x/devlink.h-5-\n--\ndrivers/dpll/zl3073x/devlink.h=12=void zl3073x_devlink_flash_notify(struct zl3073x_dev *zldev, const char *msg,\n--\ndrivers/dpll/zl3073x/devlink.h-14-\ndrivers/dpll/zl3073x/devlink.h:15:#endif /* _ZL3073X_DEVLINK_H */\n--\ndrivers/dpll/zl3073x/dpll.c-25-\ndrivers/dpll/zl3073x/dpll.c:26:#define ZL3073X_DPLL_REF_NONE\t\tZL3073X_NUM_REFS\ndrivers/dpll/zl3073x/dpll.c:27:#define ZL3073X_DPLL_REF_IS_VALID(_ref)\t((_ref) != ZL3073X_DPLL_REF_NONE)\ndrivers/dpll/zl3073x/dpll.c-28-\n--\ndrivers/dpll/zl3073x/dpll.c=91=zl3073x_dpll_is_nco_pin(struct zl3073x_dpll_pin *pin)\ndrivers/dpll/zl3073x/dpll.c-92-{\ndrivers/dpll/zl3073x/dpll.c:93:\treturn pin-\u003eid == ZL3073X_NCO_PIN_ID;\ndrivers/dpll/zl3073x/dpll.c-94-}\n--\ndrivers/dpll/zl3073x/dpll.c=424=zl3073x_dpll_input_pin_frequency_set(const struct dpll_pin *dpll_pin,\n--\ndrivers/dpll/zl3073x/dpll.c-454- *\ndrivers/dpll/zl3073x/dpll.c:455: * Return: reference index if locked, ZL3073X_DPLL_REF_NONE otherwise\ndrivers/dpll/zl3073x/dpll.c-456- */\n--\ndrivers/dpll/zl3073x/dpll.c=458=zl3073x_dpll_connected_ref_get(struct zl3073x_dpll *zldpll)\n--\ndrivers/dpll/zl3073x/dpll.c-468-\ndrivers/dpll/zl3073x/dpll.c:469:\treturn ZL3073X_DPLL_REF_NONE;\ndrivers/dpll/zl3073x/dpll.c-470-}\n--\ndrivers/dpll/zl3073x/dpll.c=473=zl3073x_dpll_input_pin_phase_offset_get(const struct dpll_pin *dpll_pin,\n--\ndrivers/dpll/zl3073x/dpll.c-508-\t */\ndrivers/dpll/zl3073x/dpll.c:509:\tif (ZL3073X_DPLL_REF_IS_VALID(conn_id) \u0026\u0026 conn_id != ref_id) {\ndrivers/dpll/zl3073x/dpll.c-510-\t\tu32 conn_freq, ref_freq;\n--\ndrivers/dpll/zl3073x/dpll.c=1423=zl3073x_dpll_mode_set(const struct dpll_device *dpll, void *dpll_priv,\n--\ndrivers/dpll/zl3073x/dpll.c-1445-\t\t */\ndrivers/dpll/zl3073x/dpll.c:1446:\t\tif (ZL3073X_DPLL_REF_IS_VALID(ref))\ndrivers/dpll/zl3073x/dpll.c-1447-\t\t\thw_mode = ZL_DPLL_MODE_REFSEL_MODE_REFLOCK;\n--\ndrivers/dpll/zl3073x/dpll.c-1460-\t\t */\ndrivers/dpll/zl3073x/dpll.c:1461:\t\tif (ZL3073X_DPLL_REF_IS_VALID(ref) \u0026\u0026\ndrivers/dpll/zl3073x/dpll.c-1462-\t\t !zl3073x_chan_ref_is_selectable(\u0026chan, ref)) {\n--\ndrivers/dpll/zl3073x/dpll.c-1476-\tzl3073x_chan_mode_set(\u0026chan, hw_mode);\n\n"
} |
| 7/2 | 2026/10/09 21:59 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 133763 output: 21 thoughts: 3084 Thoughts: **Analysis of DPLL Driver Reachability for Fuzzing** Alright, let's break this down. My goal is to determine if the `drivers/dpll/` drivers, specifically `sit9531x`, are reachable for fuzzing in a standard QEMU or GCE environment. The first question is always: do these drivers rely on physical hardware? If so, we're likely out of luck. Let's look at `sit9531x`. I see it includes `dpll_core.c`, `dpll_netlink.c`, and `zl3073x` (we'll come back to that one). This driver is interesting because it's listed among drivers that use generic netlink. My first thought is: can this netlink interface be used to configure it without requiring any physical device? The `sit9531x` driver's Kconfig states it depends on I2C and NET, and it selects DPLL and REGMAP_I2C. The driver registers on-chip PLLs as DPLL devices and exposes clocks as DPLL pins, allowing runtime configuration via Generic Netlink. Ah, the DPLL subsystem *does* expose netlink commands when a driver calls `dpll_device_register()` and `dpll_pin_register()`. This sounds promising, *but*...does the driver ever call those registration functions *unless* the probe is successful? We look at the code: `sit9531x_i2c_driver.probe` calls `sit9531x_i2c_probe`, which is an `i2c_driver`. To get this driver to run, an I2C client matching `sitime,sit95316` or `sitime,sit95317` or `sit9531x` must exist on an I2C adapter. The probe requires a clock "xtal" or a "clock-frequency" device property. Critically, it then needs to *read* a specific register (0x02 on page 0) via I2C, and it *must* return 0x17 or 0x31. *If* either of these reads fails, or returns the wrong data, the probe fails with `-ENODEV` (or I/O error). If the probe fails, we NEVER reach `sit9531x_devm_dpll_init`. This is where it gets interesting: looking inside `sit9531x_dev_probe`, it checks `xtal_freq`. Then, it reads `variant_id` from I2C. If this fails, or is not 0x17/0x31, it returns `-ENODEV`. Then it calls `sit9531x_devm_dpll_init` which calls `sit9531x_dev_start`, which fetches the device state by reading a *ton* of registers via I2C. If ANY of those reads fail, the probe *fails*. So, the crux: this hardware is unreachable in standard QEMU/GCE, it has to be connected to an I2C bus and requires a particular silicon chip, with certain values in registers. It requires an actual chip, and that chip has to respond to reads. Can we get around it? Can we use virtual buses to reach this hardware? No - no matter what bus or transport is used to get the commands to the driver, the commands would ultimately be trying to read from a non-existent I2C bus and chip. Could a USB-I2C adapter exist in QEMU? Yes, but you would still need an actual chip at the other end of the bus for the `sit9531x` driver to succeed. The Device Tree wouldn't help, either. The netlink interface is tantalizing, but the netlink calls are only going to work if `sit9531x` registers its DPLL devices in the first place, and that registration *only* happens within `sit9531x_dev_probe`, which only occurs after the I2C chip check. If no devices are registered, the netlink calls return `-ENODEV` or `-ENOENT`, and none of the `sit9531x`'s netlink callbacks are ever reachable! I double check and it is an I2C driver, nothing else. Therefore, since we can't emulate the required I2C chip in standard QEMU or GCE and the driver is only accessible via physical hardware, `sit9531x` and other DPLL drivers that operate similarly are not reachable for fuzzing, based on the stated |
| 8/2 | 2026/10/09 22:00 | tool | grepper |
0mArgs:{
"Expression": "dpll_device_register"
}Results: {
"Output": "Documentation/driver-api/dpll.rst=568=Device should implement set of operations and register device via\nDocumentation/driver-api/dpll.rst:569:dpll_device_register() at which point it becomes available to the\nDocumentation/driver-api/dpll.rst-570-users. Multiple driver instances can obtain reference to it with\n--\nDocumentation/driver-api/dpll.rst=634=The registration part is then looks like this part:\n--\nDocumentation/driver-api/dpll.rst-645-\nDocumentation/driver-api/dpll.rst:646: err = dpll_device_register(bp-\u003edpll, DPLL_TYPE_PPS, \u0026dpll_ops, bp);\nDocumentation/driver-api/dpll.rst-647- if (err)\n--\ndrivers/dpll/dpll_core.c=448=dpll_device_registration_find(struct dpll_device *dpll,\n--\ndrivers/dpll/dpll_core.c-460-/**\ndrivers/dpll/dpll_core.c:461: * dpll_device_register - register the dpll device in the subsystem\ndrivers/dpll/dpll_core.c-462- * @dpll: pointer to a dpll\n--\ndrivers/dpll/dpll_core.c-473- */\ndrivers/dpll/dpll_core.c:474:int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,\ndrivers/dpll/dpll_core.c-475-\t\t\t const struct dpll_device_ops *ops, void *priv)\n--\ndrivers/dpll/dpll_core.c-517-}\ndrivers/dpll/dpll_core.c:518:EXPORT_SYMBOL_GPL(dpll_device_register);\ndrivers/dpll/dpll_core.c-519-\n--\ndrivers/dpll/dpll_netlink.c=51=static bool dpll_pin_available(struct dpll_pin *pin)\n--\ndrivers/dpll/dpll_netlink.c-68-\ndrivers/dpll/dpll_netlink.c:69:static bool dpll_device_registered(struct dpll_device *dpll)\ndrivers/dpll/dpll_netlink.c-70-{\n--\ndrivers/dpll/dpll_netlink.c=74=static struct dpll_pin_ref *dpll_pin_first_registered_ref(struct dpll_pin *pin)\n--\ndrivers/dpll/dpll_netlink.c-79-\txa_for_each(\u0026pin-\u003edpll_refs, i, ref)\ndrivers/dpll/dpll_netlink.c:80:\t\tif (dpll_device_registered(ref-\u003edpll))\ndrivers/dpll/dpll_netlink.c-81-\t\t\treturn ref;\n--\ndrivers/dpll/dpll_netlink.c=666=dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin,\n--\ndrivers/dpll/dpll_netlink.c-674-\txa_for_each(\u0026pin-\u003edpll_refs, index, ref) {\ndrivers/dpll/dpll_netlink.c:675:\t\tif (!dpll_device_registered(ref-\u003edpll))\ndrivers/dpll/dpll_netlink.c-676-\t\t\tcontinue;\n--\ndrivers/dpll/dpll_netlink.c=713=dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,\n--\ndrivers/dpll/dpll_netlink.c-720-\tref = dpll_pin_own_dpll_ref_first(pin);\ndrivers/dpll/dpll_netlink.c:721:\tif (!ref || !dpll_device_registered(ref-\u003edpll))\ndrivers/dpll/dpll_netlink.c-722-\t\tref = dpll_pin_first_registered_ref(pin);\n--\ndrivers/dpll/dpll_netlink.c=1097=dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,\n--\ndrivers/dpll/dpll_netlink.c-1111-\tref = dpll_pin_own_dpll_ref_first(pin);\ndrivers/dpll/dpll_netlink.c:1112:\tif (!ref || !dpll_device_registered(ref-\u003edpll)) {\ndrivers/dpll/dpll_netlink.c-1113-\t\tNL_SET_ERR_MSG(extack, \"pin owner dpll not found\");\n--\ndrivers/dpll/dpll_netlink.c=1146=dpll_pin_esync_set(struct dpll_pin *pin, struct nlattr *a,\n--\ndrivers/dpll/dpll_netlink.c-1157-\tref = dpll_pin_own_dpll_ref_first(pin);\ndrivers/dpll/dpll_netlink.c:1158:\tif (!ref || !dpll_device_registered(ref-\u003edpll)) {\ndrivers/dpll/dpll_netlink.c-1159-\t\tNL_SET_ERR_MSG(extack, \"pin owner dpll not found\");\n--\ndrivers/dpll/dpll_netlink.c=1200=dpll_pin_ref_sync_state_set(struct dpll_pin *pin,\n--\ndrivers/dpll/dpll_netlink.c-1222-\tref = dpll_pin_own_dpll_ref_first(pin);\ndrivers/dpll/dpll_netlink.c:1223:\tif (!ref || !dpll_device_registered(ref-\u003edpll)) {\ndrivers/dpll/dpll_netlink.c-1224-\t\tNL_SET_ERR_MSG(extack, \"pin owner dpll not found\");\n--\ndrivers/dpll/dpll_netlink.c=1418=dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr,\n--\ndrivers/dpll/dpll_netlink.c-1441-\tref = dpll_pin_own_dpll_ref_first(pin);\ndrivers/dpll/dpll_netlink.c:1442:\tif (!ref || !dpll_device_registered(ref-\u003edpll)) {\ndrivers/dpll/dpll_netlink.c-1443-\t\tNL_SET_ERR_MSG(extack, \"pin owner dpll not found\");\n--\ndrivers/dpll/sit9531x/core.c=4827=static int sit9531x_dpll_pins_register(struct sit9531x_dpll *sitdpll)\n--\ndrivers/dpll/sit9531x/core.c-4877-\ndrivers/dpll/sit9531x/core.c:4878:static int sit9531x_dpll_device_register(struct sit9531x_dpll *sitdpll)\ndrivers/dpll/sit9531x/core.c-4879-{\n--\ndrivers/dpll/sit9531x/core.c-4892-\ndrivers/dpll/sit9531x/core.c:4893:\trc = dpll_device_register(sitdpll-\u003edpll_dev,\ndrivers/dpll/sit9531x/core.c-4894-\t\t\t\t sit9531x_prop_dpll_type_get(sitdev,\n--\ndrivers/dpll/sit9531x/core.c=4951=int sit9531x_dpll_register(struct sit9531x_dpll *sitdpll)\n--\ndrivers/dpll/sit9531x/core.c-4954-\ndrivers/dpll/sit9531x/core.c:4955:\trc = sit9531x_dpll_device_register(sitdpll);\ndrivers/dpll/sit9531x/core.c-4956-\tif (rc)\n--\ndrivers/dpll/zl3073x/dpll.c=1932=zl3073x_dpll_pins_register(struct zl3073x_dpll *zldpll)\n--\ndrivers/dpll/zl3073x/dpll.c-1982-/**\ndrivers/dpll/zl3073x/dpll.c:1983: * zl3073x_dpll_device_register - register DPLL device\ndrivers/dpll/zl3073x/dpll.c-1984- * @zldpll: pointer to zl3073x_dpll structure\n--\ndrivers/dpll/zl3073x/dpll.c=1990=static int\ndrivers/dpll/zl3073x/dpll.c:1991:zl3073x_dpll_device_register(struct zl3073x_dpll *zldpll)\ndrivers/dpll/zl3073x/dpll.c-1992-{\n--\ndrivers/dpll/zl3073x/dpll.c-2009-\tzldpll-\u003etype = zl3073x_prop_dpll_type_get(zldev, zldpll-\u003eid);\ndrivers/dpll/zl3073x/dpll.c:2010:\trc = dpll_device_register(zldpll-\u003edpll_dev, zldpll-\u003etype,\ndrivers/dpll/zl3073x/dpll.c-2011-\t\t\t\t \u0026zldpll-\u003eops, zldpll);\n--\ndrivers/dpll/zl3073x/dpll.c-2024- * Unregisters given DPLL device from DPLL sub-system previously registered\ndrivers/dpll/zl3073x/dpll.c:2025: * by @zl3073x_dpll_device_register.\ndrivers/dpll/zl3073x/dpll.c-2026- */\n--\ndrivers/dpll/zl3073x/dpll.c=2830=zl3073x_dpll_register(struct zl3073x_dpll *zldpll)\n--\ndrivers/dpll/zl3073x/dpll.c-2833-\ndrivers/dpll/zl3073x/dpll.c:2834:\trc = zl3073x_dpll_device_register(zldpll);\ndrivers/dpll/zl3073x/dpll.c-2835-\tif (rc)\n--\ndrivers/net/ethernet/intel/ice/ice_dpll.c=4555=ice_dpll_init_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu,\n--\ndrivers/net/ethernet/intel/ice/ice_dpll.c-4576-\t\t\tice_dpll_update_state(pf, d, true);\ndrivers/net/ethernet/intel/ice/ice_dpll.c:4577:\t\tret = dpll_device_register(d-\u003edpll, type, ops, d);\ndrivers/net/ethernet/intel/ice/ice_dpll.c-4578-\t\tif (ret) {\n--\ndrivers/net/ethernet/mellanox/mlx5/core/dpll.c=420=static int mlx5_dpll_probe(struct auxiliary_device *adev,\n--\ndrivers/net/ethernet/mellanox/mlx5/core/dpll.c-451-\ndrivers/net/ethernet/mellanox/mlx5/core/dpll.c:452:\terr = dpll_device_register(mdpll-\u003edpll, DPLL_TYPE_EEC,\ndrivers/net/ethernet/mellanox/mlx5/core/dpll.c-453-\t\t\t\t \u0026mlx5_dpll_device_ops, mdpll);\n--\ndrivers/ptp/ptp_ocp.c=5953=ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)\n--\ndrivers/ptp/ptp_ocp.c-6027-\ndrivers/ptp/ptp_ocp.c:6028:\terr = dpll_device_register(bp-\u003edpll, DPLL_TYPE_PPS, \u0026dpll_ops, bp);\ndrivers/ptp/ptp_ocp.c-6029-\tif (err)\n--\ndrivers/ptp/ptp_ocp.c-6055-\t}\ndrivers/ptp/ptp_ocp.c:6056:\t/* dpll_device_register() takes a second reference and stores bp; only\ndrivers/ptp/ptp_ocp.c-6057-\t * dpll_device_unregister() drops it. Without this the device stays\n--\ninclude/linux/dpll.h=280=void dpll_device_put(struct dpll_device *dpll, dpll_tracker *tracker);\ninclude/linux/dpll.h-281-\ninclude/linux/dpll.h:282:int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,\ninclude/linux/dpll.h-283-\t\t\t const struct dpll_device_ops *ops, void *priv);\n"
} |
| 9/2 | 2026/10/09 22:00 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 141617 output: 151 thoughts: 1236 Thoughts: **Triage Assessment: Sit9531x DPLL Driver** Okay, let's break this down. My initial observation is that this `dpll_device_register` call is used by various NIC hardware and a specific timecard, along with these Microchip and SiTime clock generator drivers. That's a good starting point. I see the focus here is the SiTime SiT9531x clock generator on I2C. This immediately raises a red flag; it's a *hardware* driver, specifically for a physical chip that requires I2C communication and device tree bindings for clock and pin mapping. The crucial point is this: there's *no* software emulation of the SiT9531x chip within standard QEMU or Google Compute Engine environments. Without the physical hardware, the driver's probe routine *will* fail. This failure means no DPLL device is registered, no DPLL pins are registered, no kernel workers are spun up, and, most importantly, *none* of the driver's operations are ever invoked. Essentially, the code path is dead in a virtualized setting. This aligns directly with the "Structurally unreachable hardware" criteria outlined in section 2. We're talking about a vendor-specific hardware chip (SiTime SiT9531x) that is *not* emulated in QEMU/GCE. The new driver introduces the usual components: the driver itself, DT bindings, and associated build system entries. No core kernel or DPLL subsystem code is modified, just a brand new driver to support this hardware. This clearly means the modified code is unreachable in a standard virtualized fuzzing context. Now, let's verify what we need for the `set-results` call. The parameters are: * `WorthFuzzing`: `false` (Unreachability) * `Reasoning`: Concise explanation for why the code is unreachable. * `FocusSymbols`: empty list `[]` (since `WorthFuzzing` is `false`) * `EnableConfigs`: empty list `[]` (or applicable configs, but empty when `WorthFuzzing=false`). I'll quickly double-check the file list from the git diff to be completely certain that there aren't any sneaky interactions or subtle reachability issues. The files touched are the documentation, MAINTAINERS, Kconfig/Makefile build system files, and the `drivers/dpll/sit9531x/` driver source. It's all consistent: the driver exclusively supports a real SiTime chip, requiring hardware clock inputs and I2C reads. If probe fails, which it will without the physical chip, all functionality remains unreachable. Yep, everything checks out. This is a clear case. Let's proceed with calling `set-results`. |
| 10/2 | 2026/10/09 22:00 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "This patch series introduces a new driver for the SiTime SiT95316/SiT95317 I2C-controlled DPLL clock generator chips along with Device Tree documentation. The driver requires physical SiTime hardware on an I2C bus with specific clock inputs and register responses, which is not emulated in standard QEMU/GCE virtualized environments. Because probe will fail immediately without the physical ASIC, no DPLL devices or pins are registered and the driver's runtime logic is structurally unreachable. No core subsystem or core DPLL code was modified.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "This patch series introduces a new driver for the SiTime SiT95316/SiT95317 I2C-controlled DPLL clock generator chips along with Device Tree documentation. The driver requires physical SiTime hardware on an I2C bus with specific clock inputs and register responses, which is not emulated in standard QEMU/GCE virtualized environments. Because probe will fail immediately without the physical ASIC, no DPLL devices or pins are registered and the driver's runtime logic is structurally unreachable. No core subsystem or core DPLL code was modified.",
"WorthFuzzing": false
} |
| 11/1 | 2026/10/09 22:00 | action | If |
0mArgs:{
"WorthFuzzing": false
}Results: null |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|