From: netdev-bot+sashiko@kernel.org
To: arouhi@sitime.com
Cc: jiri@resnulli.us, vadim.fedorenko@linux.dev,
arkadiusz.kubalewski@intel.com, ivecera@redhat.com,
robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
cjubran@nvidia.com, pabeni@redhat.com,
Oleg.Zadorozhnyi@devoxsoftware.com, devicetree@vger.kernel.org,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
kuba@kernel.org
Subject: Re: [PATCH net-next v9 10/15] dpll: sit9531x: add support to adjust output phase
Date: Thu, 17 Sep 2026 15:01:52 +0000 [thread overview]
Message-ID: <178965731236.22033.10465683343985463430@kernel.org> (raw)
In-Reply-To: <20260915000015.80480-11-arouhi@sitime.com>
Thank you for your contribution! Sashiko AI review found 6 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 5 · Low: 1
- [Medium] `struct sit9531x_out.phase_adj` is a pure software cache that
is never seeded from the hardware.
- [Medium] A per-pin phase-adjust request ends with
`sit9531x_output_phase_flush(sitdev, pll_idx)`, which arms…
- [Medium] `sit9531x_output_phase_adjust_set()` commits the new
PRG_RST_DELAY bytes to the device and only afterwards updates its…
- [Medium] `sit9531x_output_freq_set()` now returns the result of the
added phase-adjust re-encode.
- [Medium] `sit9531x_output_phase_adjust_set()` derives the output period
from the cached `sitdev->out[out_idx].freq` and only falls back to a…
- [Low] Concern that the block comment above
`sit9531x_output_phase_adjust_set()` in core.c documents page/base…
--
dpll: sit9531x: add support to adjust output phase
This adds phase_adjust_get/phase_adjust_set to the sit9531x output pin ops,
programming the per-output PRG_RST_DELAY block (34-bit coarse in VCO cycles
plus a 3-bit fine field in 30 ps steps) from inside the PRG_CMD programming
state, and re-encoding a cached request when the output rate changes.
A few questions about the new code below.
> diff --git a/drivers/dpll/sit9531x/core.c b/drivers/dpll/sit9531x/core.c
> index bba42fe302a3b..8d857f1a0c890 100644
> --- a/drivers/dpll/sit9531x/core.c
> +++ b/drivers/dpll/sit9531x/core.c
> @@ -1641,7 +1641,17 @@ int sit9531x_output_freq_set(struct sit9531x_dev *sitdev, u8 out_idx,
>
> sitdev->out[out_idx].freq = div64_u64(fvco, divo);
>
> - return 0;
> + /*
> + * The programmed reset delay counts VCO cycles against the output
> + * period in force when it was written, so a rate change silently
> + * re-times a previously requested phase adjust. Re-encode the
> + * cached picosecond request against the new rate.
> + */
> + if (sitdev->out[out_idx].phase_adj)
> + rc = sit9531x_output_phase_adjust_set(sitdev, out_idx,
> + sitdev->out[out_idx].phase_adj);
> +
> + return rc;
> }
[Severity: Medium]
Can this turn a completed frequency change into a reported failure? By the
time this runs, sit9531x_output_divo_write(), sit9531x_prg_commit() and the
phase flush have all succeeded and sitdev->out[out_idx].freq already holds
the new rate.
Any error inside sit9531x_output_phase_adjust_set() (bus error in
sit9531x_prg_enter(), the read/write loops, prg_commit or the second flush,
or -ENODEV from sit9531x_get_fvco()) becomes the return value here, with no
rollback of DIVO, and sit9531x_dpll_output_pin_frequency_set() reports it as
"Output frequency set failed".
An identical retry does not repair it either, because dpll_pin_freq_set()
asks the driver for the current rate first:
drivers/dpll/dpll_netlink.c:dpll_pin_freq_set() {
...
if (freq == old_freq)
return 0;
...
}
and sit9531x_dpll_output_pin_frequency_set()'s companion get reads DIVO back
from hardware, so the second request never reaches the driver and the phase
re-encode that failed is never performed. For a negative cached adjust the
delay registers then stay encoded against the old period, since the
rendering T_out - |phase| depends on the period.
Would it be better to log the re-encode failure and still return success for
the frequency set?
> @@ -1731,6 +1741,206 @@ int sit9531x_output_freq_get(struct sit9531x_dev *sitdev, u8 out_idx,
> * output period, which is identical for a periodic signal.
> */
>
> +int sit9531x_output_phase_adjust_set(struct sit9531x_dev *sitdev,
> + u8 out_idx, s32 phase_ps)
> +{
[ ... ]
> + freq = sitdev->out[out_idx].freq;
> + if (!freq) {
> + /*
> + * The cache is only seeded by a DT frequency list or an
> + * earlier get/set; a board without supported-frequencies-hz
> + * would otherwise get -EINVAL on every phase request forever.
> + * Read the effective rate back from the divider chain.
> + */
> + rc = sit9531x_output_freq_get(sitdev, out_idx, &freq);
> + if (rc)
> + return rc;
> + if (!freq)
> + return -EINVAL;
> + }
[Severity: Medium]
The live divider is consulted only when the cache is zero. Can a non-zero
cached value be a rate the hardware never produced?
sit9531x_pin_props_get() in prop.c seeds it from the DT list rather than
from the device:
if (curr_freq) {
if (dir == DPLL_PIN_DIRECTION_INPUT)
sitdev->ref[index].freq = curr_freq;
else
sitdev->out[index].freq = curr_freq;
}
where curr_freq is the first entry of supported-frequencies-hz, and the
binding only enumerates the rates the output supports. The driver also
documents the cache as unreliable in sit9531x_output_freq_get():
* 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.
A wrong period lands directly in the encoding, because the negative path
computes abs_ps = t_out_ps - advance. With a true period of 40000 ps but
6400 ps assumed (156.25 MHz listed first), a request of -1000 ps programs a
5400 ps delay instead of 39000 ps, and success is returned. Positive
requests are mis-folded the same way once the request exceeds the assumed
period.
Nothing in dpll_pin_phase_adj_set() calls frequency_get() first, so a phase
request can be the very first operation on the pin. Should the period be
read back from DIVO on every phase update?
[ ... ]
> + slot = info->clkout_map[out_idx];
> + page = (slot > SIT9531X_PAGE_OUTSYS0_SLOT_MAX) ?
> + SIT9531X_PAGE_OUTSYS1 : SIT9531X_PAGE_OUTSYS0;
> + base = SIT9531X_OUT_PRG_DELAY_BASE +
> + SIT9531X_OUT_PRG_SLOT_STRIDE * (slot % 6);
[Severity: Low]
This isn't a bug, but the block comment immediately above this function
describes the same addressing in terms of the logical output index:
* Outputs 0-5 live on Page 3, outputs 6-11 on Page 4, with each
* output's block at base = 0x15 + 16 * (out_idx % 6).
while the code here and the new regs.h comment use the physical slot from
clkout_map[]. The code is the correct one of the two.
The wording is already corrected later in the same series, in "dpll:
sit9531x: model the inter-PLL sync net as a pair of pins", where it becomes
"Slots 0-5 live on Page 3, slots 6-11 on Page 4 ... not the logical output
index", so this only concerns this commit.
[ ... ]
> + for (i = 0; i < ARRAY_SIZE(new_bytes); i++) {
> + rc = sit9531x_write_u8(sitdev,
> + SIT9531X_REG(page, base + i),
> + new_bytes[i]);
> + if (rc)
> + goto rollback;
> + }
> +
> + goto commit;
[ ... ]
> +commit:
> + /*
> + * 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)
> + return rc;
[Severity: Medium]
When the five writes all succeeded and sit9531x_prg_commit() then fails,
this returns before the phase_adj assignment at the end of the function, and
nothing restores old_bytes. The same applies to the
sit9531x_output_phase_flush() failure below.
So the delay registers of that slot hold the new coarse/fine value while
sitdev->out[out_idx].phase_adj still holds the previous one, and only a
mid-write failure takes the rollback path.
Getting back to the reported value is then not possible through the
attribute, because the core drops a request equal to what the getter
reports:
drivers/dpll/dpll_netlink.c:dpll_pin_phase_adj_set() {
...
if (phase_adj == old_phase_adj)
return 0;
...
}
The unrecorded delay is also applied at the next output-divider phase flush
of that PLL, for instance during a frequency set on a sibling output, so the
phase step appears at a point the driver never recorded.
Should these two paths roll the delay bytes back, or update the cache to
what the registers now hold?
> + /*
> + * 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)
> + return rc;
[Severity: Medium]
Does a single-pin phase-adjust request disturb the sibling outputs of the
same PLL? sit9531x_output_phase_flush() arms
SIT9531X_PLL_PHFL_ON_DEMAND_EN on the PLL page and then pulses the global
trigger:
rc = sit9531x_write_u8(sitdev, SIT9531X_REG_GPIO_FUNC_CTRL1,
ctrl | SIT9531X_DIVO_PHASE_TRIG);
regs.h documents SIT9531X_DIVO_PHASE_TRIG as flushing the output phase with
no per-output selection, so the output-divider phase of every output fed by
that PLL looks like it gets restarted, i.e. an edge jump on the outputs that
were not adjusted.
On top of that, sit9531x_prg_enter() puts the whole output system into the
PRG_CMD state with the loops open, and sit9531x_prg_commit() re-locks them
after msleep(100), so each phase-adjust request also touches outputs of the
other PLLs.
The DPLL ABI models phase adjust as a per-pin property. Could the scope of
this operation be narrowed, or at least described in the commit message and
in a comment?
> diff --git a/drivers/dpll/sit9531x/dpll.c b/drivers/dpll/sit9531x/dpll.c
> index d550eb626e6de..78034b7f089cc 100644
> --- a/drivers/dpll/sit9531x/dpll.c
> +++ b/drivers/dpll/sit9531x/dpll.c
> @@ -815,12 +815,68 @@ sit9531x_dpll_output_pin_state_on_dpll_set(const struct dpll_pin *pin,
> return rc;
> }
>
> +/*
> + * sit9531x_dpll_output_pin_phase_adjust_get - read output phase adjustment
> + *
> + * Returns the applied phase cached in out->phase_adj, i.e. the value
> + * sit9531x_output_phase_adjust_set() actually programmed after quantization.
> + */
> +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;
> +
> + mutex_lock(&sitdev->multiop_lock);
> + *phase_adjust = sit9531x_out_state_get(sitdev, dpin->id)->phase_adj;
> + mutex_unlock(&sitdev->multiop_lock);
> +
> + return 0;
> +}
[Severity: Medium]
Is the value returned here ever reconciled with the device? phase_adj is
zeroed by the devm_kzalloc() in sit9531x_i2c_probe(),
sit9531x_out_state_fetch() reads output mode, forced Hi-Z and routing but
never the five PROG6..PROG2 delay bytes, and the only assignment is at the
tail of sit9531x_output_phase_adjust_set().
Since that setter commits with SIT9531X_UPDATE_NVM through
sit9531x_prg_commit(), and the delay fields are part of the profile the chip
loads before probe, the device can hold a non-zero delay while this reports
0 - after a module unload/reload, after a warm reboot, or on a board whose
profile programmed a delay.
A request of 0 ps meant to clear such a delay then never reaches the driver:
drivers/dpll/dpll_netlink.c:dpll_pin_phase_adj_set() {
...
if (phase_adj == old_phase_adj)
return 0;
...
}
and the re-encode added to sit9531x_output_freq_set() is gated on
if (sitdev->out[out_idx].phase_adj), so an unrecorded hardware delay is not
re-timed on a rate change either.
This also does not match the commit message:
the two and what the core reads back is what the registers hold rather
than what was asked for
nor the new kernel-doc "phase adjust the delay registers actually realize",
since the registers are never read for this purpose. This looks unchanged
at the end of the series.
Could phase_adjust_get() decode the PRG_RST_DELAY block, or the startup
state fetch seed phase_adj from it?
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260915000015.80480-1-arouhi%40sitime.com
next prev parent reply other threads:[~2026-09-17 15:02 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-15 0:00 [PATCH net-next v9 00/15] dpll: add SiTime SiT9531x DPLL clock driver Ali Rouhi
2026-09-15 0:00 ` [PATCH net-next v9 01/15] dt-bindings: vendor-prefixes: add SiTime Corporation Ali Rouhi
2026-09-15 0:00 ` [PATCH net-next v9 03/15] dpll: add basic SiTime SiT9531x support Ali Rouhi
2026-09-17 8:42 ` Ivan Vecera
2026-09-17 15:01 ` netdev-bot+sashiko
2026-09-15 0:00 ` [PATCH net-next v9 02/15] dt-bindings: dpll: add SiTime SiT95316 clock generator Ali Rouhi
2026-09-17 15:01 ` netdev-bot+sashiko
2026-09-15 0:00 ` [PATCH net-next v9 05/15] dpll: sit9531x: register DPLL devices and pins Ali Rouhi
2026-09-17 15:01 ` netdev-bot+sashiko
2026-09-15 0:00 ` [PATCH net-next v9 04/15] dpll: sit9531x: read DPLL types and pin properties from system firmware Ali Rouhi
2026-09-17 9:42 ` Ivan Vecera
2026-09-17 15:01 ` netdev-bot+sashiko
2026-09-15 0:00 ` [PATCH net-next v9 06/15] dpll: sit9531x: implement input pin state on a DPLL Ali Rouhi
2026-09-17 15:01 ` netdev-bot+sashiko
2026-09-15 0:00 ` [PATCH net-next v9 08/15] dpll: sit9531x: add support to get and set frequency on pins Ali Rouhi
2026-09-17 15:01 ` netdev-bot+sashiko
2026-09-15 0:00 ` [PATCH net-next v9 07/15] dpll: sit9531x: add support to get and set priority on input pins Ali Rouhi
2026-09-17 15:01 ` netdev-bot+sashiko
2026-09-15 0:00 ` [PATCH net-next v9 10/15] dpll: sit9531x: add support to adjust output phase Ali Rouhi
2026-09-17 9:55 ` Ivan Vecera
2026-09-17 15:01 ` netdev-bot+sashiko [this message]
2026-09-15 0:00 ` [PATCH net-next v9 09/15] dpll: sit9531x: implement output pin state on a DPLL Ali Rouhi
2026-09-17 15:01 ` netdev-bot+sashiko
2026-09-15 0:00 ` [PATCH net-next v9 12/15] dpll: sit9531x: add support to get phase offset on the connected input pin Ali Rouhi
2026-09-17 15:01 ` netdev-bot+sashiko
2026-09-15 0:00 ` [PATCH net-next v9 11/15] dpll: sit9531x: add support to get and set esync on pins Ali Rouhi
2026-09-17 15:01 ` netdev-bot+sashiko
2026-09-15 0:00 ` [PATCH net-next v9 13/15] dpll: sit9531x: add support to get fractional frequency offset Ali Rouhi
2026-09-17 15:01 ` netdev-bot+sashiko
2026-09-15 0:00 ` [PATCH net-next v9 14/15] dpll: sit9531x: model the inter-PLL sync net as a pair of pins Ali Rouhi
2026-09-17 15:01 ` netdev-bot+sashiko
2026-09-15 0:00 ` [PATCH net-next v9 15/15] dpll: sit9531x: allow the device tree to override two board facts Ali Rouhi
2026-09-17 15:02 ` netdev-bot+sashiko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=178965731236.22033.10465683343985463430@kernel.org \
--to=netdev-bot+sashiko@kernel.org \
--cc=Oleg.Zadorozhnyi@devoxsoftware.com \
--cc=arkadiusz.kubalewski@intel.com \
--cc=arouhi@sitime.com \
--cc=cjubran@nvidia.com \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=ivecera@redhat.com \
--cc=jiri@resnulli.us \
--cc=krzk+dt@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=robh@kernel.org \
--cc=vadim.fedorenko@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®