From: sashiko-bot@kernel.org
To: "Ali Rouhi" <arouhi@sitime.com>
Cc: "devicetree@vger.kernel.org" <devicetree@vger.kernel.org>,
robh@kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH net-next v15 13/13] dpll: sit9531x: model the inter-PLL sync line as a pair of pins
Date: Wed, 11 Nov 2026 20:26:56 +0000 [thread overview]
Message-ID: <sashiko-outbox-209664@kernel.org> (raw)
In-Reply-To: <20261110202607.55744-14-arouhi@sitime.com>
Thank you for your contribution! Sashiko AI review found 6 potential
issues to consider:
- [Medium] dpll: sit9531x: broken rollback in
__sit9531x_intsync_disable() on write error
- [Medium] dpll: sit9531x: sticky intsync_partial in
sit9531x_intsync_readback()
- [Medium] dpll: sit9531x: wrong INTSYNC reference check and state on
manual/partial PLLs
- [Medium] dpll: sit9531x: sit9531x_resume() treats -ENODATA phase read
as error
- [Medium] dpll: sit9531x: sit9531x_intsync_src_detect() clobbers
partial state on disconnect failure
- [Low] dpll: sit9531x: lost pin notification when post-disconnect
detect fails under conflict
--
commit dee9e16728fe17201c2262f079ff7610b8f1bd64
Author: Oleg Zadorozhnyi <Oleg.Zadorozhnyi@devoxsoftware.com>
dpll: sit9531x: model the inter-PLL sync line as a pair of pins
This commit models the internal inter-PLL sync line (INTSYNC) as an output
source pin and an input destination pin on each DPLL, adding EXT-page
source detection, enable/disable sequences, and resume state readback.
> diff --git a/drivers/dpll/sit9531x/core.c b/drivers/dpll/sit9531x/core.c
> index 8f305cc27737e..e3ce7b6cad6b9 100644
> --- a/drivers/dpll/sit9531x/core.c
> +++ b/drivers/dpll/sit9531x/core.c
[ ... ]
> @@ -2585,6 +2604,556 @@ static int sit9531x_clear_notifications(struct sit9531x_dev *sitdev)
[ ... ]
> +int sit9531x_intsync_readback(struct sit9531x_dev *sitdev)
> +{
> + bool partial = sitdev->intsync_partial;
> + s8 src = sitdev->intsync_src;
> + int rc;
> +
> + rc = sit9531x_intsync_src_detect(sitdev);
> + if (rc)
> + return rc;
> +
> + if (partial && (sitdev->intsync_src < 0 ||
> + (sitdev->intsync_src == src &&
> + !sitdev->intsync_partial))) {
> + sitdev->intsync_src = src;
> + sitdev->intsync_partial = true;
> + sitdev->intsync_foreign = false;
> + }
> +
> + return 0;
> +}
[Severity: Medium]
Does sit9531x_intsync_readback() overwrite the freshly detected hardware
state whenever sitdev->intsync_partial was set before the scan?
When sit9531x_resume() calls sit9531x_intsync_readback() after power loss
and a profile reload, a pre-suspend partial state forces intsync_partial
back to true even if the reloaded profile cleanly enabled that PLL (causing
SYNCOUT to be reported as DISCONNECTED and INTSYNC destination pins as
NO_SIGNAL) or cleared all PLLs to the disable pattern (leaving intsync_src
set and blocking other PLLs from enabling SYNCOUT with -EBUSY).
In addition, in sit9531x_dpll_intsync_src_state_on_dpll_set(), when
connecting SYNCOUT fails with detect_rc != 0 or owner_kept set, the error
path sets intsync_src = sitdpll->id and intsync_partial = true without
setting intsync_stale = true, which permanently wedges intsync_src on that
PLL.
[ ... ]
> +static int __sit9531x_intsync_disable(struct sit9531x_dev *sitdev,
> + u8 src_pll_idx, bool restore,
> + bool *owner_kept, bool *touched)
> +{
> + bool in_window = false, was_enabled;
> + u8 ext_page, val;
> + int rc, lock_rc;
> + unsigned int i;
> +
> + lockdep_assert_held(&sitdev->multiop_lock);
> +
> + if (owner_kept)
> + *owner_kept = false;
> + if (touched)
> + *touched = false;
> + 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;
> + was_enabled = val & BIT(SIT9531X_INTSYNC_EN_BIT);
> + if (touched)
> + *touched = true;
> + rc = sit9531x_write_u8(sitdev, SIT9531X_REG_INTSYNC_GLOBAL,
> + val & ~BIT(SIT9531X_INTSYNC_EN_BIT));
> + if (rc)
> + goto restore_global;
> + /* A sample taken before this write can still show a lock. */
> + for (i = 0; i < SIT9531X_NUM_PLLS; i++)
> + if (sitdev->chan[i].routed_ref == SIT9531X_INTSYNC_PIN_ID)
> + sitdev->chan[i].intsync_dropped = true;
> +
> + rc = sit9531x_write_u8(sitdev, SIT9531X_REG_GLOBAL_UPDATE,
> + SIT9531X_SMALL_UPDATE_CMD);
> + usleep_range(1000, 2000);
> + if (rc)
> + goto restore_global;
> +
> + rc = sit9531x_write_u8(sitdev,
> + SIT9531X_REG(ext_page,
> + SIT9531X_PLL_REG_DEBUG),
> + SIT9531X_PLL_DEBUG_UNLOCK);
> + if (rc)
> + goto restore_global;
> + in_window = true;
> +
> + 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;
> + }
> +
> + rc = sit9531x_pll_small_update(sitdev, src_pll_idx);
> + if (rc && owner_kept)
> + *owner_kept = true;
> +
> + goto relock;
> +
> +restore_global:
> + /*
> + * Restore the global enable, or finish the disable if the page holds
> + * the disable pattern.
> + */
> + if (restore && in_window) {
> + bool off;
> +
> + if (sit9531x_intsync_page_off(sitdev, ext_page, &off)) {
> + if (owner_kept)
> + *owner_kept = true;
> + } else if (off) {
> + rc = sit9531x_pll_small_update(sitdev, src_pll_idx);
> + if (rc && owner_kept)
> + *owner_kept = true;
> + goto relock;
> + }
> + }
> +
> + if (restore && was_enabled) {
> + int ret;
> +
> + ret = sit9531x_read_u8(sitdev, SIT9531X_REG_INTSYNC_GLOBAL,
> + &val);
> + if (!ret)
> + ret = sit9531x_write_u8(sitdev,
> + SIT9531X_REG_INTSYNC_GLOBAL,
> + val |
> + BIT(SIT9531X_INTSYNC_EN_BIT));
> + if (!ret) {
> + /* The page-0 small update applies the global enable */
> + ret = sit9531x_write_close(sitdev,
> + SIT9531X_REG_GLOBAL_UPDATE,
> + SIT9531X_SMALL_UPDATE_CMD);
> + usleep_range(1000, 2000);
> + }
> + if (ret) {
> + dev_warn(sitdev->dev,
> + "PLL%c: INTSYNC enable not put back after a failed disable: %d\n",
> + 'A' + src_pll_idx, ret);
> + if (owner_kept)
> + *owner_kept = true;
> + }
> + }
[Severity: Medium]
If an I2C write fails partway through the intsync_config[] loop, does
restore_global re-enable SIT9531X_REG_INTSYNC_GLOBAL while leaving the
partially written dis_val entries in the EXT page and chan->intsync_dropped
set?
Because sit9531x_pll_small_update() was not reached, the hardware still
drives the line with the previously latched enable pattern (until a later
PLL small update latches the half-disabled registers under an active global
enable), but sit9531x_intsync_src_detect() sees a mix of en_val and dis_val
in the unlatched EXT page and sets sitdev->intsync_partial = true, reporting
SYNCOUT as DISCONNECTED and INTSYNC destination pins as NO_SIGNAL.
Conversely, when was_enabled is false (such as when disconnecting a partial
source whose EXT page already holds unlatched dis_val entries) and a write
fails before in_window is set, *owner_kept remains false and
sit9531x_intsync_src_detect() clears sitdev->intsync_src to -1 even though
the disable pattern was never latched.
[ ... ]
> @@ -4420,12 +5000,128 @@ static int sit9531x_suspend(struct device *dev)
> static int sit9531x_resume(struct device *dev)
> {
[ ... ]
> + for (i = 0; i < sitdev->info->num_outputs; i++) {
> + struct sit9531x_out *out = &sitdev->out[i];
> + bool beyond;
> + s64 phase_ps;
> +
> + if (!out->routed)
> + continue;
> + rc = sit9531x_output_phase_read(sitdev, i, &phase_ps,
> + &beyond);
> + if (rc) {
> + dev_warn(sitdev->dev,
> + "Output %u delay not read on resume: %d\n",
> + i, rc);
> + out->phase_stale = true;
> + outs_unread |= BIT(i);
> + continue;
> + }
[Severity: Medium]
Should sit9531x_resume() ignore -ENODATA from sit9531x_output_phase_read()
the way sit9531x_dev_state_fetch() does at probe?
When an output's PLL has an unprogrammed divider or out-of-range VCO,
sit9531x_output_phase_read() returns -ENODATA. Setting out->phase_stale =
true on -ENODATA causes sit9531x_dpll_output_pin_phase_adjust_get() to fail
with -ENODATA and sit9531x_dpll_output_ntf_pending() to return false on
every poll tick, permanently blocking change notifications for that output
pin.
> diff --git a/drivers/dpll/sit9531x/dpll.c b/drivers/dpll/sit9531x/dpll.c
> index ba4ac5e7d365d..1bd2b962cdabe 100644
> --- a/drivers/dpll/sit9531x/dpll.c
> +++ b/drivers/dpll/sit9531x/dpll.c
[ ... ]
> @@ -662,6 +689,325 @@ sit9531x_dpll_output_pin_direction_get(const struct dpll_pin *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)
> +{
[ ... ]
> + 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, &owner_kept,
> + &changed);
> + break;
> + case DPLL_PIN_STATE_DISCONNECTED:
> + if (sitdev->intsync_src != sitdpll->id)
> + break;
> + rc = sit9531x_intsync_disable(sitdev, sitdpll->id,
> + &owner_kept, &changed);
> + break;
> + default:
> + rc = -EINVAL;
> + break;
> + }
> +
> + if (changed && !rc) {
> + sitdev->intsync_src = state == DPLL_PIN_STATE_CONNECTED ?
> + sitdpll->id : -1;
> + sitdev->intsync_partial = false;
> + }
> +
> + if (changed)
> + detect_rc = sit9531x_intsync_src_detect(sitdev);
> + if (changed && rc) {
> + if (owner_kept ||
> + (state == DPLL_PIN_STATE_CONNECTED &&
> + (detect_rc || sitdev->intsync_src == sitdpll->id))) {
> + sitdev->intsync_src = sitdpll->id;
> + sitdev->intsync_partial = true;
> + sitdev->intsync_foreign = false;
> + } else if (detect_rc) {
> + sitdev->intsync_stale = true;
> + }
> + } else if (changed && detect_rc && sitdev->intsync_conflict) {
> + sitdev->intsync_stale = true;
> + }
[Severity: Medium]
Does calling sit9531x_intsync_src_detect() instead of
sit9531x_intsync_readback() here lose the recorded intsync_partial state
when a disconnect fails before modifying the EXT page?
If a PLL has sitdev->intsync_partial == true because an earlier enable left
en_val in the EXT page (with the global enable set) without latching via
sit9531x_pll_small_update(), and a subsequent DPLL_PIN_STATE_DISCONNECTED
request fails before unlocking the EXT page while restore_global restores
SIT9531X_REG_INTSYNC_GLOBAL (so owner_kept is false),
sit9531x_intsync_src_detect() sees all en_val with global enable set and
clears sitdev->intsync_partial to false. The failed disconnect then flips
the pin state from DISCONNECTED to CONNECTED and sends a change
notification.
[Severity: Low]
When disconnecting a PLL under sitdev->intsync_conflict succeeds (changed &&
!rc) but the post-write sit9531x_intsync_src_detect() fails, can the pin
change notification be lost?
The changed && !rc block clears sitdev->intsync_src to -1 before
sitdev->intsync_stale = true is set. If the immediate getter refresh inside
__dpll_pin_change_ntf() also fails, the later periodic retry in
sit9531x_intsync_refresh() sees sit9531x_intsync_owner() == -1 both before
and after sit9531x_intsync_readback() and never sets
sitdev->intsync_ntf_pending.
[ ... ]
> +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;
> + int rc;
> +
> + mutex_lock(&sitdev->multiop_lock);
> + rc = sit9531x_dpll_intsync_refresh(sitdev, extack);
> + if (rc) {
> + mutex_unlock(&sitdev->multiop_lock);
> + return rc;
> + }
> + 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;
> +}
[Severity: Medium]
Does checking sit9531x_input_prio_present() in
sit9531x_dpll_intsync_src_state_on_dpll_set() work when the PLL uses manual
input selection, or when sitdev->intsync_partial is set?
On a manual-input PLL (chan->manual_input), reference selection uses
chan->manual_ref / chan->routed_ref rather than chan->prio_mask, and
sit9531x_dpll_intsync_dst_pin_manual_ops has no .state_on_dpll_set callback.
Checking only chan->prio_mask allows a manual PLL routing INTSYNC to drive
the line it locks to, while permanently blocking a manual PLL routing
another input if its unused priority table happens to list
SIT9531X_PRIO_SRC_INTSYNC.
Also, sit9531x_dpll_intsync_dst_state_on_dpll_get() and
sit9531x_dpll_pin_snapshot() report the INTSYNC input pin as DISCONNECTED
whenever sitdev->intsync_src == sitdpll->id, even when
sitdev->intsync_partial is true (so SYNCOUT is also DISCONNECTED). If the
priority table still contains INTSYNC, enabling SYNCOUT fails with -EBUSY
("PLL selects INTSYNC as a reference; it cannot drive it") while the INTSYNC
input pin is reported as DISCONNECTED.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261110202607.55744-1-arouhi@sitime.com?part=13
prev parent reply other threads:[~2026-11-11 20:26 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 20:26 [PATCH net-next v15 00/13] dpll: add SiTime SiT9531x DPLL clock driver Ali Rouhi
2026-11-10 20:26 ` [PATCH net-next v15 01/13] dpll: omit pin phase attributes the driver has no value for Ali Rouhi
2026-11-11 15:35 ` Ivan Vecera
2026-11-10 20:26 ` [PATCH net-next v15 02/13] dt-bindings: vendor-prefixes: add SiTime Corporation Ali Rouhi
2026-11-10 20:26 ` [PATCH net-next v15 03/13] dt-bindings: dpll: add SiTime SiT95316 clock generator Ali Rouhi
2026-11-10 20:26 ` [PATCH net-next v15 04/13] dpll: add basic SiTime SiT9531x support Ali Rouhi
2026-11-10 20:26 ` [PATCH net-next v15 05/13] dpll: sit9531x: read DPLL types and pin properties from system firmware Ali Rouhi
2026-11-10 20:26 ` [PATCH net-next v15 06/13] dpll: sit9531x: register DPLL devices and pins Ali Rouhi
2026-11-10 20:26 ` [PATCH net-next v15 07/13] dpll: sit9531x: implement input pin state on a DPLL Ali Rouhi
2026-11-11 20:26 ` sashiko-bot
2026-11-10 20:26 ` [PATCH net-next v15 08/13] dpll: sit9531x: add support to get and set priority on input pins Ali Rouhi
2026-11-10 20:26 ` [PATCH net-next v15 09/13] dpll: sit9531x: add support to get and set frequency on pins Ali Rouhi
2026-11-10 20:26 ` [PATCH net-next v15 10/13] dpll: sit9531x: implement output pin state on a DPLL Ali Rouhi
2026-11-11 20:26 ` sashiko-bot
2026-11-10 20:26 ` [PATCH net-next v15 11/13] dpll: sit9531x: add support to adjust output phase Ali Rouhi
2026-11-11 20:26 ` sashiko-bot
2026-11-10 20:26 ` [PATCH net-next v15 12/13] dpll: sit9531x: add support to get phase offset on the connected input pin Ali Rouhi
2026-11-11 20:26 ` sashiko-bot
2026-11-10 20:26 ` [PATCH net-next v15 13/13] dpll: sit9531x: model the inter-PLL sync line as a pair of pins Ali Rouhi
2026-11-11 20:26 ` sashiko-bot [this message]
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=sashiko-outbox-209664@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=arouhi@sitime.com \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=robh@kernel.org \
--cc=sashiko-reviews@lists.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.