[PATCH net-next 4/6] dpll: zl3073x: add DPLL channel status fields to zl3073x_chan

From: Ivan Vecera

Date: Wed Mar 11 2026 - 15:03:30 EST


Add mon_status and refsel_status fields to struct zl3073x_chan in a
stat group to cache the 'dpll_mon_status' and 'dpll_refsel_status'
registers.

Add zl3073x_chan_lock_state_get(), zl3073x_chan_is_ho_ready(),
zl3073x_chan_refsel_state_get() and zl3073x_chan_refsel_ref_get()
inline helpers for reading cached state, and zl3073x_chan_state_update()
for refreshing both registers from hardware. Call it from
zl3073x_chan_state_fetch() as well so that channel status is
initialized at device startup.

Call zl3073x_dev_chan_states_update() from the periodic work to
keep the cached state up to date and convert
zl3073x_dpll_lock_status_get() and zl3073x_dpll_selected_ref_get()
to use the cached state via the new helpers instead of direct register
reads.

Signed-off-by: Ivan Vecera <ivecera@xxxxxxxxxx>
---
drivers/dpll/zl3073x/chan.c | 32 +++++++++++++++++++++++
drivers/dpll/zl3073x/chan.h | 52 +++++++++++++++++++++++++++++++++++++
drivers/dpll/zl3073x/core.c | 17 ++++++++++++
drivers/dpll/zl3073x/dpll.c | 41 +++++++----------------------
4 files changed, 111 insertions(+), 31 deletions(-)

diff --git a/drivers/dpll/zl3073x/chan.c b/drivers/dpll/zl3073x/chan.c
index 6f383d489fab7..10189c8a5ded3 100644
--- a/drivers/dpll/zl3073x/chan.c
+++ b/drivers/dpll/zl3073x/chan.c
@@ -7,6 +7,27 @@
#include "chan.h"
#include "core.h"

+/**
+ * zl3073x_chan_state_update - update DPLL channel status from HW
+ * @zldev: pointer to zl3073x_dev structure
+ * @index: DPLL channel index
+ *
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index)
+{
+ struct zl3073x_chan *chan = &zldev->chan[index];
+ int rc;
+
+ rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MON_STATUS(index),
+ &chan->mon_status);
+ if (rc)
+ return rc;
+
+ return zl3073x_read_u8(zldev, ZL_REG_DPLL_REFSEL_STATUS(index),
+ &chan->refsel_status);
+}
+
/**
* zl3073x_chan_state_fetch - fetch DPLL channel state from hardware
* @zldev: pointer to zl3073x_dev structure
@@ -30,6 +51,17 @@ int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index)
dev_dbg(zldev->dev, "DPLL%u mode: %u, ref: %u\n", index,
zl3073x_chan_mode_get(chan), zl3073x_chan_ref_get(chan));

+ rc = zl3073x_chan_state_update(zldev, index);
+ if (rc)
+ return rc;
+
+ dev_dbg(zldev->dev,
+ "DPLL%u lock_state: %u, ho: %u, sel_state: %u, sel_ref: %u\n",
+ index, zl3073x_chan_lock_state_get(chan),
+ zl3073x_chan_is_ho_ready(chan) ? 1 : 0,
+ zl3073x_chan_refsel_state_get(chan),
+ zl3073x_chan_refsel_ref_get(chan));
+
return 0;
}

diff --git a/drivers/dpll/zl3073x/chan.h b/drivers/dpll/zl3073x/chan.h
index 3e6ffaef0c743..f73a076108551 100644
--- a/drivers/dpll/zl3073x/chan.h
+++ b/drivers/dpll/zl3073x/chan.h
@@ -14,11 +14,17 @@ struct zl3073x_dev;
/**
* struct zl3073x_chan - DPLL channel state
* @mode_refsel: mode and reference selection register value
+ * @mon_status: monitor status register value
+ * @refsel_status: reference selection status register value
*/
struct zl3073x_chan {
struct_group(cfg,
u8 mode_refsel;
);
+ struct_group(stat,
+ u8 mon_status;
+ u8 refsel_status;
+ );
};

int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index);
@@ -27,6 +33,8 @@ const struct zl3073x_chan *zl3073x_chan_state_get(struct zl3073x_dev *zldev,
int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index,
const struct zl3073x_chan *chan);

+int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index);
+
/**
* zl3073x_chan_mode_get - get DPLL channel operating mode
* @chan: pointer to channel state
@@ -71,4 +79,48 @@ static inline void zl3073x_chan_ref_set(struct zl3073x_chan *chan, u8 ref)
chan->mode_refsel |= FIELD_PREP(ZL_DPLL_MODE_REFSEL_REF, ref);
}

+/**
+ * zl3073x_chan_lock_state_get - get DPLL channel lock state
+ * @chan: pointer to channel state
+ *
+ * Return: lock state of the given DPLL channel
+ */
+static inline u8 zl3073x_chan_lock_state_get(const struct zl3073x_chan *chan)
+{
+ return FIELD_GET(ZL_DPLL_MON_STATUS_STATE, chan->mon_status);
+}
+
+/**
+ * zl3073x_chan_is_ho_ready - check if holdover is ready
+ * @chan: pointer to channel state
+ *
+ * Return: true if holdover is ready, false otherwise
+ */
+static inline bool zl3073x_chan_is_ho_ready(const struct zl3073x_chan *chan)
+{
+ return !!FIELD_GET(ZL_DPLL_MON_STATUS_HO_READY, chan->mon_status);
+}
+
+/**
+ * zl3073x_chan_refsel_state_get - get reference selection state
+ * @chan: pointer to channel state
+ *
+ * Return: reference selection state of the given DPLL channel
+ */
+static inline u8 zl3073x_chan_refsel_state_get(const struct zl3073x_chan *chan)
+{
+ return FIELD_GET(ZL_DPLL_REFSEL_STATUS_STATE, chan->refsel_status);
+}
+
+/**
+ * zl3073x_chan_refsel_ref_get - get currently selected reference in auto mode
+ * @chan: pointer to channel state
+ *
+ * Return: reference selected by the DPLL in automatic mode
+ */
+static inline u8 zl3073x_chan_refsel_ref_get(const struct zl3073x_chan *chan)
+{
+ return FIELD_GET(ZL_DPLL_REFSEL_STATUS_REFSEL, chan->refsel_status);
+}
+
#endif /* _ZL3073X_CHAN_H */
diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c
index b03e59fa0834b..6363002d48d46 100644
--- a/drivers/dpll/zl3073x/core.c
+++ b/drivers/dpll/zl3073x/core.c
@@ -566,6 +566,20 @@ zl3073x_dev_ref_states_update(struct zl3073x_dev *zldev)
}
}

+static void
+zl3073x_dev_chan_states_update(struct zl3073x_dev *zldev)
+{
+ int i, rc;
+
+ for (i = 0; i < zldev->info->num_channels; i++) {
+ rc = zl3073x_chan_state_update(zldev, i);
+ if (rc)
+ dev_warn(zldev->dev,
+ "Failed to get DPLL%u state: %pe\n", i,
+ ERR_PTR(rc));
+ }
+}
+
/**
* zl3073x_ref_phase_offsets_update - update reference phase offsets
* @zldev: pointer to zl3073x_dev structure
@@ -691,6 +705,9 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
/* Update input references' states */
zl3073x_dev_ref_states_update(zldev);

+ /* Update DPLL channels' states */
+ zl3073x_dev_chan_states_update(zldev);
+
/* Update DPLL-to-connected-ref phase offsets registers */
rc = zl3073x_ref_phase_offsets_update(zldev, -1);
if (rc)
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
index f56f073e57df4..49e3f9f130848 100644
--- a/drivers/dpll/zl3073x/dpll.c
+++ b/drivers/dpll/zl3073x/dpll.c
@@ -258,28 +258,16 @@ zl3073x_dpll_input_pin_frequency_set(const struct dpll_pin *dpll_pin,
static int
zl3073x_dpll_selected_ref_get(struct zl3073x_dpll *zldpll, u8 *ref)
{
- struct zl3073x_dev *zldev = zldpll->dev;
const struct zl3073x_chan *chan;
- u8 state, value;
- int rc;

- chan = zl3073x_chan_state_get(zldev, zldpll->id);
+ chan = zl3073x_chan_state_get(zldpll->dev, zldpll->id);

switch (zl3073x_chan_mode_get(chan)) {
case ZL_DPLL_MODE_REFSEL_MODE_AUTO:
- /* For automatic mode read refsel_status register */
- rc = zl3073x_read_u8(zldev,
- ZL_REG_DPLL_REFSEL_STATUS(zldpll->id),
- &value);
- if (rc)
- return rc;
-
- /* Extract reference state */
- state = FIELD_GET(ZL_DPLL_REFSEL_STATUS_STATE, value);
-
/* Return the reference only if the DPLL is locked to it */
- if (state == ZL_DPLL_REFSEL_STATUS_STATE_LOCK)
- *ref = FIELD_GET(ZL_DPLL_REFSEL_STATUS_REFSEL, value);
+ if (zl3073x_chan_refsel_state_get(chan) ==
+ ZL_DPLL_REFSEL_STATUS_STATE_LOCK)
+ *ref = zl3073x_chan_refsel_ref_get(chan);
else
*ref = ZL3073X_DPLL_REF_NONE;
break;
@@ -1089,12 +1077,9 @@ zl3073x_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv,
struct netlink_ext_ack *extack)
{
struct zl3073x_dpll *zldpll = dpll_priv;
- struct zl3073x_dev *zldev = zldpll->dev;
const struct zl3073x_chan *chan;
- u8 mon_status, state;
- int rc;

- chan = zl3073x_chan_state_get(zldev, zldpll->id);
+ chan = zl3073x_chan_state_get(zldpll->dev, zldpll->id);

switch (zl3073x_chan_mode_get(chan)) {
case ZL_DPLL_MODE_REFSEL_MODE_FREERUN:
@@ -1107,16 +1092,9 @@ zl3073x_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv,
break;
}

- /* Read DPLL monitor status */
- rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MON_STATUS(zldpll->id),
- &mon_status);
- if (rc)
- return rc;
- state = FIELD_GET(ZL_DPLL_MON_STATUS_STATE, mon_status);
-
- switch (state) {
+ switch (zl3073x_chan_lock_state_get(chan)) {
case ZL_DPLL_MON_STATUS_STATE_LOCK:
- if (FIELD_GET(ZL_DPLL_MON_STATUS_HO_READY, mon_status))
+ if (zl3073x_chan_is_ho_ready(chan))
*status = DPLL_LOCK_STATUS_LOCKED_HO_ACQ;
else
*status = DPLL_LOCK_STATUS_LOCKED;
@@ -1126,8 +1104,9 @@ zl3073x_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv,
*status = DPLL_LOCK_STATUS_HOLDOVER;
break;
default:
- dev_warn(zldev->dev, "Unknown DPLL monitor status: 0x%02x\n",
- mon_status);
+ dev_warn(zldpll->dev->dev,
+ "Unknown DPLL monitor status: 0x%02x\n",
+ chan->mon_status);
*status = DPLL_LOCK_STATUS_UNLOCKED;
break;
}
--
2.52.0