From: Ali Rouhi <arouhi@sitime.com>
To: "jiri@resnulli.us" <jiri@resnulli.us>
Cc: "vadim.fedorenko@linux.dev" <vadim.fedorenko@linux.dev>,
"arkadiusz.kubalewski@intel.com" <arkadiusz.kubalewski@intel.com>,
"ivecera@redhat.com" <ivecera@redhat.com>,
"kuba@kernel.org" <kuba@kernel.org>,
"pabeni@redhat.com" <pabeni@redhat.com>,
"robh@kernel.org" <robh@kernel.org>,
"krzk+dt@kernel.org" <krzk+dt@kernel.org>,
"conor+dt@kernel.org" <conor+dt@kernel.org>,
"cjubran@nvidia.com" <cjubran@nvidia.com>,
"Oleg.Zadorozhnyi@devoxsoftware.com"
<Oleg.Zadorozhnyi@devoxsoftware.com>,
"prabhakar.mahadev-lad.rj@bp.renesas.com"
<prabhakar.mahadev-lad.rj@bp.renesas.com>,
"devicetree@vger.kernel.org" <devicetree@vger.kernel.org>,
"netdev@vger.kernel.org" <netdev@vger.kernel.org>,
"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>
Subject: [PATCH net-next v15 12/13] dpll: sit9531x: add support to get phase offset on the connected input pin
Date: Tue, 10 Nov 2026 20:26:16 +0000 [thread overview]
Message-ID: <20261110202607.55744-13-arouhi@sitime.com> (raw)
In-Reply-To: <20261110202607.55744-1-arouhi@sitime.com>
From: Oleg Zadorozhnyi <Oleg.Zadorozhnyi@devoxsoftware.com>
Report the phase difference between a PLL's reference and the PLL, read
from the on-chip time-to-digital converter through the debug window as
the documented procedure does. A read latches a sample and returns the
previous one, so each sample is read three times.
Only the input the part feeds the PLL has an offset. Any other pin, a PLL
without a reference, or a PLL that drives no enabled output returns
-ENODATA and the core leaves the attribute out.
Signed-off-by: Oleg Zadorozhnyi <Oleg.Zadorozhnyi@devoxsoftware.com>
Signed-off-by: Ali Rouhi <arouhi@sitime.com>
---
drivers/dpll/sit9531x/core.c | 357 +++++++++++++++++++++++++++++------
drivers/dpll/sit9531x/core.h | 13 ++
drivers/dpll/sit9531x/dpll.c | 120 ++++++++++++
drivers/dpll/sit9531x/regs.h | 19 ++
4 files changed, 456 insertions(+), 53 deletions(-)
diff --git a/drivers/dpll/sit9531x/core.c b/drivers/dpll/sit9531x/core.c
index 9896d5aa40f5..8f305cc27737 100644
--- a/drivers/dpll/sit9531x/core.c
+++ b/drivers/dpll/sit9531x/core.c
@@ -262,6 +262,28 @@ static int sit9531x_update_pll_close(struct sit9531x_dev *sitdev, u8 pll_idx,
return rc;
}
+/*
+ * Restore the debug taps a phase-offset read changed. The PLL page's debug
+ * window must already be open.
+ */
+static int sit9531x_tap_restore(struct sit9531x_dev *sitdev, u8 pll_idx)
+{
+ struct sit9531x_chan *chan = &sitdev->chan[pll_idx];
+ int rc;
+
+ rc = sit9531x_write_pll_close(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_READ_CODE,
+ chan->tap_read_code);
+ if (!rc)
+ rc = sit9531x_write_pll_close(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_WRITE_CODE,
+ chan->tap_write_code);
+ if (!rc)
+ chan->tap_pending = false;
+
+ return rc;
+}
+
static void sit9531x_input_get_regs(const struct sit9531x_dev *sitdev,
u8 index,
unsigned int *force_reg,
@@ -785,12 +807,16 @@ static int sit9531x_prio_prg_commit(struct sit9531x_dev *sitdev)
return 0;
}
-/*
- * Four registers carry a nibble per lane: loss of signal and the fine and
- * coarse frequency-drift flags. Caller must hold sitdev->multiop_lock, or run
- * before registration.
+/**
+ * sit9531x_input_mon_fetch - read the clock monitor status of every lane
+ * @sitdev: device pointer
+ *
+ * One nibble per lane: loss of signal, fine and coarse frequency drift.
+ * Caller must hold sitdev->multiop_lock, or run before registration.
+ *
+ * Return: 0 on success, <0 on error
*/
-static int sit9531x_input_mon_fetch(struct sit9531x_dev *sitdev)
+int sit9531x_input_mon_fetch(struct sit9531x_dev *sitdev)
{
static const unsigned int regs[] = {
SIT9531X_CLKMON_P_STATUS_01, SIT9531X_CLKMON_P_STATUS_23,
@@ -2559,6 +2585,171 @@ static int sit9531x_clear_notifications(struct sit9531x_dev *sitdev)
return 0;
}
+/**
+ * sit9531x_phase_offset_read - read phase difference via TDC
+ * @sitdev: device pointer
+ * @pll_idx: PLL index (0-3)
+ * @phase_offset: phase difference in 1/DPLL_PHASE_OFFSET_DIVIDER ps
+ *
+ * The TDC code is a 35-bit magnitude with a separate sign bit;
+ * phase_diff = tdc_code / fvco. Caller must hold sitdev->multiop_lock.
+ *
+ * Return: 0 on success, -ENODATA if Fvco is unknown, <0 on other errors
+ */
+int sit9531x_phase_offset_read(struct sit9531x_dev *sitdev, u8 pll_idx,
+ s64 *phase_offset)
+{
+ u8 v, old_write_code, old_read_code;
+ struct sit9531x_chan *chan;
+ bool have_old = false;
+ int rc, ret, i;
+ s64 tdc_signed;
+ u64 fvco, mag;
+ u64 tdc_raw;
+ bool sign;
+
+ lockdep_assert_held(&sitdev->multiop_lock);
+
+ if (pll_idx >= SIT9531X_NUM_PLLS)
+ return -EINVAL;
+ chan = &sitdev->chan[pll_idx];
+
+ /*
+ * Read Fvco first; without it there is no reason to open the debug
+ * window.
+ */
+ rc = sit9531x_get_fvco(sitdev, pll_idx, &fvco);
+ if (rc) {
+ if (rc == -ENODATA)
+ dev_dbg(sitdev->dev,
+ "PLL%c: Fvco unknown, skip TDC\n",
+ 'A' + pll_idx);
+ return rc;
+ }
+
+ rc = sit9531x_write_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DEBUG,
+ SIT9531X_PLL_DEBUG_UNLOCK);
+ if (rc)
+ goto relock;
+
+ /* Restore pending taps before saving the selection */
+ if (chan->tap_pending) {
+ rc = sit9531x_tap_restore(sitdev, pll_idx);
+ if (rc)
+ goto relock;
+ }
+
+ /* Save the tap selection; the debug key does not cover the mux. */
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_WRITE_CODE,
+ &old_write_code);
+ if (!rc)
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_READ_CODE,
+ &old_read_code);
+ if (rc)
+ goto relock;
+
+ have_old = true;
+
+ /*
+ * Enable the debug clock for taps below 200 kHz, then select the TDC
+ * tap. The modifier register also holds fields of other taps.
+ */
+ rc = sit9531x_update_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_WRITE_CODE,
+ SIT9531X_DBG_LOW_FREQ_CLK_BIT,
+ SIT9531X_DBG_LOW_FREQ_CLK_BIT);
+ if (rc)
+ goto relock;
+ rc = sit9531x_write_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_READ_CODE,
+ SIT9531X_DBG_READ_CODE_TDC);
+ if (rc)
+ goto relock;
+
+ /*
+ * A status data read latches a sample and returns the previous one;
+ * read three times as the documented procedure does.
+ */
+ for (i = 0; i < SIT9531X_DBG_STATUS_READS; i++) {
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_STATUS_DATA,
+ &v);
+ if (rc)
+ goto relock;
+ }
+
+ tdc_raw = 0;
+
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_DATA_4, &v);
+ if (rc)
+ goto relock;
+ sign = !!(v & BIT(SIT9531X_TDC_SIGN_BIT));
+ tdc_raw = (u64)(v & SIT9531X_TDC_MAG_HI_MASK) << 32;
+
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_DATA_3, &v);
+ if (rc)
+ goto relock;
+ tdc_raw |= (u64)v << 24;
+
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_DATA_2, &v);
+ if (rc)
+ goto relock;
+ tdc_raw |= (u64)v << 16;
+
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_DATA_1, &v);
+ if (rc)
+ goto relock;
+ tdc_raw |= (u64)v << 8;
+
+ rc = sit9531x_read_pll_u8(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DBG_DATA_0, &v);
+ if (rc)
+ goto relock;
+ tdc_raw |= v;
+
+ /* Per the register map the sign bit set means a positive offset. */
+ tdc_signed = sign ? (s64)tdc_raw : -(s64)tdc_raw;
+
+ /*
+ * phase_diff (ps) = tdc_code * 1e12 / fvco. Scale by the subsystem's
+ * divider before dividing to keep the fractional digits; one code is
+ * 203.45 ps at 4.9152 GHz.
+ */
+ mag = mul_u64_u64_div_u64(tdc_signed < 0 ? -tdc_signed : tdc_signed,
+ 1000000000000ULL * DPLL_PHASE_OFFSET_DIVIDER,
+ fvco);
+ *phase_offset = tdc_signed < 0 ? -(s64)mag : (s64)mag;
+
+ rc = 0;
+
+relock:
+ if (have_old) {
+ chan->tap_read_code = old_read_code;
+ chan->tap_write_code = old_write_code;
+ ret = sit9531x_tap_restore(sitdev, pll_idx);
+ if (ret)
+ chan->tap_pending = true;
+ if (ret && !rc)
+ rc = ret;
+ }
+
+ ret = sit9531x_write_pll_close(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DEBUG,
+ SIT9531X_PLL_DEBUG_LOCK);
+ chan->key_pending = !!ret;
+ if (ret && !rc)
+ rc = ret;
+
+ return rc;
+}
+
/* Read the lane's receiver state from the Page 0x02 force and state bits */
static int sit9531x_ref_state_fetch(struct sit9531x_dev *sitdev, u8 index)
{
@@ -2689,13 +2880,20 @@ static int sit9531x_chan_select_fetch(struct sit9531x_dev *sitdev, u8 pll_idx)
return 0;
}
-/*
- * Read the reference the device feeds a PLL from the debug status bus; the
- * active selection does not change on a fallback. The caller rules out free-run
- * first and must hold sitdev->multiop_lock.
+/**
+ * sit9531x_chan_routed_ref_read - read the reference the device feeds a PLL
+ * @sitdev: device pointer
+ * @pll_idx: PLL index (0-3)
+ * @ref: logical input index, SIT9531X_INTSYNC_PIN_ID, or SIT9531X_REF_INVALID
+ *
+ * The device falls back between listed sources without changing the active
+ * selection, so read the source from the debug status bus. The caller rules
+ * out free-run first and must hold multiop_lock.
+ *
+ * Return: 0 on success, <0 on error
*/
-static int sit9531x_chan_routed_ref_read(struct sit9531x_dev *sitdev,
- u8 pll_idx, u8 *ref)
+int sit9531x_chan_routed_ref_read(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u8 *ref)
{
int rc, lock_rc;
u8 v, i;
@@ -3189,10 +3387,32 @@ static void sit9531x_table_back_retry(struct sit9531x_dev *sitdev, u8 pll_idx)
'A' + pll_idx);
}
+/*
+ * Restore the debug taps and close the debug window after a failed phase-offset
+ * read. Caller must hold sitdev->multiop_lock.
+ */
+static void sit9531x_tap_back_retry(struct sit9531x_dev *sitdev, u8 pll_idx)
+{
+ struct sit9531x_chan *chan = &sitdev->chan[pll_idx];
+
+ if (!chan->tap_pending && !chan->key_pending)
+ return;
+
+ if (chan->tap_pending &&
+ !sit9531x_write_pll_u8(sitdev, pll_idx, SIT9531X_PLL_REG_DEBUG,
+ SIT9531X_PLL_DEBUG_UNLOCK))
+ sit9531x_tap_restore(sitdev, pll_idx);
+
+ chan->key_pending = !!sit9531x_write_pll_close(sitdev, pll_idx,
+ SIT9531X_PLL_REG_DEBUG,
+ SIT9531X_PLL_DEBUG_LOCK);
+}
+
/*
* Rewrite the priority table when a listed source returns to a PLL without a
- * reference; the latch restarts acquisition. Caller must hold
- * sitdev->multiop_lock.
+ * reference; the write latches the active selection again. Each latch restarts
+ * acquisition, so the table is written once per change of the best live source.
+ * Caller must hold sitdev->multiop_lock.
*/
static void sit9531x_reselect(struct sit9531x_dev *sitdev, u8 pll_idx)
{
@@ -3273,6 +3493,7 @@ sit9531x_dev_chan_states_update(struct sit9531x_dev *sitdev)
for (i = 0; i < SIT9531X_NUM_PLLS; i++) {
sit9531x_table_back_retry(sitdev, i);
sit9531x_ho_release_retry(sitdev, i);
+ sit9531x_tap_back_retry(sitdev, i);
/*
* A flush pulse sent into open loops may be lost when they
* re-lock.
@@ -3525,55 +3746,30 @@ static void sit9531x_pin_nodes_check(struct sit9531x_dev *sitdev)
fwnode_handle_put(pins);
}
-static int sit9531x_dev_start(struct sit9531x_dev *sitdev)
+/* Retry the pending debug tap restore and debug window close */
+static void sit9531x_tap_pending_retry(struct sit9531x_dev *sitdev)
{
- struct sit9531x_dpll *sitdpll;
- int rc;
-
- rc = sit9531x_dev_state_fetch(sitdev);
- if (rc)
- return rc;
+ u8 i;
mutex_lock(&sitdev->multiop_lock);
- sit9531x_eeprom_state_report(sitdev);
- sit9531x_pll_states_report(sitdev);
- mutex_unlock(&sitdev->multiop_lock);
-
- sit9531x_pin_nodes_check(sitdev);
-
- list_for_each_entry(sitdpll, &sitdev->dplls, list) {
- rc = sit9531x_dpll_register(sitdpll);
- if (rc) {
- dev_err_probe(sitdev->dev, rc,
- "Failed to register DPLL%u\n",
- sitdpll->id);
- goto err_unregister;
- }
+ for (i = 0; i < SIT9531X_NUM_PLLS; i++) {
+ sit9531x_tap_back_retry(sitdev, i);
+ if (sitdev->chan[i].tap_pending || sitdev->chan[i].key_pending)
+ dev_warn(sitdev->dev,
+ "PLL%c left with its debug taps moved or window open\n",
+ 'A' + i);
}
-
- kthread_queue_delayed_work(sitdev->kworker, &sitdev->work, 0);
-
- return 0;
-
-err_unregister:
- list_for_each_entry_continue_reverse(sitdpll, &sitdev->dplls, list)
- sit9531x_dpll_unregister(sitdpll);
-
- return rc;
+ mutex_unlock(&sitdev->multiop_lock);
}
-static void sit9531x_dev_stop(struct sit9531x_dev *sitdev)
+/*
+ * Retry every pending write once more and report what is still pending. Call
+ * with the DPLLs unregistered.
+ */
+static void sit9531x_dev_pending_retry(struct sit9531x_dev *sitdev)
{
- struct sit9531x_dpll *sitdpll;
u8 i;
- kthread_cancel_delayed_work_sync(&sitdev->work);
-
- list_for_each_entry(sitdpll, &sitdev->dplls, list) {
- if (sitdpll->dpll_dev)
- sit9531x_dpll_unregister(sitdpll);
- }
-
/*
* Write back tables first: forced holdover stays set while a table
* write-back is pending.
@@ -3637,6 +3833,61 @@ static void sit9531x_dev_stop(struct sit9531x_dev *sitdev)
'A' + i);
}
mutex_unlock(&sitdev->multiop_lock);
+
+ sit9531x_tap_pending_retry(sitdev);
+}
+
+static int sit9531x_dev_start(struct sit9531x_dev *sitdev)
+{
+ struct sit9531x_dpll *sitdpll;
+ int rc;
+
+ rc = sit9531x_dev_state_fetch(sitdev);
+ if (rc)
+ return rc;
+
+ mutex_lock(&sitdev->multiop_lock);
+ sit9531x_eeprom_state_report(sitdev);
+ sit9531x_pll_states_report(sitdev);
+ mutex_unlock(&sitdev->multiop_lock);
+
+ sit9531x_pin_nodes_check(sitdev);
+
+ list_for_each_entry(sitdpll, &sitdev->dplls, list) {
+ rc = sit9531x_dpll_register(sitdpll);
+ if (rc) {
+ dev_err_probe(sitdev->dev, rc,
+ "Failed to register DPLL%u\n",
+ sitdpll->id);
+ goto err_unregister;
+ }
+ }
+
+ kthread_queue_delayed_work(sitdev->kworker, &sitdev->work, 0);
+
+ return 0;
+
+err_unregister:
+ list_for_each_entry_continue_reverse(sitdpll, &sitdev->dplls, list)
+ sit9531x_dpll_unregister(sitdpll);
+
+ sit9531x_dev_pending_retry(sitdev);
+
+ return rc;
+}
+
+static void sit9531x_dev_stop(struct sit9531x_dev *sitdev)
+{
+ struct sit9531x_dpll *sitdpll;
+
+ kthread_cancel_delayed_work_sync(&sitdev->work);
+
+ list_for_each_entry(sitdpll, &sitdev->dplls, list) {
+ if (sitdpll->dpll_dev)
+ sit9531x_dpll_unregister(sitdpll);
+ }
+
+ sit9531x_dev_pending_retry(sitdev);
}
static struct sit9531x_dpll_pin *
diff --git a/drivers/dpll/sit9531x/core.h b/drivers/dpll/sit9531x/core.h
index bbf5026f945d..8718c463c560 100644
--- a/drivers/dpll/sit9531x/core.h
+++ b/drivers/dpll/sit9531x/core.h
@@ -165,6 +165,10 @@ struct sit9531x_out {
* stays in forced holdover
* @table_back: table reported and to be rewritten while @table_pending
* @tap_pending: debug taps not restored after a phase-offset read
+ * @tap_write_code: tap selection to restore while @tap_pending
+ * @tap_read_code: readback selection to restore while @tap_pending
+ * @key_pending: debug key not locked again after a phase-offset read or tap
+ * retry
* @prio_srcs: cached priority table, a source code per slot
* @prio_last: first slot of each source plus one, 0 if absent
* @prio_mask: bit per canonical source in the priority table read back
@@ -194,6 +198,9 @@ struct sit9531x_chan {
bool table_pending;
u8 table_back[SIT9531X_PRIO_MAX_SLOTS];
bool tap_pending;
+ u8 tap_write_code;
+ u8 tap_read_code;
+ bool key_pending;
u8 prio_srcs[SIT9531X_PRIO_MAX_SLOTS];
u8 prio_last[SIT9531X_PRIO_NUM_SRC];
u16 prio_mask;
@@ -311,6 +318,12 @@ int sit9531x_output_phase_read(struct sit9531x_dev *sitdev, u8 out_idx,
int sit9531x_output_phase_adjust_set(struct sit9531x_dev *sitdev,
u8 out_idx, s32 phase_ps);
+int sit9531x_chan_routed_ref_read(struct sit9531x_dev *sitdev, u8 pll_idx,
+ u8 *ref);
+int sit9531x_input_mon_fetch(struct sit9531x_dev *sitdev);
+int sit9531x_phase_offset_read(struct sit9531x_dev *sitdev, u8 pll_idx,
+ s64 *phase_offset);
+
static inline u8 sit9531x_pll_page(u8 pll_idx)
{
return SIT9531X_PAGE_PLLA + pll_idx;
diff --git a/drivers/dpll/sit9531x/dpll.c b/drivers/dpll/sit9531x/dpll.c
index e6271ebd47ac..ba4ac5e7d365 100644
--- a/drivers/dpll/sit9531x/dpll.c
+++ b/drivers/dpll/sit9531x/dpll.c
@@ -488,6 +488,122 @@ sit9531x_dpll_input_pin_prio_set(const struct dpll_pin *pin, void *pin_priv,
return 0;
}
+/*
+ * Report the loop's residual phase error, sampled with the loop closed. The TDC
+ * compares against a signal the PLL drives, so a PLL driving no output returns
+ * -ENODATA.
+ */
+static int
+sit9531x_dpll_input_pin_phase_offset_get(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv, s64 *phase_offset,
+ struct netlink_ext_ack *extack)
+{
+ struct sit9531x_dpll_pin *dpin = pin_priv;
+ struct sit9531x_dpll *sitdpll = dpll_priv;
+ struct sit9531x_dev *sitdev = sitdpll->dev;
+ bool drives = false;
+ u8 routed, i;
+ s64 offset;
+ int rc;
+
+ mutex_lock(&sitdev->multiop_lock);
+
+ /*
+ * Check the cache first: the core calls this for every pin of every
+ * DPLL in a dump.
+ */
+ if (!sit9531x_dpll_selection_active(sitdev, sitdpll, dpin->id)) {
+ mutex_unlock(&sitdev->multiop_lock);
+ return -ENODATA;
+ }
+
+ /*
+ * The device can change the routed reference, so read it before and
+ * after the sample. A PLL with no routed, enabled output has no
+ * measurement.
+ */
+ for (i = 0; i < sitdev->info->num_outputs; i++) {
+ if (!sitdev->out[i].routed ||
+ sitdev->out[i].pll_idx != sitdpll->id)
+ continue;
+ if (sitdev->out[i].state_stale) {
+ rc = sit9531x_output_state_refresh(sitdev, i);
+ if (rc) {
+ mutex_unlock(&sitdev->multiop_lock);
+ NL_SET_ERR_MSG(extack,
+ "Output mute state could not be read back");
+ return rc;
+ }
+ }
+ if (sitdev->out[i].enabled)
+ drives = true;
+ }
+
+ if (!drives) {
+ mutex_unlock(&sitdev->multiop_lock);
+ return -ENODATA;
+ }
+
+ /*
+ * Read the monitors live: on loss of signal the device falls back
+ * without a request.
+ */
+ rc = sit9531x_input_mon_fetch(sitdev);
+ if (rc) {
+ mutex_unlock(&sitdev->multiop_lock);
+ NL_SET_ERR_MSG(extack,
+ "Failed to read the input clock monitors");
+ return rc;
+ }
+
+ if (!sit9531x_dpll_selection_active(sitdev, sitdpll, dpin->id)) {
+ mutex_unlock(&sitdev->multiop_lock);
+ return -ENODATA;
+ }
+
+ rc = sit9531x_chan_routed_ref_read(sitdev, sitdpll->id, &routed);
+ if (!rc && routed == dpin->id) {
+ rc = sit9531x_phase_offset_read(sitdev, sitdpll->id, &offset);
+ if (!rc)
+ rc = sit9531x_chan_routed_ref_read(sitdev, sitdpll->id,
+ &routed);
+ if (!rc && routed != dpin->id)
+ rc = -ENODATA;
+ } else if (!rc) {
+ rc = -ENODATA;
+ }
+
+ /*
+ * A reference lost during the sample can leave the routed reference
+ * unchanged while the PLL is in holdover; read the monitors again.
+ */
+ if (!rc) {
+ rc = sit9531x_input_mon_fetch(sitdev);
+ if (rc) {
+ mutex_unlock(&sitdev->multiop_lock);
+ NL_SET_ERR_MSG(extack,
+ "Failed to read the input clock monitors");
+ return rc;
+ }
+ if (dpin->id < sitdev->info->num_inputs &&
+ sit9531x_ref_state_get(sitdev, dpin->id)->los)
+ rc = -ENODATA;
+ }
+ mutex_unlock(&sitdev->multiop_lock);
+
+ if (rc == -ENODATA)
+ return rc;
+ if (rc) {
+ NL_SET_ERR_MSG(extack, "TDC phase readback failed");
+ return rc;
+ }
+
+ *phase_offset = offset;
+ return 0;
+}
+
static const struct dpll_pin_ops sit9531x_dpll_input_pin_ops = {
.direction_get = sit9531x_dpll_input_pin_direction_get,
.frequency_get = sit9531x_dpll_input_pin_frequency_get,
@@ -496,6 +612,7 @@ static const struct dpll_pin_ops sit9531x_dpll_input_pin_ops = {
.operstate_on_dpll_get = sit9531x_dpll_input_pin_operstate_on_dpll_get,
.prio_get = sit9531x_dpll_input_pin_prio_get,
.prio_set = sit9531x_dpll_input_pin_prio_set,
+ .phase_offset_get = sit9531x_dpll_input_pin_phase_offset_get,
};
/*
@@ -509,6 +626,7 @@ static const struct dpll_pin_ops sit9531x_dpll_input_pin_norate_ops = {
.operstate_on_dpll_get = sit9531x_dpll_input_pin_operstate_on_dpll_get,
.prio_get = sit9531x_dpll_input_pin_prio_get,
.prio_set = sit9531x_dpll_input_pin_prio_set,
+ .phase_offset_get = sit9531x_dpll_input_pin_phase_offset_get,
};
/*
@@ -520,12 +638,14 @@ static const struct dpll_pin_ops sit9531x_dpll_input_pin_manual_ops = {
.frequency_get = sit9531x_dpll_input_pin_frequency_get,
.state_on_dpll_get = sit9531x_dpll_input_pin_state_on_dpll_get,
.operstate_on_dpll_get = sit9531x_dpll_input_pin_operstate_on_dpll_get,
+ .phase_offset_get = sit9531x_dpll_input_pin_phase_offset_get,
};
static const struct dpll_pin_ops sit9531x_dpll_input_pin_norate_manual_ops = {
.direction_get = sit9531x_dpll_input_pin_direction_get,
.state_on_dpll_get = sit9531x_dpll_input_pin_state_on_dpll_get,
.operstate_on_dpll_get = sit9531x_dpll_input_pin_operstate_on_dpll_get,
+ .phase_offset_get = sit9531x_dpll_input_pin_phase_offset_get,
};
/*
diff --git a/drivers/dpll/sit9531x/regs.h b/drivers/dpll/sit9531x/regs.h
index 8c76609b99c5..d6c6b4a6a6bb 100644
--- a/drivers/dpll/sit9531x/regs.h
+++ b/drivers/dpll/sit9531x/regs.h
@@ -229,6 +229,25 @@
#define SIT9531X_PLL_REG_DIVN_NUM 0x32 /* 4 bytes (0x32-0x35) */
#define SIT9531X_PLL_REG_DIVN_DEN 0x38 /* 4 bytes (0x38-0x3B) */
+/*
+ * Signal pathway debug readback: Dig_Sys_ReadCode selects the tap,
+ * Dig_Sys_WriteCode its modifiers; a read of the debug status data register
+ * latches a sample into Dig_Sys_read7..read0.
+ */
+#define SIT9531X_PLL_REG_DBG_READ_CODE 0xB3
+#define SIT9531X_PLL_REG_DBG_WRITE_CODE 0xB4
+#define SIT9531X_DBG_LOW_FREQ_CLK_BIT BIT(7)
+#define SIT9531X_PLL_REG_DBG_DATA_0 0xB5 /* [7:0] */
+#define SIT9531X_PLL_REG_DBG_DATA_1 0xB6 /* [15:8] */
+#define SIT9531X_PLL_REG_DBG_DATA_2 0xB7 /* [23:16] */
+#define SIT9531X_PLL_REG_DBG_DATA_3 0xB8 /* [31:24] */
+#define SIT9531X_PLL_REG_DBG_DATA_4 0xB9 /* [34:32] + sign */
+
+/* Read code of the TDC phase tap, and the sign bit of its sample */
+#define SIT9531X_DBG_READ_CODE_TDC 69
+#define SIT9531X_TDC_SIGN_BIT 3
+#define SIT9531X_TDC_MAG_HI_MASK GENMASK(2, 0)
+
/* DIVN is carried as fixed point, in steps of 1e-12 of a whole divider */
#define SIT9531X_DIVN_SCALE 1000000000000ULL
--
2.39.2 (Apple Git-143)
next prev parent reply other threads:[~2026-11-10 20:26 UTC|newest]
Thread overview: 15+ 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 04/13] dpll: add basic SiTime SiT9531x support 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 06/13] dpll: sit9531x: register DPLL devices and pins 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 07/13] dpll: sit9531x: implement input pin state on a DPLL Ali Rouhi
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-10 20:26 ` Ali Rouhi [this message]
2026-11-10 20:26 ` [PATCH net-next v15 11/13] dpll: sit9531x: add support to adjust output phase Ali Rouhi
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
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=20261110202607.55744-13-arouhi@sitime.com \
--to=arouhi@sitime.com \
--cc=Oleg.Zadorozhnyi@devoxsoftware.com \
--cc=arkadiusz.kubalewski@intel.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=prabhakar.mahadev-lad.rj@bp.renesas.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®