[PATCH net-next v9 00/15] dpll: add SiTime SiT9531x DPLL clock driver
From: Ali Rouhi
Date: Mon Sep 14 2026 - 20:00:45 EST
This series adds a DPLL subsystem driver for the SiTime SiT95316 and
SiT95317 I2C clock generators. Each device integrates four PLLs with
automatic reference selection and on-chip TDC phase-offset measurement,
and is used for synchronization in telecom, networking, and data-center
timing.
The series contains the device-tree binding, the driver under
drivers/dpll/sit9531x/, and the MAINTAINERS entry.
v1 https://lore.kernel.org/netdev/20260511211143.19792-1-arouhi@xxxxxxxxxx/
v2 https://lore.kernel.org/netdev/20260520191943.73938-1-arouhi@xxxxxxxxxx/
v3 https://lore.kernel.org/netdev/20260731180951.65725-1-arouhi@xxxxxxxxxx/
v4 https://lore.kernel.org/netdev/20260806232439.27551-1-arouhi@xxxxxxxxxx/
v5 https://lore.kernel.org/netdev/20260810230439.22866-1-arouhi@xxxxxxxxxx/
v6 https://lore.kernel.org/netdev/20260812175337.18155-1-arouhi@xxxxxxxxxx/
v7 https://lore.kernel.org/netdev/20260815221919.64226-1-arouhi@xxxxxxxxxx/
v8 https://lore.kernel.org/netdev/20260902214030.20955-1-arouhi@xxxxxxxxxx/
The driver was a single 5.6k-line patch through v7. Vadim and Ivan asked
for it to be split into logical parts, with the first zl3073x series as
the model, so v8 introduced it feature by feature and v9 keeps that
shape:
1-2 bindings: vendor prefix, then the device schema
3 basic support: paged regmap, variant detection, probe
4 DPLL types and pin properties from system firmware
5 register the DPLL devices and pins, and keep their state
6 input pin state on a DPLL, with the pin-state contract
7 input pin priority
8 pin frequency, both directions
9 output pin state (mute)
10 output phase adjust
11 esync on outputs
12 phase offset through the TDC
13 fractional frequency offset of the selected reference
14 the inter-PLL sync net as a pair of pins
15 optional device-tree overrides for two board facts
The two bindings patches come first, so the driver never matches on a
compatible string before the schema that describes it is in the tree.
Each of the thirteen driver patches builds and links on its own: no
patch calls something a later patch introduces, so a bisect cannot land
on a tree that fails to compile. For this posting that was re-checked
patch by patch with W=1 and with sparse. checkpatch --strict is clean
except for the "does MAINTAINERS need updating?" hint on patches 4 and
5, which add files under drivers/dpll/sit9531x/ -- patch 3 already
covers that directory with an F: entry.
On process, and on Paolo's note against v8: the v8 AI review raised 86
comments across the series, and each one now has an individual reply in
its own sub-thread -- fixed, or answered with the reason it is not a
bug. The second half of that guidance is the more useful half, and we
had not been following it. We now run the same review locally before
posting rather than after; v9 has been through it twice over the whole
series, and what it found is folded into the patches below instead of
being left for the bots to raise again. We intend to keep doing this for
any further revision.
Changes in v9:
- Put the cost and the constraints in the commit messages, next to the
code they apply to, rather than only in replies on the list. A
priority-table rewrite forces holdover and takes ten to twenty
milliseconds, because the device cannot change one slot in
isolation. Programming an output divider costs about a hundred
milliseconds under the DPLL core's global lock, most of it the
settling time the part requires after the loop-lock command. The
phase flush that follows a divider write realigns every output on
that PLL, not only the one that changed. The clock_id carries the
I2C adapter number in eight bits, so the collision it admits is
stated rather than designed around. The debug window returns the
previous latch, which is why every read of it triggers three times.
The running feedback divider is two taps sampled separately and can
tear; on a locked loop it moves below what the measurement resolves,
and the part offers no way to latch both at once.
- Never leave the device armed when a sequence fails partway. The
programming state, the debug window, the on-demand phase flush and
forced holdover are each entered by one write and left by another,
and every fallible step in between now exits through the common
path that issues the second write. The first error is the one
returned, so a transient I2C failure can no longer leave the chip
with its loops open, its debug block unlocked, its flush armed or a
PLL parked in holdover.
- Answer from the device rather than from a cache where the cache can
be wrong. Whether an output is muted now reads both the force bit
and the state bit, and picks the register bank from the pad's own
CMOS enables, so a single-ended pad is no longer interrogated
through the differential bank or the reverse. State that a write may
have invalidated is marked stale and re-read on the next get instead
of being reported from the last poll. Priority-table membership is
asked of the table.
- Make .mode_set() idempotent. It reads the outer-loop disable bit
before writing it and does nothing when the PLL is already in
automatic mode. Without that, setting the only mode the driver
advertises a second time, followed by one failed latch, would
restore a disable the PLL never had and drop a running loop into
free-run.
- Park the poll worker and the INTRB interrupt across system suspend
and take a fresh sample on resume. The tick talks to the device over
I2C, and one landing mid-suspend could tear a paged sequence between
the page-selector write and the register access.
- Check a PLL's VCO frequency against the device's two bands before
using it, so a value that fell in the gap between them cannot reach
the divider arithmetic.
- Binding: accept clock-frequency as an alternative to the clocks
phandle, for platforms whose firmware does not expose the crystal
through the clock framework; exactly one of the two is now required.
The sitime,pll-fvco description was also rewritten to say why the
property carries no unit suffix: the values exceed 32 bits and so
need an explicit uint64-array type, and the dtschema meta-schema
does not permit a type $ref on a "-hz" property. That was a review
suggestion we tried and dt_binding_check rejected, so the reason is
recorded in the binding itself. The schema changed, so Krzysztof's
Reviewed-by is not carried across it. Patch 1 is unchanged since v8
and keeps Conor's Acked-by.
One finding from the v8 review is declined rather than fixed, and the
reply on patch 8 gives the argument: a frequency request of 0 Hz is
refused with -EINVAL instead of being treated as a request to stop the
output. Nothing in the ABI says zero means off, and this device already
has a mute control that says so explicitly.
The use-after-free report on the pin properties remains a false
positive: dpll_pin_alloc() calls dpll_pin_prop_dup(), which copies
freq_supported with kmemdup() and the labels with kstrdup(), so the core
owns its copies and the driver has to free its own.
The review also turned up one bug that is not ours: dpll_pin_freq_set()
reads the requested frequency as a u64 and validates it through a helper
that takes a u32, so a rate of U32_MAX + 1 + N is accepted as N against
the supported ranges, which are themselves u64. That affects every
driver behind the interface, not just this one. It is a separate patch
against the core and will be posted on its own rather than buried in a
driver series; this driver range-checks its own input in the meantime.
Ali Rouhi (2):
dt-bindings: vendor-prefixes: add SiTime Corporation
dt-bindings: dpll: add SiTime SiT95316 clock generator
Oleg Zadorozhnyi (13):
dpll: add basic SiTime SiT9531x support
dpll: sit9531x: read DPLL types and pin properties from system
firmware
dpll: sit9531x: register DPLL devices and pins
dpll: sit9531x: implement input pin state on a DPLL
dpll: sit9531x: add support to get and set priority on input pins
dpll: sit9531x: add support to get and set frequency on pins
dpll: sit9531x: implement output pin state on a DPLL
dpll: sit9531x: add support to adjust output phase
dpll: sit9531x: add support to get and set esync on pins
dpll: sit9531x: add support to get phase offset on the connected input
pin
dpll: sit9531x: add support to get fractional frequency offset
dpll: sit9531x: model the inter-PLL sync net as a pair of pins
dpll: sit9531x: allow the device tree to override two board facts
.../bindings/dpll/sitime,sit95316.yaml | 181 +
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
MAINTAINERS | 7 +
drivers/dpll/Kconfig | 2 +
drivers/dpll/Makefile | 1 +
drivers/dpll/sit9531x/Kconfig | 17 +
drivers/dpll/sit9531x/Makefile | 4 +
drivers/dpll/sit9531x/core.c | 4130 +++++++++++++++++
drivers/dpll/sit9531x/core.h | 407 ++
drivers/dpll/sit9531x/dpll.c | 1486 ++++++
drivers/dpll/sit9531x/dpll.h | 70 +
drivers/dpll/sit9531x/prop.c | 437 ++
drivers/dpll/sit9531x/prop.h | 39 +
drivers/dpll/sit9531x/regs.h | 394 ++
14 files changed, 7177 insertions(+)
create mode 100644 Documentation/devicetree/bindings/dpll/sitime,sit95316.yaml
create mode 100644 drivers/dpll/sit9531x/Kconfig
create mode 100644 drivers/dpll/sit9531x/Makefile
create mode 100644 drivers/dpll/sit9531x/core.c
create mode 100644 drivers/dpll/sit9531x/core.h
create mode 100644 drivers/dpll/sit9531x/dpll.c
create mode 100644 drivers/dpll/sit9531x/dpll.h
create mode 100644 drivers/dpll/sit9531x/prop.c
create mode 100644 drivers/dpll/sit9531x/prop.h
create mode 100644 drivers/dpll/sit9531x/regs.h
base-commit: d0ec95a8a4e79f2fd6063fc8932415db8c227689
--
2.39.2 (Apple Git-143)