* [PATCH net-next 0/3] dpll: add actual frequency monitoring feature
@ 2026-03-25 19:39 Ivan Vecera
2026-03-25 19:39 ` [PATCH net-next 1/3] dpll: add actual frequency monitoring to netlink spec Ivan Vecera
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Ivan Vecera @ 2026-03-25 19:39 UTC (permalink / raw)
To: netdev
Cc: Vadim Fedorenko, Arkadiusz Kubalewski, Jiri Pirko,
Jonathan Corbet, Shuah Khan, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Donald Hunter,
Prathosh Satish, Petr Oros, linux-doc, linux-kernel
This series adds support for monitoring the actual (measured) input
frequency of DPLL input pins via the DPLL netlink interface.
Some DPLL devices can measure the actual frequency being received on
input pins. Previously, the ZL3073x driver exposed this through the
hwmon interface using custom sysfs attributes, but this was rejected
as frequency is not a valid hwmon attribute. This series implements the
feature properly through the DPLL netlink interface instead.
The approach mirrors the existing phase-offset-monitor feature:
a device-level attribute (DPLL_A_FREQUENCY_MONITOR) enables or
disables monitoring, and a per-pin attribute (DPLL_A_PIN_ACTUAL_FREQUENCY)
exposes the measured frequency in Hz when monitoring is enabled.
Patch 1 adds the new attributes to the DPLL netlink spec (dpll.yaml),
regenerates the auto-generated UAPI header and netlink policy, and
updates Documentation/driver-api/dpll.rst.
Patch 2 adds the callback operations (freq_monitor_get/set for
devices, actual_freq_get for pins) and the corresponding netlink GET/SET
handlers in the DPLL core. The core only invokes actual_freq_get when
the frequency monitor is enabled on the parent device.
Patch 3 implements the feature in the ZL3073x driver by extracting
a common measurement latch helper from the existing FFO update path,
adding a frequency measurement function, and wiring up the new
callbacks.
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
Ivan Vecera (3):
dpll: add actual frequency monitoring to netlink spec
dpll: add actual frequency monitoring callback ops
dpll: zl3073x: implement actual frequency monitoring
Documentation/driver-api/dpll.rst | 18 ++++++
Documentation/netlink/specs/dpll.yaml | 17 +++++
drivers/dpll/dpll_netlink.c | 90 +++++++++++++++++++++++++++
drivers/dpll/dpll_nl.c | 5 +-
drivers/dpll/zl3073x/core.c | 88 ++++++++++++++++++++++----
drivers/dpll/zl3073x/dpll.c | 88 +++++++++++++++++++++++++-
drivers/dpll/zl3073x/dpll.h | 2 +
drivers/dpll/zl3073x/ref.h | 14 +++++
include/linux/dpll.h | 12 ++++
include/uapi/linux/dpll.h | 2 +
10 files changed, 320 insertions(+), 16 deletions(-)
--
2.52.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH net-next 1/3] dpll: add actual frequency monitoring to netlink spec
2026-03-25 19:39 [PATCH net-next 0/3] dpll: add actual frequency monitoring feature Ivan Vecera
@ 2026-03-25 19:39 ` Ivan Vecera
2026-03-26 11:06 ` Vadim Fedorenko
2026-03-25 19:39 ` [PATCH net-next 2/3] dpll: add actual frequency monitoring callback ops Ivan Vecera
2026-03-25 19:39 ` [PATCH net-next 3/3] dpll: zl3073x: implement actual frequency monitoring Ivan Vecera
2 siblings, 1 reply; 6+ messages in thread
From: Ivan Vecera @ 2026-03-25 19:39 UTC (permalink / raw)
To: netdev
Cc: Vadim Fedorenko, Arkadiusz Kubalewski, Jiri Pirko,
Jonathan Corbet, Shuah Khan, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Donald Hunter,
Prathosh Satish, Petr Oros, linux-doc, linux-kernel
Add DPLL_A_FREQUENCY_MONITOR device attribute to allow control over
the frequency monitor feature. The attribute uses the existing
dpll_feature_state enum (enable/disable) and is present in both
device-get reply and device-set request.
Add DPLL_A_PIN_ACTUAL_FREQUENCY pin attribute to expose the measured
input frequency in Hz. The attribute is present in the pin-get reply.
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
Documentation/driver-api/dpll.rst | 18 ++++++++++++++++++
Documentation/netlink/specs/dpll.yaml | 17 +++++++++++++++++
drivers/dpll/dpll_nl.c | 5 +++--
include/uapi/linux/dpll.h | 2 ++
4 files changed, 40 insertions(+), 2 deletions(-)
diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-api/dpll.rst
index 83118c728ed90..5634111e651df 100644
--- a/Documentation/driver-api/dpll.rst
+++ b/Documentation/driver-api/dpll.rst
@@ -250,6 +250,22 @@ in the ``DPLL_A_PIN_PHASE_OFFSET`` attribute.
``DPLL_A_PHASE_OFFSET_MONITOR`` attr state of a feature
=============================== ========================
+Frequency monitor
+=================
+
+Some DPLL devices may offer the capability to measure the actual
+frequency of all available input pins. The attribute and current feature state
+shall be included in the response message of the ``DPLL_CMD_DEVICE_GET``
+command for supported DPLL devices. In such cases, users can also control
+the feature using the ``DPLL_CMD_DEVICE_SET`` command by setting the
+``enum dpll_feature_state`` values for the attribute.
+Once enabled the measured input frequency for each input pin shall be
+returned in the ``DPLL_A_PIN_ACTUAL_FREQUENCY`` attribute.
+
+ =============================== ========================
+ ``DPLL_A_FREQUENCY_MONITOR`` attr state of a feature
+ =============================== ========================
+
Embedded SYNC
=============
@@ -411,6 +427,8 @@ according to attribute purpose.
``DPLL_A_PIN_STATE`` attr state of pin on the parent
pin
``DPLL_A_PIN_CAPABILITIES`` attr bitmask of pin capabilities
+ ``DPLL_A_PIN_ACTUAL_FREQUENCY`` attr measured actual frequency of
+ an input pin in Hz
==================================== ==================================
==================================== =================================
diff --git a/Documentation/netlink/specs/dpll.yaml b/Documentation/netlink/specs/dpll.yaml
index 3dd48a32f7837..84ccc686db4dd 100644
--- a/Documentation/netlink/specs/dpll.yaml
+++ b/Documentation/netlink/specs/dpll.yaml
@@ -319,6 +319,13 @@ attribute-sets:
name: phase-offset-avg-factor
type: u32
doc: Averaging factor applied to calculation of reported phase offset.
+ -
+ name: frequency-monitor
+ type: u32
+ enum: feature-state
+ doc: Receive or request state of frequency monitor feature.
+ If enabled, dpll device shall monitor all currently available
+ inputs for their actual (measured) input frequency.
-
name: pin
enum-name: dpll_a_pin
@@ -456,6 +463,13 @@ attribute-sets:
Value is in PPT (parts per trillion, 10^-12).
Note: This attribute provides higher resolution than the standard
fractional-frequency-offset (which is in PPM).
+ -
+ name: actual-frequency
+ type: u64
+ doc: |
+ The actual (measured) frequency of the input pin in Hz.
+ This is the actual frequency being received on the pin,
+ as measured by the dpll device hardware.
-
name: pin-parent-device
@@ -544,6 +558,7 @@ operations:
- type
- phase-offset-monitor
- phase-offset-avg-factor
+ - frequency-monitor
dump:
reply: *dev-attrs
@@ -563,6 +578,7 @@ operations:
- mode
- phase-offset-monitor
- phase-offset-avg-factor
+ - frequency-monitor
-
name: device-create-ntf
doc: Notification about device appearing
@@ -643,6 +659,7 @@ operations:
- esync-frequency-supported
- esync-pulse
- reference-sync
+ - actual-frequency
dump:
request:
diff --git a/drivers/dpll/dpll_nl.c b/drivers/dpll/dpll_nl.c
index a2b22d4921142..1e652340a5d73 100644
--- a/drivers/dpll/dpll_nl.c
+++ b/drivers/dpll/dpll_nl.c
@@ -43,11 +43,12 @@ static const struct nla_policy dpll_device_get_nl_policy[DPLL_A_ID + 1] = {
};
/* DPLL_CMD_DEVICE_SET - do */
-static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_PHASE_OFFSET_AVG_FACTOR + 1] = {
+static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_FREQUENCY_MONITOR + 1] = {
[DPLL_A_ID] = { .type = NLA_U32, },
[DPLL_A_MODE] = NLA_POLICY_RANGE(NLA_U32, 1, 2),
[DPLL_A_PHASE_OFFSET_MONITOR] = NLA_POLICY_MAX(NLA_U32, 1),
[DPLL_A_PHASE_OFFSET_AVG_FACTOR] = { .type = NLA_U32, },
+ [DPLL_A_FREQUENCY_MONITOR] = NLA_POLICY_MAX(NLA_U32, 1),
};
/* DPLL_CMD_PIN_ID_GET - do */
@@ -115,7 +116,7 @@ static const struct genl_split_ops dpll_nl_ops[] = {
.doit = dpll_nl_device_set_doit,
.post_doit = dpll_post_doit,
.policy = dpll_device_set_nl_policy,
- .maxattr = DPLL_A_PHASE_OFFSET_AVG_FACTOR,
+ .maxattr = DPLL_A_FREQUENCY_MONITOR,
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
},
{
diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
index de0005f28e5c5..f7be0b4c123e9 100644
--- a/include/uapi/linux/dpll.h
+++ b/include/uapi/linux/dpll.h
@@ -218,6 +218,7 @@ enum dpll_a {
DPLL_A_CLOCK_QUALITY_LEVEL,
DPLL_A_PHASE_OFFSET_MONITOR,
DPLL_A_PHASE_OFFSET_AVG_FACTOR,
+ DPLL_A_FREQUENCY_MONITOR,
__DPLL_A_MAX,
DPLL_A_MAX = (__DPLL_A_MAX - 1)
@@ -254,6 +255,7 @@ enum dpll_a_pin {
DPLL_A_PIN_REFERENCE_SYNC,
DPLL_A_PIN_PHASE_ADJUST_GRAN,
DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT,
+ DPLL_A_PIN_ACTUAL_FREQUENCY,
__DPLL_A_PIN_MAX,
DPLL_A_PIN_MAX = (__DPLL_A_PIN_MAX - 1)
--
2.52.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH net-next 2/3] dpll: add actual frequency monitoring callback ops
2026-03-25 19:39 [PATCH net-next 0/3] dpll: add actual frequency monitoring feature Ivan Vecera
2026-03-25 19:39 ` [PATCH net-next 1/3] dpll: add actual frequency monitoring to netlink spec Ivan Vecera
@ 2026-03-25 19:39 ` Ivan Vecera
2026-03-26 11:21 ` Vadim Fedorenko
2026-03-25 19:39 ` [PATCH net-next 3/3] dpll: zl3073x: implement actual frequency monitoring Ivan Vecera
2 siblings, 1 reply; 6+ messages in thread
From: Ivan Vecera @ 2026-03-25 19:39 UTC (permalink / raw)
To: netdev
Cc: Vadim Fedorenko, Arkadiusz Kubalewski, Jiri Pirko,
Jonathan Corbet, Shuah Khan, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Donald Hunter,
Prathosh Satish, Petr Oros, linux-doc, linux-kernel
Add new callback operations for a dpll device:
- freq_monitor_get(..) - to obtain current state of frequency monitor
feature from dpll device,
- freq_monitor_set(..) - to allow feature configuration.
Add new callback operation for a dpll pin:
- actual_freq_get(..) - to obtain the actual (measured) frequency in Hz.
Obtain the feature state value using the get callback and provide it to
the user if the device driver implements callbacks. The actual_freq_get
pin callback is only invoked when the frequency monitor is enabled.
Execute the set callback upon user requests.
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
drivers/dpll/dpll_netlink.c | 90 +++++++++++++++++++++++++++++++++++++
include/linux/dpll.h | 12 +++++
2 files changed, 102 insertions(+)
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index 83cbd64abf5a4..6fe4ae38cff62 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -175,6 +175,26 @@ dpll_msg_add_phase_offset_monitor(struct sk_buff *msg, struct dpll_device *dpll,
return 0;
}
+static int
+dpll_msg_add_freq_monitor(struct sk_buff *msg, struct dpll_device *dpll,
+ struct netlink_ext_ack *extack)
+{
+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
+ enum dpll_feature_state state;
+ int ret;
+
+ if (ops->freq_monitor_set && ops->freq_monitor_get) {
+ ret = ops->freq_monitor_get(dpll, dpll_priv(dpll),
+ &state, extack);
+ if (ret)
+ return ret;
+ if (nla_put_u32(msg, DPLL_A_FREQUENCY_MONITOR, state))
+ return -EMSGSIZE;
+ }
+
+ return 0;
+}
+
static int
dpll_msg_add_phase_offset_avg_factor(struct sk_buff *msg,
struct dpll_device *dpll,
@@ -400,6 +420,38 @@ static int dpll_msg_add_ffo(struct sk_buff *msg, struct dpll_pin *pin,
ffo);
}
+static int dpll_msg_add_actual_freq(struct sk_buff *msg, struct dpll_pin *pin,
+ struct dpll_pin_ref *ref,
+ struct netlink_ext_ack *extack)
+{
+ const struct dpll_device_ops *dev_ops = dpll_device_ops(ref->dpll);
+ const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+ struct dpll_device *dpll = ref->dpll;
+ enum dpll_feature_state state;
+ u64 actual_freq;
+ int ret;
+
+ if (!ops->actual_freq_get)
+ return 0;
+ if (dev_ops->freq_monitor_get) {
+ ret = dev_ops->freq_monitor_get(dpll, dpll_priv(dpll),
+ &state, extack);
+ if (ret)
+ return ret;
+ if (state == DPLL_FEATURE_STATE_DISABLE)
+ return 0;
+ }
+ ret = ops->actual_freq_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
+ dpll, dpll_priv(dpll), &actual_freq, extack);
+ if (ret)
+ return ret;
+ if (nla_put_64bit(msg, DPLL_A_PIN_ACTUAL_FREQUENCY,
+ sizeof(actual_freq), &actual_freq, DPLL_A_PIN_PAD))
+ return -EMSGSIZE;
+
+ return 0;
+}
+
static int
dpll_msg_add_pin_freq(struct sk_buff *msg, struct dpll_pin *pin,
struct dpll_pin_ref *ref, struct netlink_ext_ack *extack)
@@ -670,6 +722,9 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
if (ret)
return ret;
ret = dpll_msg_add_ffo(msg, pin, ref, extack);
+ if (ret)
+ return ret;
+ ret = dpll_msg_add_actual_freq(msg, pin, ref, extack);
if (ret)
return ret;
ret = dpll_msg_add_pin_esync(msg, pin, ref, extack);
@@ -722,6 +777,9 @@ dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
if (ret)
return ret;
ret = dpll_msg_add_phase_offset_avg_factor(msg, dpll, extack);
+ if (ret)
+ return ret;
+ ret = dpll_msg_add_freq_monitor(msg, dpll, extack);
if (ret)
return ret;
@@ -948,6 +1006,32 @@ dpll_phase_offset_avg_factor_set(struct dpll_device *dpll, struct nlattr *a,
extack);
}
+static int
+dpll_freq_monitor_set(struct dpll_device *dpll, struct nlattr *a,
+ struct netlink_ext_ack *extack)
+{
+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
+ enum dpll_feature_state state = nla_get_u32(a), old_state;
+ int ret;
+
+ if (!(ops->freq_monitor_set && ops->freq_monitor_get)) {
+ NL_SET_ERR_MSG_ATTR(extack, a,
+ "dpll device not capable of frequency monitor");
+ return -EOPNOTSUPP;
+ }
+ ret = ops->freq_monitor_get(dpll, dpll_priv(dpll), &old_state,
+ extack);
+ if (ret) {
+ NL_SET_ERR_MSG(extack,
+ "unable to get current state of frequency monitor");
+ return ret;
+ }
+ if (state == old_state)
+ return 0;
+
+ return ops->freq_monitor_set(dpll, dpll_priv(dpll), state, extack);
+}
+
static int
dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
struct netlink_ext_ack *extack)
@@ -1878,6 +1962,12 @@ dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
if (ret)
return ret;
break;
+ case DPLL_A_FREQUENCY_MONITOR:
+ ret = dpll_freq_monitor_set(dpll, a,
+ info->extack);
+ if (ret)
+ return ret;
+ break;
}
}
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index 2ce295b46b8cd..d14cdc2a37f62 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -52,6 +52,14 @@ struct dpll_device_ops {
int (*phase_offset_avg_factor_get)(const struct dpll_device *dpll,
void *dpll_priv, u32 *factor,
struct netlink_ext_ack *extack);
+ int (*freq_monitor_set)(const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_feature_state state,
+ struct netlink_ext_ack *extack);
+ int (*freq_monitor_get)(const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_feature_state *state,
+ struct netlink_ext_ack *extack);
};
struct dpll_pin_ops {
@@ -110,6 +118,10 @@ struct dpll_pin_ops {
int (*ffo_get)(const struct dpll_pin *pin, void *pin_priv,
const struct dpll_device *dpll, void *dpll_priv,
s64 *ffo, struct netlink_ext_ack *extack);
+ int (*actual_freq_get)(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ u64 *actual_freq,
+ struct netlink_ext_ack *extack);
int (*esync_set)(const struct dpll_pin *pin, void *pin_priv,
const struct dpll_device *dpll, void *dpll_priv,
u64 freq, struct netlink_ext_ack *extack);
--
2.52.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH net-next 3/3] dpll: zl3073x: implement actual frequency monitoring
2026-03-25 19:39 [PATCH net-next 0/3] dpll: add actual frequency monitoring feature Ivan Vecera
2026-03-25 19:39 ` [PATCH net-next 1/3] dpll: add actual frequency monitoring to netlink spec Ivan Vecera
2026-03-25 19:39 ` [PATCH net-next 2/3] dpll: add actual frequency monitoring callback ops Ivan Vecera
@ 2026-03-25 19:39 ` Ivan Vecera
2 siblings, 0 replies; 6+ messages in thread
From: Ivan Vecera @ 2026-03-25 19:39 UTC (permalink / raw)
To: netdev
Cc: Vadim Fedorenko, Arkadiusz Kubalewski, Jiri Pirko,
Jonathan Corbet, Shuah Khan, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Donald Hunter,
Prathosh Satish, Petr Oros, linux-doc, linux-kernel
Extract common measurement latch logic from zl3073x_ref_ffo_update()
into a new zl3073x_ref_freq_meas_latch() helper and add
zl3073x_ref_freq_meas_update() that uses it to latch and read absolute
input reference frequencies in Hz.
Add meas_freq field to struct zl3073x_ref and the corresponding
zl3073x_ref_meas_freq_get() accessor. The measured frequencies are
updated periodically alongside the existing FFO measurements.
Add freq_monitor boolean to struct zl3073x_dpll and implement the
freq_monitor_set/get device callbacks to enable/disable frequency
monitoring via the DPLL netlink interface.
Implement actual_freq_get pin callback for input pins that returns the
measured input frequency in Hz.
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
drivers/dpll/zl3073x/core.c | 88 +++++++++++++++++++++++++++++++------
drivers/dpll/zl3073x/dpll.c | 88 ++++++++++++++++++++++++++++++++++++-
drivers/dpll/zl3073x/dpll.h | 2 +
drivers/dpll/zl3073x/ref.h | 14 ++++++
4 files changed, 178 insertions(+), 14 deletions(-)
diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c
index 6363002d48d46..320c199637efa 100644
--- a/drivers/dpll/zl3073x/core.c
+++ b/drivers/dpll/zl3073x/core.c
@@ -632,22 +632,21 @@ int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel)
}
/**
- * zl3073x_ref_ffo_update - update reference fractional frequency offsets
+ * zl3073x_ref_freq_meas_latch - latch reference frequency measurements
* @zldev: pointer to zl3073x_dev structure
+ * @type: measurement type (ZL_REF_FREQ_MEAS_CTRL_*)
*
- * The function asks device to update fractional frequency offsets latch
- * registers the latest measured values, reads and stores them into
+ * The function waits for the previous measurement to finish, selects all
+ * references and requests a new measurement of the given type.
*
* Return: 0 on success, <0 on error
*/
static int
-zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
+zl3073x_ref_freq_meas_latch(struct zl3073x_dev *zldev, u8 type)
{
- int i, rc;
+ int rc;
- /* Per datasheet we have to wait for 'ref_freq_meas_ctrl' to be zero
- * to ensure that the measured data are coherent.
- */
+ /* Wait for previous measurement to finish */
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
ZL_REF_FREQ_MEAS_CTRL);
if (rc)
@@ -663,15 +662,64 @@ zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
if (rc)
return rc;
- /* Request frequency offset measurement */
- rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
- ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF);
+ /* Request measurement */
+ rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL, type);
if (rc)
return rc;
/* Wait for finish */
- rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
- ZL_REF_FREQ_MEAS_CTRL);
+ return zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
+ ZL_REF_FREQ_MEAS_CTRL);
+}
+
+/**
+ * zl3073x_ref_freq_meas_update - update measured input reference frequencies
+ * @zldev: pointer to zl3073x_dev structure
+ *
+ * The function asks device to latch measured input reference frequencies
+ * and stores the results in the ref state.
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int
+zl3073x_ref_freq_meas_update(struct zl3073x_dev *zldev)
+{
+ int i, rc;
+
+ rc = zl3073x_ref_freq_meas_latch(zldev, ZL_REF_FREQ_MEAS_CTRL_REF_FREQ);
+ if (rc)
+ return rc;
+
+ /* Read measured frequencies in Hz (unsigned 32-bit, LSB = 1 Hz) */
+ for (i = 0; i < ZL3073X_NUM_REFS; i++) {
+ u32 value;
+
+ rc = zl3073x_read_u32(zldev, ZL_REG_REF_FREQ(i), &value);
+ if (rc)
+ return rc;
+
+ zldev->ref[i].meas_freq = value;
+ }
+
+ return 0;
+}
+
+/**
+ * zl3073x_ref_ffo_update - update reference fractional frequency offsets
+ * @zldev: pointer to zl3073x_dev structure
+ *
+ * The function asks device to update fractional frequency offsets latch
+ * registers the latest measured values, reads and stores them into
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int
+zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
+{
+ int i, rc;
+
+ rc = zl3073x_ref_freq_meas_latch(zldev,
+ ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF);
if (rc)
return rc;
@@ -714,6 +762,20 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
dev_warn(zldev->dev, "Failed to update phase offsets: %pe\n",
ERR_PTR(rc));
+ /* Update measured input reference frequencies if any DPLL has
+ * frequency monitoring enabled.
+ */
+ list_for_each_entry(zldpll, &zldev->dplls, list) {
+ if (zldpll->freq_monitor) {
+ rc = zl3073x_ref_freq_meas_update(zldev);
+ if (rc)
+ dev_warn(zldev->dev,
+ "Failed to update measured frequencies: %pe\n",
+ ERR_PTR(rc));
+ break;
+ }
+ }
+
/* Update references' fractional frequency offsets */
rc = zl3073x_ref_ffo_update(zldev);
if (rc)
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
index a29f606318f6d..bc2e4fc51a797 100644
--- a/drivers/dpll/zl3073x/dpll.c
+++ b/drivers/dpll/zl3073x/dpll.c
@@ -39,6 +39,7 @@
* @pin_state: last saved pin state
* @phase_offset: last saved pin phase offset
* @freq_offset: last saved fractional frequency offset
+ * @actual_freq: last saved actual (measured) frequency
*/
struct zl3073x_dpll_pin {
struct list_head list;
@@ -54,6 +55,7 @@ struct zl3073x_dpll_pin {
enum dpll_pin_state pin_state;
s64 phase_offset;
s64 freq_offset;
+ u32 actual_freq;
};
/*
@@ -202,6 +204,20 @@ zl3073x_dpll_input_pin_ffo_get(const struct dpll_pin *dpll_pin, void *pin_priv,
return 0;
}
+static int
+zl3073x_dpll_input_pin_actual_freq_get(const struct dpll_pin *dpll_pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv, u64 *actual_freq,
+ struct netlink_ext_ack *extack)
+{
+ struct zl3073x_dpll_pin *pin = pin_priv;
+
+ *actual_freq = pin->actual_freq;
+
+ return 0;
+}
+
static int
zl3073x_dpll_input_pin_frequency_get(const struct dpll_pin *dpll_pin,
void *pin_priv,
@@ -1116,6 +1132,35 @@ zl3073x_dpll_phase_offset_monitor_set(const struct dpll_device *dpll,
return 0;
}
+static int
+zl3073x_dpll_freq_monitor_get(const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_feature_state *state,
+ struct netlink_ext_ack *extack)
+{
+ struct zl3073x_dpll *zldpll = dpll_priv;
+
+ if (zldpll->freq_monitor)
+ *state = DPLL_FEATURE_STATE_ENABLE;
+ else
+ *state = DPLL_FEATURE_STATE_DISABLE;
+
+ return 0;
+}
+
+static int
+zl3073x_dpll_freq_monitor_set(const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_feature_state state,
+ struct netlink_ext_ack *extack)
+{
+ struct zl3073x_dpll *zldpll = dpll_priv;
+
+ zldpll->freq_monitor = (state == DPLL_FEATURE_STATE_ENABLE);
+
+ return 0;
+}
+
static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
.direction_get = zl3073x_dpll_pin_direction_get,
.esync_get = zl3073x_dpll_input_pin_esync_get,
@@ -1123,6 +1168,7 @@ static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
.ffo_get = zl3073x_dpll_input_pin_ffo_get,
.frequency_get = zl3073x_dpll_input_pin_frequency_get,
.frequency_set = zl3073x_dpll_input_pin_frequency_set,
+ .actual_freq_get = zl3073x_dpll_input_pin_actual_freq_get,
.phase_offset_get = zl3073x_dpll_input_pin_phase_offset_get,
.phase_adjust_get = zl3073x_dpll_input_pin_phase_adjust_get,
.phase_adjust_set = zl3073x_dpll_input_pin_phase_adjust_set,
@@ -1151,6 +1197,8 @@ static const struct dpll_device_ops zl3073x_dpll_device_ops = {
.phase_offset_avg_factor_set = zl3073x_dpll_phase_offset_avg_factor_set,
.phase_offset_monitor_get = zl3073x_dpll_phase_offset_monitor_get,
.phase_offset_monitor_set = zl3073x_dpll_phase_offset_monitor_set,
+ .freq_monitor_get = zl3073x_dpll_freq_monitor_get,
+ .freq_monitor_set = zl3073x_dpll_freq_monitor_set,
.supported_modes_get = zl3073x_dpll_supported_modes_get,
};
@@ -1593,6 +1641,39 @@ zl3073x_dpll_pin_ffo_check(struct zl3073x_dpll_pin *pin)
return false;
}
+/**
+ * zl3073x_dpll_pin_actual_freq_check - check for pin actual frequency change
+ * @pin: pin to check
+ *
+ * Check for the given pin's actual (measured) frequency change.
+ *
+ * Return: true on actual frequency change, false otherwise
+ */
+static bool
+zl3073x_dpll_pin_actual_freq_check(struct zl3073x_dpll_pin *pin)
+{
+ struct zl3073x_dpll *zldpll = pin->dpll;
+ struct zl3073x_dev *zldev = zldpll->dev;
+ const struct zl3073x_ref *ref;
+ u8 ref_id;
+
+ if (!zldpll->freq_monitor)
+ return false;
+
+ ref_id = zl3073x_input_pin_ref_get(pin->id);
+ ref = zl3073x_ref_state_get(zldev, ref_id);
+
+ if (pin->actual_freq != ref->meas_freq) {
+ dev_dbg(zldev->dev, "%s actual freq changed: %u -> %u\n",
+ pin->label, pin->actual_freq, ref->meas_freq);
+ pin->actual_freq = ref->meas_freq;
+
+ return true;
+ }
+
+ return false;
+}
+
/**
* zl3073x_dpll_changes_check - check for changes and send notifications
* @zldpll: pointer to zl3073x_dpll structure
@@ -1677,13 +1758,18 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll)
pin_changed = true;
}
- /* Check for phase offset and ffo change once per second */
+ /* Check for phase offset, ffo, and actual freq change
+ * once per second.
+ */
if (zldpll->check_count % 2 == 0) {
if (zl3073x_dpll_pin_phase_offset_check(pin))
pin_changed = true;
if (zl3073x_dpll_pin_ffo_check(pin))
pin_changed = true;
+
+ if (zl3073x_dpll_pin_actual_freq_check(pin))
+ pin_changed = true;
}
if (pin_changed)
diff --git a/drivers/dpll/zl3073x/dpll.h b/drivers/dpll/zl3073x/dpll.h
index 115ee4f67e7ab..ec27a5c854363 100644
--- a/drivers/dpll/zl3073x/dpll.h
+++ b/drivers/dpll/zl3073x/dpll.h
@@ -15,6 +15,7 @@
* @id: DPLL index
* @check_count: periodic check counter
* @phase_monitor: is phase offset monitor enabled
+ * @freq_monitor: is real frequency monitor enabled
* @ops: DPLL device operations for this instance
* @dpll_dev: pointer to registered DPLL device
* @tracker: tracking object for the acquired reference
@@ -28,6 +29,7 @@ struct zl3073x_dpll {
u8 id;
u8 check_count;
bool phase_monitor;
+ bool freq_monitor;
struct dpll_device_ops ops;
struct dpll_device *dpll_dev;
dpll_tracker tracker;
diff --git a/drivers/dpll/zl3073x/ref.h b/drivers/dpll/zl3073x/ref.h
index 06d8d4d97ea26..be16be20dbc7e 100644
--- a/drivers/dpll/zl3073x/ref.h
+++ b/drivers/dpll/zl3073x/ref.h
@@ -23,6 +23,7 @@ struct zl3073x_dev;
* @sync_ctrl: reference sync control
* @config: reference config
* @ffo: current fractional frequency offset
+ * @meas_freq: measured input frequency in Hz
* @mon_status: reference monitor status
*/
struct zl3073x_ref {
@@ -40,6 +41,7 @@ struct zl3073x_ref {
);
struct_group(stat, /* Status */
s64 ffo;
+ u32 meas_freq;
u8 mon_status;
);
};
@@ -68,6 +70,18 @@ zl3073x_ref_ffo_get(const struct zl3073x_ref *ref)
return ref->ffo;
}
+/**
+ * zl3073x_ref_meas_freq_get - get measured input frequency
+ * @ref: pointer to ref state
+ *
+ * Return: measured input frequency in Hz
+ */
+static inline u32
+zl3073x_ref_meas_freq_get(const struct zl3073x_ref *ref)
+{
+ return ref->meas_freq;
+}
+
/**
* zl3073x_ref_freq_get - get given input reference frequency
* @ref: pointer to ref state
--
2.52.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH net-next 1/3] dpll: add actual frequency monitoring to netlink spec
2026-03-25 19:39 ` [PATCH net-next 1/3] dpll: add actual frequency monitoring to netlink spec Ivan Vecera
@ 2026-03-26 11:06 ` Vadim Fedorenko
0 siblings, 0 replies; 6+ messages in thread
From: Vadim Fedorenko @ 2026-03-26 11:06 UTC (permalink / raw)
To: Ivan Vecera, netdev
Cc: Arkadiusz Kubalewski, Jiri Pirko, Jonathan Corbet, Shuah Khan,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Donald Hunter, Prathosh Satish, Petr Oros,
linux-doc, linux-kernel
On 25/03/2026 19:39, Ivan Vecera wrote:
> Add DPLL_A_FREQUENCY_MONITOR device attribute to allow control over
> the frequency monitor feature. The attribute uses the existing
> dpll_feature_state enum (enable/disable) and is present in both
> device-get reply and device-set request.
>
> Add DPLL_A_PIN_ACTUAL_FREQUENCY pin attribute to expose the measured
> input frequency in Hz. The attribute is present in the pin-get reply.
Overall looks ok, but the wording can be improved, I think. What about
using "MEASURED" or "READ" instead of "ACTUAL"? The spec file has this
note already, looks like there were some concerns already?
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH net-next 2/3] dpll: add actual frequency monitoring callback ops
2026-03-25 19:39 ` [PATCH net-next 2/3] dpll: add actual frequency monitoring callback ops Ivan Vecera
@ 2026-03-26 11:21 ` Vadim Fedorenko
0 siblings, 0 replies; 6+ messages in thread
From: Vadim Fedorenko @ 2026-03-26 11:21 UTC (permalink / raw)
To: Ivan Vecera, netdev
Cc: Arkadiusz Kubalewski, Jiri Pirko, Jonathan Corbet, Shuah Khan,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Donald Hunter, Prathosh Satish, Petr Oros,
linux-doc, linux-kernel
On 25/03/2026 19:39, Ivan Vecera wrote:
> +static int dpll_msg_add_actual_freq(struct sk_buff *msg, struct dpll_pin *pin,
> + struct dpll_pin_ref *ref,
> + struct netlink_ext_ack *extack)
> +{
> + const struct dpll_device_ops *dev_ops = dpll_device_ops(ref->dpll);
> + const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
> + struct dpll_device *dpll = ref->dpll;
> + enum dpll_feature_state state;
> + u64 actual_freq;
> + int ret;
> +
> + if (!ops->actual_freq_get)
> + return 0;
> + if (dev_ops->freq_monitor_get) {
> + ret = dev_ops->freq_monitor_get(dpll, dpll_priv(dpll),
> + &state, extack);
> + if (ret)
> + return ret;
> + if (state == DPLL_FEATURE_STATE_DISABLE)
> + return 0;
I think we have to signal back to user that frequency monitoring is
disabled via extack.
> + }
> + ret = ops->actual_freq_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
> + dpll, dpll_priv(dpll), &actual_freq, extack);
> + if (ret)
> + return ret;
> + if (nla_put_64bit(msg, DPLL_A_PIN_ACTUAL_FREQUENCY,
> + sizeof(actual_freq), &actual_freq, DPLL_A_PIN_PAD))
> + return -EMSGSIZE;
> +
> + return 0;
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-03-26 11:21 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-25 19:39 [PATCH net-next 0/3] dpll: add actual frequency monitoring feature Ivan Vecera
2026-03-25 19:39 ` [PATCH net-next 1/3] dpll: add actual frequency monitoring to netlink spec Ivan Vecera
2026-03-26 11:06 ` Vadim Fedorenko
2026-03-25 19:39 ` [PATCH net-next 2/3] dpll: add actual frequency monitoring callback ops Ivan Vecera
2026-03-26 11:21 ` Vadim Fedorenko
2026-03-25 19:39 ` [PATCH net-next 3/3] dpll: zl3073x: implement actual frequency monitoring Ivan Vecera
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox