[PATCH 6.18 428/752] dpll: zl3073x: Cache reference monitor status

Sasha Levin
2026-02-28T17:54:06Z

From: Ivan Vecera <ivecera@redhat.com>

[ Upstream commit 5534a8202d7cf9b2e4557517745583c43662b6a8 ]

Instead of reading the ZL_REG_REF_MON_STATUS register every time
the reference status is needed, cache this value in the zl3073x_ref
struct.

This is achieved by:
* Adding a mon_status field to struct zl3073x_ref
* Introducing zl3073x_dev_ref_status_update() to read the status for
all references into this new cache field
* Calling this update function from the periodic work handler
* Adding zl3073x_ref_is_status_ok() and zl3073x_dev_ref_is_status_ok()
helpers to check the cached value
* Refactoring all callers in dpll.c to use the new
zl3073x_dev_ref_is_status_ok() helper, removing direct register reads

This change consolidates all status register reads into a single periodic
function and reduces I/O bus traffic in dpll callbacks.

Reviewed-by: Petr Oros <poros@redhat.com>
Tested-by: Prathosh Satish <Prathosh.Satish@microchip.com>
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
Link: https://patch.msgid.link/20251113074105.141379-4-ivecera@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Stable-dep-of: 4cfe066a82cd ("dpll: zl3073x: fix REF_PHASE_OFFSET_COMP register width for some chip IDs")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
drivers/dpll/zl3073x/core.c | 18 +++++++
drivers/dpll/zl3073x/core.h | 15 ++++++
drivers/dpll/zl3073x/dpll.c | 96 ++++++++-----------------------------
drivers/dpll/zl3073x/ref.h | 14 ++++++
4 files changed, 68 insertions(+), 75 deletions(-)

diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c
index 2f340f7eb9ec3..383e2397dd033 100644
--- a/drivers/dpll/zl3073x/core.c
+++ b/drivers/dpll/zl3073x/core.c
@@ -591,6 +591,21 @@ zl3073x_dev_state_fetch(struct zl3073x_dev *zldev)
return rc;
}

+static void
+zl3073x_dev_ref_status_update(struct zl3073x_dev *zldev)
+{
+ int i, rc;
+
+ for (i = 0; i < ZL3073X_NUM_REFS; i++) {
+ rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(i),
+ &zldev->ref[i].mon_status);
+ if (rc)
+ dev_warn(zldev->dev,
+ "Failed to get REF%u status: %pe\n", i,
+ ERR_PTR(rc));
+ }
+}
+
/**
* zl3073x_ref_phase_offsets_update - update reference phase offsets
* @zldev: pointer to zl3073x_dev structure
@@ -710,6 +725,9 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
struct zl3073x_dpll *zldpll;
int rc;

+ /* Update input references status */
+ zl3073x_dev_ref_status_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/core.h b/drivers/dpll/zl3073x/core.h
index fe779fc77dd09..4148580d1f343 100644
--- a/drivers/dpll/zl3073x/core.h
+++ b/drivers/dpll/zl3073x/core.h
@@ -227,6 +227,21 @@ zl3073x_dev_ref_is_enabled(struct zl3073x_dev *zldev, u8 index)
return zl3073x_ref_is_enabled(ref);
}

+/*
+ * zl3073x_dev_ref_is_status_ok - check the given input reference status
+ * @zldev: pointer to zl3073x device
+ * @index: input reference index
+ *
+ * Return: true if the status is ok, false otherwise
+ */
+static inline bool
+zl3073x_dev_ref_is_status_ok(struct zl3073x_dev *zldev, u8 index)
+{
+ const struct zl3073x_ref *ref = zl3073x_ref_state_get(zldev, index);
+
+ return zl3073x_ref_is_status_ok(ref);
+}
+
/**
* zl3073x_dev_synth_dpll_get - get DPLL ID the synth is driven by
* @zldev: pointer to zl3073x device
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
index 11ca32e1bb829..29a3b55bc823c 100644
--- a/drivers/dpll/zl3073x/dpll.c
+++ b/drivers/dpll/zl3073x/dpll.c
@@ -497,19 +497,10 @@ zl3073x_dpll_connected_ref_get(struct zl3073x_dpll *zldpll, u8 *ref)
if (rc)
return rc;

- if (ZL3073X_DPLL_REF_IS_VALID(*ref)) {
- u8 ref_status;
-
- /* Read the reference monitor status */
- rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(*ref),
- &ref_status);
- if (rc)
- return rc;
-
- /* If the monitor indicates an error nothing is connected */
- if (ref_status != ZL_REF_MON_STATUS_OK)
- *ref = ZL3073X_DPLL_REF_NONE;
- }
+ /* If the monitor indicates an error nothing is connected */
+ if (ZL3073X_DPLL_REF_IS_VALID(*ref) &&
+ !zl3073x_dev_ref_is_status_ok(zldev, *ref))
+ *ref = ZL3073X_DPLL_REF_NONE;

return 0;
}
@@ -524,7 +515,7 @@ zl3073x_dpll_input_pin_phase_offset_get(const struct dpll_pin *dpll_pin,
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
- u8 conn_ref, ref, ref_status;
+ u8 conn_ref, ref;
s64 ref_phase;
int rc;

@@ -537,21 +528,9 @@ zl3073x_dpll_input_pin_phase_offset_get(const struct dpll_pin *dpll_pin,
* monitor feature is disabled.
*/
ref = zl3073x_input_pin_ref_get(pin->id);
- if (!zldpll->phase_monitor && ref != conn_ref) {
- *phase_offset = 0;
-
- return 0;
- }
-
- /* Get this pin monitor status */
- rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref), &ref_status);
- if (rc)
- return rc;
-
- /* Report phase offset only if the input pin signal is present */
- if (ref_status != ZL_REF_MON_STATUS_OK) {
+ if ((!zldpll->phase_monitor && ref != conn_ref) ||
+ !zl3073x_dev_ref_is_status_ok(zldev, ref)) {
*phase_offset = 0;
-
return 0;
}

@@ -777,7 +756,7 @@ zl3073x_dpll_ref_state_get(struct zl3073x_dpll_pin *pin,
{
struct zl3073x_dpll *zldpll = pin->dpll;
struct zl3073x_dev *zldev = zldpll->dev;
- u8 ref, ref_conn, status;
+ u8 ref, ref_conn;
int rc;

ref = zl3073x_input_pin_ref_get(pin->id);
@@ -797,20 +776,9 @@ zl3073x_dpll_ref_state_get(struct zl3073x_dpll_pin *pin,
* pin as selectable.
*/
if (zldpll->refsel_mode == ZL_DPLL_MODE_REFSEL_MODE_AUTO &&
- pin->selectable) {
- /* Read reference monitor status */
- rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref),
- &status);
- if (rc)
- return rc;
-
- /* If the monitor indicates errors report the reference
- * as disconnected
- */
- if (status == ZL_REF_MON_STATUS_OK) {
- *state = DPLL_PIN_STATE_SELECTABLE;
- return 0;
- }
+ zl3073x_dev_ref_is_status_ok(zldev, ref) && pin->selectable) {
+ *state = DPLL_PIN_STATE_SELECTABLE;
+ return 0;
}

/* Otherwise report the pin as disconnected */
@@ -1832,37 +1800,23 @@ zl3073x_dpll_pin_phase_offset_check(struct zl3073x_dpll_pin *pin)

ref = zl3073x_input_pin_ref_get(pin->id);

+ /* No phase offset if the ref monitor reports signal errors */
+ if (!zl3073x_dev_ref_is_status_ok(zldev, ref))
+ return false;
+
/* Select register to read phase offset value depending on pin and
* phase monitor state:
* 1) For connected pin use dpll_phase_err_data register
* 2) For other pins use appropriate ref_phase register if the phase
- * monitor feature is enabled and reference monitor does not
- * report signal errors for given input pin
+ * monitor feature is enabled.
*/
- if (pin->pin_state == DPLL_PIN_STATE_CONNECTED) {
+ if (pin->pin_state == DPLL_PIN_STATE_CONNECTED)
reg = ZL_REG_DPLL_PHASE_ERR_DATA(zldpll->id);
- } else if (zldpll->phase_monitor) {
- u8 status;
-
- /* Get reference monitor status */
- rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref),
- &status);
- if (rc) {
- dev_err(zldev->dev,
- "Failed to read %s refmon status: %pe\n",
- pin->label, ERR_PTR(rc));
-
- return false;
- }
-
- if (status != ZL_REF_MON_STATUS_OK)
- return false;
-
+ else if (zldpll->phase_monitor)
reg = ZL_REG_REF_PHASE(ref);
- } else {
+ else
/* The pin is not connected or phase monitor disabled */
return false;
- }

/* Read measured phase offset value */
rc = zl3073x_read_u48(zldev, reg, &phase_offset);
@@ -1901,22 +1855,14 @@ zl3073x_dpll_pin_ffo_check(struct zl3073x_dpll_pin *pin)
{
struct zl3073x_dpll *zldpll = pin->dpll;
struct zl3073x_dev *zldev = zldpll->dev;
- u8 ref, status;
s64 ffo;
- int rc;
+ u8 ref;

/* Get reference monitor status */
ref = zl3073x_input_pin_ref_get(pin->id);
- rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref), &status);
- if (rc) {
- dev_err(zldev->dev, "Failed to read %s refmon status: %pe\n",
- pin->label, ERR_PTR(rc));
-
- return false;
- }

/* Do not report ffo changes if the reference monitor report errors */
- if (status != ZL_REF_MON_STATUS_OK)
+ if (!zl3073x_dev_ref_is_status_ok(zldev, ref))
return false;

/* Get the latest measured ref's ffo */
diff --git a/drivers/dpll/zl3073x/ref.h b/drivers/dpll/zl3073x/ref.h
index e72f2c8750876..c4931e545d24d 100644
--- a/drivers/dpll/zl3073x/ref.h
+++ b/drivers/dpll/zl3073x/ref.h
@@ -14,10 +14,12 @@ struct zl3073x_dev;
* struct zl3073x_ref - input reference state
* @ffo: current fractional frequency offset
* @config: reference config
+ * @mon_status: reference monitor status
*/
struct zl3073x_ref {
s64 ffo;
u8 config;
+ u8 mon_status;
};

int zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index);
@@ -63,4 +65,16 @@ zl3073x_ref_is_enabled(const struct zl3073x_ref *ref)
return !!FIELD_GET(ZL_REF_CONFIG_ENABLE, ref->config);
}

+/**
+ * zl3073x_ref_is_status_ok - check the given input reference status
+ * @ref: pointer to ref state
+ *
+ * Return: true if the status is ok, false otherwise
+ */
+static inline bool
+zl3073x_ref_is_status_ok(const struct zl3073x_ref *ref)
+{
+ return ref->mon_status == ZL_REF_MON_STATUS_OK;
+}
+
#endif /* _ZL3073X_REF_H */
--
2.51.0