]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
dpll: add frequency monitoring callback ops
authorIvan Vecera <ivecera@redhat.com>
Thu, 2 Apr 2026 18:40:56 +0000 (20:40 +0200)
committerJakub Kicinski <kuba@kernel.org>
Fri, 3 Apr 2026 23:48:01 +0000 (16:48 -0700)
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:
- measured_freq_get(..) - to obtain the measured frequency in mHz.

Obtain the feature state value using the get callback and provide it to
the user if the device driver implements callbacks. The measured_freq_get
pin callback is only invoked when the frequency monitor is enabled.
The freq_monitor_get device callback is required when measured_freq_get
is provided by the driver.

Execute the set callback upon user requests.

Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
Link: https://patch.msgid.link/20260402184057.1890514-3-ivecera@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/dpll/dpll_core.c
drivers/dpll/dpll_netlink.c
include/linux/dpll.h

index 3f54754cdec4b1d48b94991d3dd2394dd69daba6..cbb635db43210ff01d84562bdb1edf1e71944d49 100644 (file)
@@ -876,7 +876,10 @@ dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
 
        if (WARN_ON(!ops) ||
            WARN_ON(!ops->state_on_dpll_get) ||
-           WARN_ON(!ops->direction_get))
+           WARN_ON(!ops->direction_get) ||
+           WARN_ON(ops->measured_freq_get &&
+                   (!dpll_device_ops(dpll)->freq_monitor_get ||
+                    !dpll_device_ops(dpll)->freq_monitor_set)))
                return -EINVAL;
 
        mutex_lock(&dpll_lock);
index 83cbd64abf5a47357412a630bb2aae727d3539d1..af7ce62ec55ca8bb916896454530059456c7226a 100644 (file)
@@ -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_measured_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 measured_freq;
+       int ret;
+
+       if (!ops->measured_freq_get)
+               return 0;
+       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->measured_freq_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
+                                   dpll, dpll_priv(dpll), &measured_freq,
+                                   extack);
+       if (ret)
+               return ret;
+       if (nla_put_64bit(msg, DPLL_A_PIN_MEASURED_FREQUENCY,
+                         sizeof(measured_freq), &measured_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_measured_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;
                }
        }
 
index 2ce295b46b8cdca4755485f904e06e99a1a8f885..b7277a8b484d264019ed67cf8ef8bc1aa9f638a7 100644 (file)
@@ -52,6 +52,12 @@ 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 +116,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 (*measured_freq_get)(const struct dpll_pin *pin, void *pin_priv,
+                                const struct dpll_device *dpll,
+                                void *dpll_priv, u64 *measured_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);