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, 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 v8 11/15] dpll: sit9531x: add support to get and set esync on pins
Date: Tue, 08 Sep 2026 12:45:21 +0000 [thread overview]
Message-ID: <178887152134.219967.16181634650893462486@kernel.org> (raw)
In-Reply-To: <20260902214030.20955-12-arouhi@sitime.com>
Thank you for your contribution! Sashiko AI review found 7 potential
issue(s) to consider:
Critical: 0 · High: 2 · Medium: 3 · Low: 2
- [High] Embedded-sync frequency is implemented as the output's carrier
frequency.
- [High] sit9531x_dpll_output_pin_esync_set() is asymmetric and destroys
the only record of the pin's previous rate.
- [Medium] sit9531x_dpll_output_pin_esync_get() reports esync state from
an unsynchronized software cache: it returns dpin->esync_freq and a…
- [Medium] The uAPI duty-cycle percentage constant is written verbatim
into a hardware register field.
- [Medium] The esync enable path chains three independently committed
hardware transactions with no rollback: sit9531x_output_freq_set()…
- [Low] sit9531x_dpll_output_pin_esync_get() reports esync->pulse = 50
unconditionally, including when it reports esync->freq == 0 (embedded…
- [Low] The new PROG0 write is wrapped in its own
prg_enter()/prg_commit() window even though PROG0 lives in the same…
--
dpll: sit9531x: add support to get and set esync on pins
This adds esync_get and esync_set callbacks to the sit9531x output pin ops
and a new core helper that programs the per-output ODRn_PROG0 pulse control
byte. Only outputs flagged as esync-control accept the operations, the
rest return -EOPNOTSUPP.
No Link: tags are present in the commit header.
A few questions below.
> diff --git a/drivers/dpll/sit9531x/core.c b/drivers/dpll/sit9531x/core.c
> index be033d7cfe7c..c93578f3b750 100644
> --- a/drivers/dpll/sit9531x/core.c
> +++ b/drivers/dpll/sit9531x/core.c
> @@ -1535,6 +1535,57 @@ int sit9531x_clear_notifications(struct sit9531x_dev *sitdev)
> return 0;
> }
>
> +/*
> + * sit9531x_output_pulse_ctrl_set - program per-output PULSE_CTRL byte
> + * @out_idx: logical output index (translated to chip slot internally)
> + * @pulse_ctrl: 8-bit PULSE_CTRL value (PROG0)
> + *
> + * Writes ODRn_PROG0 on the output page (Page 3 for slots 0..5,
> + * Page 4 for slots 6..11) at offset 0x1B + 16 * (slot % 6).
> + *
> + * Caller must hold sitdev->multiop_lock.
> + */
> +int sit9531x_output_pulse_ctrl_set(struct sit9531x_dev *sitdev,
> + u8 out_idx, u8 pulse_ctrl)
> +{
> + const struct sit9531x_chip_info *info = sitdev->info;
> + u8 slot, page, reg;
> + int rc, ret;
> +
> + lockdep_assert_held(&sitdev->multiop_lock);
> +
> + if (out_idx >= info->num_outputs)
> + return -EINVAL;
> +
> + slot = info->clkout_map[out_idx];
> + page = (slot > SIT9531X_PAGE_OUTSYS0_SLOT_MAX) ?
> + SIT9531X_PAGE_OUTSYS1 : SIT9531X_PAGE_OUTSYS0;
> + reg = SIT9531X_OUT_PROG0_BASE +
> + SIT9531X_OUT_PRG_SLOT_STRIDE * (slot % 6);
> +
> + /*
> + * PROG0 lives in the output system, so like the DIVO and
> + * PRG_RST_DELAY writes it only takes effect inside the PRG_CMD
> + * programming state committed to the NVM shadow.
> + */
> + rc = sit9531x_prg_enter(sitdev);
> + if (rc)
> + return rc;
> +
> + rc = sit9531x_write_u8(sitdev, SIT9531X_REG(page, reg), pulse_ctrl);
[Severity: Medium]
Is any conversion needed between the value passed in as pulse_ctrl and the
hardware field? The only caller passes SIT9531X_ESYNC_PULSE_DEFAULT, which
is decimal 50, and the same constant is handed to userspace as esync->pulse
in sit9531x_dpll_output_pin_esync_get(). The uAPI unit for that attribute
is percent:
Documentation/netlink/specs/dpll.yaml
name: esync-pulse
type: u32
doc: |
A ratio of high to low state of a SYNC signal pulse embedded
into base clock frequency. Value is in percents.
The new regs.h comment in this patch describes the same register as a pulse
count byte for SYSREF / SYNCB modes and defines no field masks, so it looks
like one constant is being used both as a uAPI percentage and as a raw
register value. Which of the two is it?
Separately, this is an unmasked full-byte write with no read-modify-write.
The neighbouring PROG6 programming in sit9531x_output_phase_adjust_set()
does preserve the upper bits:
/* PROG6 RMW: preserve OPSTG_VCASC_BUMP in [7:5] */
rc = sit9531x_read_u8(sitdev, SIT9531X_REG(page, base), &prog6_val);
...
prog6_val &= SIT9531X_OUT_PRG_OPSTG_MASK;
Can writing all eight bits of ODRn_PROG0 clear other fields in that byte
that the initial config blob programmed?
> +
> + /*
> + * Always leave the PRG_CMD state via prg_commit(), even if the write
> + * failed, 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;
> +
> + return rc;
> +}
[Severity: Low]
This isn't a bug, but the comment above states PROG0 shares the PRG_CMD
programming state with the DIVO and PRG_RST_DELAY writes, so could this
byte be written inside the window sit9531x_output_freq_set() already opens?
As written, one call to sit9531x_dpll_output_pin_esync_set() runs three
separate windows:
sit9531x_output_freq_set() -> prg_enter()/prg_commit()
sit9531x_output_pulse_ctrl_set() -> prg_enter()/prg_commit()
sit9531x_output_enable() -> prg_enter()/prg_commit()
and sit9531x_prg_commit() issues UPDATE_NVM, then LOOP_LOCK, then an
unconditional msleep(100), so that is roughly 300 ms of multiop_lock hold
time plus three unlock/relock cycles of the output loops on a live output.
> /*
> * sit9531x_ref_state_fetch - read input reference status from hardware
> * @index: logical input index
> diff --git a/drivers/dpll/sit9531x/dpll.c b/drivers/dpll/sit9531x/dpll.c
> index c07fb2256510..76d7d7d21d3d 100644
> --- a/drivers/dpll/sit9531x/dpll.c
> +++ b/drivers/dpll/sit9531x/dpll.c
> @@ -25,6 +25,17 @@
> #define SIT9531X_ESYNC_FREQ_10MHZ 10000000ULL
> #define SIT9531X_ESYNC_PULSE_DEFAULT 50
>
> +static const struct dpll_pin_frequency sit9531x_esync_ranges[] = {
> + DPLL_PIN_FREQUENCY(0),
> + DPLL_PIN_FREQUENCY(SIT9531X_ESYNC_FREQ_10MHZ),
> +};
[Severity: High]
Is the embedded sync frequency here the marker rate or the carrier rate?
The dpll uAPI describes DPLL_A_PIN_ESYNC_FREQUENCY as a signal embedded
into the pin's separately configured base clock:
Documentation/netlink/specs/dpll.yaml
name: esync-frequency
type: u64
doc: |
Frequency of Embedded SYNC signal. If provided, the pin is configured
with a SYNC signal embedded into its base clock frequency.
This range advertises 0 and 10 MHz as the acceptable esync frequencies, and
sit9531x_dpll_output_pin_esync_set() then feeds the accepted 10 MHz into
sit9531x_output_freq_set(), which programs the DIVO output divider, i.e.
the base clock of the pad. A 1 PPS esync request is rejected with -EINVAL.
For comparison, ice_dpll_output_esync_get() treats 10 MHz as the required
base frequency and reports the marker rate separately:
if (!(p->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_ABILITY) ||
p->freq != DPLL_PIN_FREQUENCY_10_MHZ)
return -EOPNOTSUPP;
...
esync->freq = DPLL_PIN_FREQUENCY_1_HZ;
The driver's own binding example also pairs esync-control with a base rate
that esync_set would overwrite:
Documentation/devicetree/bindings/dpll/sitime,sit95316.yaml
pin@0 {
reg = <0>;
label = "clkout0";
esync-control;
supported-frequencies-hz = /bits/ 64 <156250000>;
};
Enabling esync on that pin changes the pad from 156.25 MHz to 10 MHz. Is
that intended?
The changelog says:
"On this device that is the same divider programming as a frequency
change plus the per-output pulse control, so enabling it sets the output
to 1 Hz and disabling it restores the requested rate."
That does not seem to match the code: the only accepted non-zero value is
10 MHz, not 1 Hz, and the freq == 0 path only calls
sit9531x_output_disable() rather than restoring a rate. Could the
changelog be adjusted to describe what the code does?
> +
> +static inline bool
> +sit9531x_dpll_esync_pin_supported(const struct sit9531x_dpll_pin *dpin)
> +{
> + return dpin->esync_control;
> +}
> +
> static inline bool sit9531x_dpll_is_input_pin(const struct sit9531x_dpll_pin *pin)
> {
> return pin->dir == DPLL_PIN_DIRECTION_INPUT;
> @@ -785,6 +796,92 @@ sit9531x_dpll_output_pin_phase_adjust_set(const struct dpll_pin *pin,
> return 0;
> }
>
> +static int
> +sit9531x_dpll_output_pin_esync_get(const struct dpll_pin *pin,
> + void *pin_priv,
> + const struct dpll_device *dpll,
> + void *dpll_priv,
> + struct dpll_pin_esync *esync,
> + struct netlink_ext_ack *extack)
> +{
> + struct sit9531x_dpll_pin *dpin = pin_priv;
> +
> + if (!sit9531x_dpll_esync_pin_supported(dpin))
> + return -EOPNOTSUPP;
> +
> + esync->range = sit9531x_esync_ranges;
> + esync->range_num = ARRAY_SIZE(sit9531x_esync_ranges);
> + esync->pulse = SIT9531X_ESYNC_PULSE_DEFAULT;
> + esync->freq = dpin->esync_freq;
[Severity: Medium]
Can dpin->esync_freq go stale here? Unlike
sit9531x_dpll_output_pin_frequency_get(), this getter does no DIVO or
PROG0 readback, and dpin->esync_freq is written only by esync_set().
Two writers of the same divider leave it inconsistent:
sit9531x_dpll_output_pin_frequency_set()
-> sit9531x_output_freq_set(sitdev, dpin->id, actual_pll, frequency)
sit9531x_dpll_output_pin_state_on_dpll_set(DISCONNECTED)
-> sit9531x_output_disable(sitdev, dpin->id)
Neither clears dpin->esync_freq, so after pin_set(esync-frequency=10000000)
followed by pin_set(frequency=25000000) a pin dump still advertises an
embedded sync configuration the device is no longer producing. Note that
dpll_pin_set_from_nlattr() handles DPLL_A_PIN_FREQUENCY and
DPLL_A_PIN_ESYNC_FREQUENCY independently in message order, so both can
arrive in one request.
Userspace also cannot repair the stale value, because dpll_pin_esync_set()
consults the getter first:
drivers/dpll/dpll_netlink.c:dpll_pin_esync_set() {
...
if (freq == esync.freq)
return 0;
...
}
so a repeat request for the cached value is acked without the driver
callback running.
Also, sit9531x_dpll_pin_register() copies only the capability flag:
pin->esync_control = props->esync_control;
so an output that the efuse/NVM blob already programmed for esync reads
back as disabled. Should the getter read the hardware instead?
[Severity: Low]
Should esync->pulse be reported as 0 when esync->freq is 0? As written a
pin dump on a never-configured esync-control output reports
DPLL_A_PIN_ESYNC_FREQUENCY = 0 together with DPLL_A_PIN_ESYNC_PULSE = 50,
i.e. a 50 percent pulse ratio for a signal that is not present. Both
ice_dpll_output_esync_get() and zl3073x_dpll_output_pin_esync_get() zero
both fields in the disabled case:
} else {
esync->freq = 0;
esync->pulse = 0;
}
> +
> + return 0;
> +}
> +
> +static int
> +sit9531x_dpll_output_pin_esync_set(const struct dpll_pin *pin,
> + void *pin_priv,
> + const struct dpll_device *dpll,
> + void *dpll_priv,
> + u64 freq,
> + 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;
> +
> + if (!sit9531x_dpll_esync_pin_supported(dpin)) {
> + NL_SET_ERR_MSG(extack,
> + "Embedded sync not enabled for this pin");
> + return -EOPNOTSUPP;
> + }
> +
> + actual_pll = sitdev->out[dpin->id].pll_idx;
> +
> + mutex_lock(&sitdev->multiop_lock);
> +
> + /*
> + * This output is a dedicated embedded-sync pin.
> + * Treat freq=0 as a request to disable the entire output.
> + */
> + if (!freq) {
> + rc = sit9531x_output_disable(sitdev, dpin->id);
> + if (!rc)
> + dpin->esync_freq = 0;
> + mutex_unlock(&sitdev->multiop_lock);
> + return rc;
> + }
[Severity: High]
Where is the pin's previous output rate saved so this path can restore it?
The enable path below calls sit9531x_output_freq_set(), which reprograms
DIVO and also replaces the cached rate:
drivers/dpll/sit9531x/core.c:sit9531x_output_freq_set() {
...
sitdev->out[out_idx].freq = (u32)div64_u64(fvco, divo);
}
and sit9531x_output_disable() touches only the Hi-Z bits:
/* Take control (MASK=1) and mute (STATE=0) on both DIFF and SE */
Neither struct sit9531x_dpll_pin nor struct sit9531x_out holds the
pre-esync frequency or the previous PROG0 byte, and
sit9531x_output_pulse_ctrl_set() never reads PROG0 back, so after an
enable/disable cycle the rate userspace originally configured appears to be
gone from both hardware and driver state.
Does a following pin_set(state=connected) then un-mute the pad at 10 MHz
with the esync pulse still armed?
drivers/dpll/sit9531x/dpll.c:sit9531x_dpll_output_pin_state_on_dpll_set() {
case DPLL_PIN_STATE_CONNECTED:
rc = sit9531x_output_enable(sitdev, dpin->id);
...
}
while sit9531x_dpll_output_pin_esync_get() reports esync->freq == 0.
> +
> + if (freq != SIT9531X_ESYNC_FREQ_10MHZ) {
> + mutex_unlock(&sitdev->multiop_lock);
> + NL_SET_ERR_MSG(extack,
> + "Only 10 MHz esync frequency is supported");
> + return -EINVAL;
> + }
> +
> + rc = sit9531x_output_freq_set(sitdev, dpin->id, actual_pll,
> + SIT9531X_ESYNC_FREQ_10MHZ);
> + /*
> + * Program the pulse generator (PROG0 PULSE_CTRL) so the embedded-sync
> + * pulse is actually emitted; without it the output carries the clock
> + * but no esync marker. SIT9531X_ESYNC_PULSE_DEFAULT is the same duty
> + * the esync_get callback advertises.
> + */
> + if (!rc)
> + rc = sit9531x_output_pulse_ctrl_set(sitdev, dpin->id,
> + SIT9531X_ESYNC_PULSE_DEFAULT);
> + if (!rc)
> + rc = sit9531x_output_enable(sitdev, dpin->id);
> +
> + mutex_unlock(&sitdev->multiop_lock);
> +
> + if (!rc)
> + dpin->esync_freq = SIT9531X_ESYNC_FREQ_10MHZ;
> +
> + return rc;
> +}
[Severity: Medium]
What happens if the second or third step here fails?
Each of the three calls commits independently: sit9531x_output_freq_set()
writes DIVO and runs prg_commit() with UPDATE_NVM plus LOOP_LOCK, and
sit9531x_output_pulse_ctrl_set() opens its own PRG_CMD window, so this is
not one transaction. If sit9531x_output_pulse_ctrl_set() or
sit9531x_output_enable() returns an I2C or regmap error, the divider change
stays committed and nothing is unwound.
Since dpin->esync_freq is only assigned when all three steps return 0, the
driver then reports esync as disabled on a pin whose carrier has already
been changed to 10 MHz. Is that state recoverable through this attribute?
dpll_pin_esync_set() short-circuits on the cached value:
if (freq == esync.freq)
return 0;
so a follow-up request for freq == 0 matches the stale cached 0 and never
reaches the driver.
There is also a window between the frequency commit and the PROG0 commit
where an already-enabled output emits the new rate with no marker.
> static const struct dpll_pin_ops sit9531x_dpll_output_pin_ops = {
[ ... ]
> diff --git a/drivers/dpll/sit9531x/regs.h b/drivers/dpll/sit9531x/regs.h
> index 91d33abd15c3..251c98dc7cbe 100644
> --- a/drivers/dpll/sit9531x/regs.h
> +++ b/drivers/dpll/sit9531x/regs.h
> @@ -215,6 +215,13 @@
> #define SIT9531X_OUT_PRG_FINE_MAX 7 /* 3-bit field */
> #define SIT9531X_OUT_PRG_COARSE_BITS 34
>
> +/*
> + * Per-output pulse-count control byte used in SYSREF / SYNCB modes.
> + * Slot N within a page sits at 0x1B + 16 * (slot % 6). Same page
> + * mapping as PRG_RST_DELAY: slots 0-5 on Page 3, slots 6-11 on Page 4.
> + */
> +#define SIT9531X_OUT_PROG0_BASE 0x1B
The comment says this byte applies in SYSREF / SYNCB modes. The new esync
path does not select either mode, so does the write take effect at all in
the mode the output is left in?
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260902214030.20955-1-arouhi%40sitime.com
next prev parent reply other threads:[~2026-09-08 12:45 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-02 21:40 [PATCH net-next v8 00/15] dpll: add SiTime SiT9531x DPLL clock driver Ali Rouhi
2026-09-02 21:40 ` [PATCH net-next v8 01/15] dt-bindings: vendor-prefixes: add SiTime Corporation Ali Rouhi
2026-09-02 21:40 ` [PATCH net-next v8 03/15] dpll: add basic SiTime SiT9531x support Ali Rouhi
2026-09-08 12:45 ` netdev-bot+sashiko
2026-09-02 21:40 ` [PATCH net-next v8 02/15] dt-bindings: dpll: add SiTime SiT95316 clock generator Ali Rouhi
2026-09-08 12:45 ` netdev-bot+sashiko
2026-09-02 21:40 ` [PATCH net-next v8 05/15] dpll: sit9531x: register DPLL devices and pins Ali Rouhi
2026-09-08 12:45 ` netdev-bot+sashiko
2026-09-02 21:40 ` [PATCH net-next v8 04/15] dpll: sit9531x: read DPLL types and pin properties from system firmware Ali Rouhi
2026-09-08 12:45 ` netdev-bot+sashiko
2026-09-02 21:40 ` [PATCH net-next v8 07/15] dpll: sit9531x: add support to get and set priority on input pins Ali Rouhi
2026-09-08 12:45 ` netdev-bot+sashiko
2026-09-02 21:40 ` [PATCH net-next v8 06/15] dpll: sit9531x: implement input pin state on a DPLL Ali Rouhi
2026-09-08 12:45 ` netdev-bot+sashiko
2026-09-02 21:40 ` [PATCH net-next v8 08/15] dpll: sit9531x: add support to get and set frequency on pins Ali Rouhi
2026-09-08 12:45 ` netdev-bot+sashiko
2026-09-02 21:40 ` [PATCH net-next v8 09/15] dpll: sit9531x: implement output pin state on a DPLL Ali Rouhi
2026-09-08 12:45 ` netdev-bot+sashiko
2026-09-02 21:40 ` [PATCH net-next v8 10/15] dpll: sit9531x: add support to adjust output phase Ali Rouhi
2026-09-08 12:45 ` netdev-bot+sashiko
2026-09-02 21:40 ` [PATCH net-next v8 11/15] dpll: sit9531x: add support to get and set esync on pins Ali Rouhi
2026-09-08 12:45 ` netdev-bot+sashiko [this message]
2026-09-02 21:40 ` [PATCH net-next v8 13/15] dpll: sit9531x: add support to get fractional frequency offset Ali Rouhi
2026-09-08 12:45 ` netdev-bot+sashiko
2026-09-02 21:40 ` [PATCH net-next v8 12/15] dpll: sit9531x: add support to get phase offset on the connected input pin Ali Rouhi
2026-09-08 12:45 ` netdev-bot+sashiko
2026-09-02 21:40 ` [PATCH net-next v8 14/15] dpll: sit9531x: model the inter-PLL sync net as a pair of pins Ali Rouhi
2026-09-08 12:45 ` netdev-bot+sashiko
2026-09-02 21:40 ` [PATCH net-next v8 15/15] dpll: sit9531x: allow the device tree to override two board facts Ali Rouhi
2026-09-08 12:45 ` netdev-bot+sashiko
2026-09-08 12:57 ` [PATCH net-next v8 00/15] dpll: add SiTime SiT9531x DPLL clock driver Paolo Abeni
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=178887152134.219967.16181634650893462486@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=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®