[PATCH v10 13/14] dpll: sit9531x: model the inter-PLL sync net as a pair of pins

From: Ali Rouhi

Date: Mon Sep 21 2026 - 16:19:19 EST


From: Oleg Zadorozhnyi <Oleg.Zadorozhnyi@xxxxxxxxxxxxxxxxx>

The device has an internal net by which one PLL can drive the others: the
source PLL puts its output on it, and any other PLL can select it as a
reference instead of an external input. The two ends are nothing alike --
one is driven, the other is selected -- so they are two pins rather than
one: an output pin on the source and an input pin on each destination.

That keeps each pin honest about what its state means. The source pin
reports whether this PLL is the one driving the net, and setting it takes
the net over or gives it up; a destination pin reports whether its PLL has
selected the net, and behaves like any other selectable input. A single
pin would have had to answer both questions at once and could only have
been right about one of them.

Signed-off-by: Oleg Zadorozhnyi <Oleg.Zadorozhnyi@xxxxxxxxxxxxxxxxx>
Assisted-by: Claude:claude-4-opus [chat]
Signed-off-by: Ali Rouhi <arouhi@xxxxxxxxxx>
---

Notes:
Changes in v10:
INTSYNC ownership: recorded before the read-back that can fail, and a
disable that fails part way leaves the state describable so the request
can be repeated.

Error reporting: a full priority table says so instead of reading like a
bus failure.

drivers/dpll/sit9531x/core.c | 291 ++++++++++++++++++++++++++++++++++-
drivers/dpll/sit9531x/core.h | 3 +
drivers/dpll/sit9531x/dpll.c | 247 ++++++++++++++++++++++++++++-
drivers/dpll/sit9531x/regs.h | 3 +
4 files changed, 538 insertions(+), 6 deletions(-)

diff --git a/drivers/dpll/sit9531x/core.c b/drivers/dpll/sit9531x/core.c
index 1cb691f04019..e8d47999f1a9 100644
--- a/drivers/dpll/sit9531x/core.c
+++ b/drivers/dpll/sit9531x/core.c
@@ -2264,9 +2264,9 @@ int sit9531x_output_freq_get(struct sit9531x_dev *sitdev, u8 out_idx,
* 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. A negative phase
- * adjustment (advance) is wrapped to (T_out - |phase|) modulo one
- * output period, which is identical for a periodic signal.
+ * 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|).
*/

int sit9531x_output_phase_adjust_set(struct sit9531x_dev *sitdev,
@@ -2531,6 +2531,278 @@ int sit9531x_clear_notifications(struct sit9531x_dev *sitdev)
return 0;
}

+/*
+ * 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;
+ u8 global;
+ u8 pll, ext_page;
+ int rc, 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;
+ return 0;
+ }
+
+ for (pll = 0; pll < SIT9531X_NUM_PLLS; pll++) {
+ ext_page = SIT9531X_PLL_EXT_PAGE(pll);
+
+ 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)
+ return rc;
+ if (val != intsync_config[i].en_val)
+ break;
+ }
+
+ if (i == 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);
+ }
+ }
+
+ sitdev->intsync_src = src;
+
+ 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);
+}
+
+/*
+ * 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, 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_write_pll_u8(sitdev, src_pll_idx,
+ SIT9531X_PLL_REG_SMALL_UPDATE,
+ SIT9531X_SMALL_UPDATE_CMD);
+ 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.
+ */
+ lock_rc = sit9531x_intsync_debug_lock(sitdev, ext_page);
+ if (lock_rc && !rc)
+ rc = 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.
+ */
+ {
+ int rollback_rc;
+
+ rollback_rc = sit9531x_intsync_disable(sitdev, src_pll_idx);
+ if (rollback_rc)
+ dev_warn(sitdev->dev,
+ "INTSYNC rollback failed after enable error: %d (original %d)\n",
+ rollback_rc, 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)
+{
+ u8 ext_page, val;
+ int rc, lock_rc, 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)
+ return rc;
+
+ /* 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;
+
+ 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 */
+ rc = sit9531x_write_pll_u8(sitdev, src_pll_idx,
+ SIT9531X_PLL_REG_SMALL_UPDATE,
+ SIT9531X_SMALL_UPDATE_CMD);
+ if (rc)
+ goto relock;
+
+ rc = 0;
+
+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 (!sit9531x_read_u8(sitdev, SIT9531X_REG_INTSYNC_GLOBAL, &val))
+ sit9531x_write_u8(sitdev, SIT9531X_REG_INTSYNC_GLOBAL,
+ val | BIT(SIT9531X_INTSYNC_EN_BIT));
+
+relock:
+ /* Close the EXT page debug window the sequence opened. */
+ lock_rc = sit9531x_intsync_debug_lock(sitdev, ext_page);
+ if (lock_rc && !rc)
+ rc = lock_rc;
+
+ return rc;
+}
+
/**
* sit9531x_chan_selected_ref_read - read a PLL's active reference now
* @sitdev: device pointer
@@ -3109,6 +3381,15 @@ static int sit9531x_dev_state_fetch(struct sit9531x_dev *sitdev)
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++) {
s32 phase_ps;

@@ -3664,13 +3945,13 @@ static bool sit9531x_dpll_pin_is_registrable(struct sit9531x_dpll *sitdpll,
if (index == SIT9531X_MAX_INPUTS)
return true;
if (index == SIT9531X_INTSYNC_PIN_ID)
- return false;
+ return true;

return sit9531x_input_pin_is_registrable(sitdev, index);
}

if (index == SIT9531X_INTSYNC_OUT_PIN_ID)
- return false;
+ return true;

if (index >= sitdev->info->num_outputs)
return false;
diff --git a/drivers/dpll/sit9531x/core.h b/drivers/dpll/sit9531x/core.h
index 1d77b89e83ec..1fc14eabb621 100644
--- a/drivers/dpll/sit9531x/core.h
+++ b/drivers/dpll/sit9531x/core.h
@@ -287,6 +287,9 @@ int sit9531x_output_phase_adjust_set(struct sit9531x_dev *sitdev,
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_pll_ffo_ppt(struct sit9531x_dev *sitdev, u8 pll_idx, s64 *ffo);
diff --git a/drivers/dpll/sit9531x/dpll.c b/drivers/dpll/sit9531x/dpll.c
index fcdd19e0bd3b..856c9bdf33d4 100644
--- a/drivers/dpll/sit9531x/dpll.c
+++ b/drivers/dpll/sit9531x/dpll.c
@@ -27,6 +27,20 @@ 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)
{
@@ -843,8 +857,234 @@ sit9531x_dpll_output_pin_direction_get(const struct dpll_pin *pin,
enum dpll_pin_direction *direction,
struct netlink_ext_ack *extack);

+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 (sitdev->intsync_src == sitdpll->id)
+ *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 *sitdpll = dpll_priv;
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+ int rc = 0, detect_rc = 0;
+ u8 hw_src;
+
+ mutex_lock(&sitdev->multiop_lock);
+
+ switch (state) {
+ case DPLL_PIN_STATE_CONNECTED:
+ if (sitdev->intsync_src == sitdpll->id)
+ break;
+ if (sitdev->intsync_src >= 0) {
+ 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);
+ break;
+ case DPLL_PIN_STATE_DISCONNECTED:
+ if (sitdev->intsync_src != sitdpll->id)
+ break;
+ rc = sit9531x_intsync_disable(sitdev, sitdpll->id);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ /*
+ * Re-scan hardware after source state transitions so cache follows
+ * partially failed enable/disable paths as closely as possible.
+ */
+ /*
+ * Record what was asked for before confirming it. The refresh below
+ * leaves the cache untouched when a read fails, and a cache that
+ * still says nobody drives the net would let a second PLL be
+ * configured to drive it as well.
+ */
+ if (!rc && state == DPLL_PIN_STATE_CONNECTED)
+ sitdev->intsync_src = sitdpll->id;
+ else if (!rc && state == DPLL_PIN_STATE_DISCONNECTED)
+ sitdev->intsync_src = -1;
+
+ if (state == DPLL_PIN_STATE_CONNECTED ||
+ state == DPLL_PIN_STATE_DISCONNECTED)
+ detect_rc = sit9531x_intsync_src_detect(sitdev);
+ /*
+ * 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);
+
+ mutex_unlock(&sitdev->multiop_lock);
+
+ if (rc && rc != -EBUSY && rc != -EINVAL && rc != -EOPNOTSUPP)
+ NL_SET_ERR_MSG(extack, "Failed to set INTSYNC source state");
+
+ 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;
+}
+
+/*
+ * 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,
+ .prio_get = sit9531x_dpll_input_pin_prio_get,
+ .prio_set = sit9531x_dpll_input_pin_prio_set,
+};
+
/*
* XO (crystal oscillator) pin ops
*
@@ -1126,8 +1366,13 @@ static const struct dpll_pin_ops sit9531x_dpll_output_pin_ops = {
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_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;
return &sit9531x_dpll_input_pin_ops;
diff --git a/drivers/dpll/sit9531x/regs.h b/drivers/dpll/sit9531x/regs.h
index 4a928126473d..09a2032f7bf0 100644
--- a/drivers/dpll/sit9531x/regs.h
+++ b/drivers/dpll/sit9531x/regs.h
@@ -315,6 +315,9 @@
#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)

/*
--
2.43.0