]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
dpll: add fractional frequency offset to pin-parent-device
authorIvan Vecera <ivecera@redhat.com>
Mon, 11 May 2026 15:58:15 +0000 (17:58 +0200)
committerJakub Kicinski <kuba@kernel.org>
Wed, 13 May 2026 01:48:07 +0000 (18:48 -0700)
Add both fractional-frequency-offset (PPM) and
fractional-frequency-offset-ppt (PPT) attributes to the
pin-parent-device nested attribute set, alongside the existing
top-level pin attributes. Both carry the same measurement at
different precisions.

Introduce enum dpll_ffo_type and struct dpll_ffo_param to
distinguish FFO contexts: DPLL_FFO_PORT_RXTX_RATE for the RX vs
TX symbol rate offset reported at the top level, and
DPLL_FFO_PIN_DEVICE for the pin vs parent DPLL offset reported
in the pin-parent-device nest.

Add a supported_ffo bitmask to struct dpll_pin_ops so drivers
declare which FFO types they support. The core only calls ffo_get
for types the driver has opted into, eliminating the need for
per-driver NULL pointer guards. Validate at pin registration time
that supported_ffo is not set without an ffo_get callback.

Update mlx5 (DPLL_FFO_PORT_RXTX_RATE) and zl3073x
(DPLL_FFO_PORT_RXTX_RATE) drivers to use the new API.

Add documentation for both FFO types to dpll.rst.

Changes v3 -> v4:
- Replace dpll=NULL overloading with enum dpll_ffo_type and
  struct dpll_ffo_param (Jakub Kicinski)
- Add supported_ffo opt-in bitmask in dpll_pin_ops for fail-close
  driver validation (Jakub Kicinski)
- Add WARN_ON in dpll_pin_register for supported_ffo without
  ffo_get callback

Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
Link: https://patch.msgid.link/20260511155816.99936-2-ivecera@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Documentation/driver-api/dpll.rst
Documentation/netlink/specs/dpll.yaml
drivers/dpll/dpll_core.c
drivers/dpll/dpll_netlink.c
drivers/dpll/dpll_nl.c
drivers/dpll/zl3073x/dpll.c
drivers/net/ethernet/mellanox/mlx5/core/dpll.c
include/linux/dpll.h

index 37eaef785e30494815316de7a518ac2295391b12..bae14766d4f7bcebcaf5ad28c4ad263bfceb165e 100644 (file)
@@ -258,6 +258,26 @@ in the ``DPLL_A_PIN_PHASE_OFFSET`` attribute.
   ``DPLL_A_PHASE_OFFSET_MONITOR`` attr state of a feature
   =============================== ========================
 
+Fractional frequency offset
+===========================
+
+The fractional frequency offset (FFO) is reported through two attributes
+that carry the same measurement at different precisions:
+
+- ``DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET`` in PPM (parts per million)
+- ``DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT`` in PPT (parts per trillion)
+
+Both attributes appear at the top level of a pin and inside each
+``pin-parent-device`` nest. Two FFO types are defined:
+
+- ``DPLL_FFO_PORT_RXTX_RATE`` - RX vs TX symbol rate offset (top-level)
+- ``DPLL_FFO_PIN_DEVICE`` - pin vs parent DPLL offset (per-parent)
+
+The driver declares which types it supports via the ``supported_ffo``
+bitmask in ``struct dpll_pin_ops``. The core only calls the ``ffo_get``
+callback for types the driver has opted into. The requested type is
+passed to the driver in the ``struct dpll_ffo_param``.
+
 Frequency monitor
 =================
 
index c45de70a47ce62ba791767670f5eaf091e877acb..91a172617b3a97a876afb163111b662d257baddc 100644 (file)
@@ -448,12 +448,14 @@ attribute-sets:
         name: fractional-frequency-offset
         type: sint
         doc: |
-          The FFO (Fractional Frequency Offset) between the RX and TX
-          symbol rate on the media associated with the pin:
-          (rx_frequency-tx_frequency)/rx_frequency
+          The FFO (Fractional Frequency Offset) of the pin.
+          At top level this represents the RX vs TX symbol rate
+          offset on the media associated with the pin. Inside
+          the pin-parent-device nest it represents the frequency
+          offset between the pin and its parent DPLL device.
           Value is in PPM (parts per million).
-          This may be implemented for example for pin of type
-          PIN_TYPE_SYNCE_ETH_PORT.
+          This is a lower-precision version of
+          fractional-frequency-offset-ppt.
       -
         name: esync-frequency
         type: u64
@@ -492,12 +494,14 @@ attribute-sets:
         name: fractional-frequency-offset-ppt
         type: sint
         doc: |
-          The FFO (Fractional Frequency Offset) of the pin with respect to
-          the nominal frequency.
-          Value = (frequency_measured - frequency_nominal) / frequency_nominal
+          The FFO (Fractional Frequency Offset) of the pin.
+          At top level this represents the RX vs TX symbol rate
+          offset on the media associated with the pin. Inside
+          the pin-parent-device nest it represents the frequency
+          offset between the pin and its parent DPLL device.
           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).
+          This is a higher-precision version of
+          fractional-frequency-offset.
       -
         name: measured-frequency
         type: u64
@@ -534,6 +538,10 @@ attribute-sets:
         name: operstate
       -
         name: phase-offset
+      -
+        name: fractional-frequency-offset
+      -
+        name: fractional-frequency-offset-ppt
   -
     name: pin-parent-pin
     subset-of: pin
index cbb635db43210ff01d84562bdb1edf1e71944d49..20a54728549cc78de2fdd830da5086a899951a42 100644 (file)
@@ -879,7 +879,8 @@ dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
            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)))
+                    !dpll_device_ops(dpll)->freq_monitor_set)) ||
+           WARN_ON(ops->supported_ffo && !ops->ffo_get))
                return -EINVAL;
 
        mutex_lock(&dpll_lock);
index ea63602637866ecd5c2a68a5831d8a6668a3dfeb..53ca24d0f191bc3268340543769fd96c836f9318 100644 (file)
@@ -417,31 +417,28 @@ dpll_msg_add_phase_offset(struct sk_buff *msg, struct dpll_pin *pin,
 
 static int dpll_msg_add_ffo(struct sk_buff *msg, struct dpll_pin *pin,
                            struct dpll_pin_ref *ref,
+                           enum dpll_ffo_type type,
                            struct netlink_ext_ack *extack)
 {
        const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
-       struct dpll_device *dpll = ref->dpll;
-       s64 ffo;
+       struct dpll_ffo_param ffo = { .type = type };
        int ret;
 
-       if (!ops->ffo_get)
+       if (!ops->ffo_get || !(ops->supported_ffo & BIT(type)))
                return 0;
-       ret = ops->ffo_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
-                          dpll, dpll_priv(dpll), &ffo, extack);
+       ret = ops->ffo_get(pin, dpll_pin_on_dpll_priv(ref->dpll, pin),
+                          ref->dpll, dpll_priv(ref->dpll), &ffo, extack);
        if (ret) {
                if (ret == -ENODATA)
                        return 0;
                return ret;
        }
-       /* Put the FFO value in PPM to preserve compatibility with older
-        * programs.
-        */
-       ret = nla_put_sint(msg, DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET,
-                          div_s64(ffo, 1000000));
-       if (ret)
+       if (nla_put_sint(msg, DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET,
+                        div_s64(ffo.ffo, 1000000)))
                return -EMSGSIZE;
-       return nla_put_sint(msg, DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT,
-                           ffo);
+       return nla_put_sint(msg,
+                           DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT,
+                           ffo.ffo);
 }
 
 static int dpll_msg_add_measured_freq(struct sk_buff *msg, struct dpll_pin *pin,
@@ -686,6 +683,10 @@ dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin,
                if (ret)
                        goto nest_cancel;
                ret = dpll_msg_add_phase_offset(msg, pin, ref, extack);
+               if (ret)
+                       goto nest_cancel;
+               ret = dpll_msg_add_ffo(msg, pin, ref,
+                                      DPLL_FFO_PIN_DEVICE, extack);
                if (ret)
                        goto nest_cancel;
                nla_nest_end(msg, attr);
@@ -748,7 +749,8 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
        ret = dpll_msg_add_pin_phase_adjust(msg, pin, ref, extack);
        if (ret)
                return ret;
-       ret = dpll_msg_add_ffo(msg, pin, ref, extack);
+       ret = dpll_msg_add_ffo(msg, pin, ref,
+                              DPLL_FFO_PORT_RXTX_RATE, extack);
        if (ret)
                return ret;
        ret = dpll_msg_add_measured_freq(msg, pin, ref, extack);
index 58235845fa3d5f9f341ee4cbc11d5eacaa5131e2..b1d9182c7802fe0532eb52f02d9942e0f44c2a17 100644 (file)
@@ -19,6 +19,8 @@ const struct nla_policy dpll_pin_parent_device_nl_policy[DPLL_A_PIN_OPERSTATE +
        [DPLL_A_PIN_STATE] = NLA_POLICY_RANGE(NLA_U32, 1, 3),
        [DPLL_A_PIN_OPERSTATE] = NLA_POLICY_RANGE(NLA_U32, 1, 4),
        [DPLL_A_PIN_PHASE_OFFSET] = { .type = NLA_S64, },
+       [DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET] = { .type = NLA_SINT, },
+       [DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT] = { .type = NLA_SINT, },
 };
 
 const struct nla_policy dpll_pin_parent_pin_nl_policy[DPLL_A_PIN_STATE + 1] = {
index 6fd718696de0d9333dcf1a8485d5c233fdd0f92a..05e63661bf074b3b7805596a0a6e31907d5be1df 100644 (file)
@@ -295,11 +295,12 @@ zl3073x_dpll_input_pin_ref_sync_set(const struct dpll_pin *dpll_pin,
 static int
 zl3073x_dpll_input_pin_ffo_get(const struct dpll_pin *dpll_pin, void *pin_priv,
                               const struct dpll_device *dpll, void *dpll_priv,
-                              s64 *ffo, struct netlink_ext_ack *extack)
+                              struct dpll_ffo_param *ffo,
+                              struct netlink_ext_ack *extack)
 {
        struct zl3073x_dpll_pin *pin = pin_priv;
 
-       *ffo = pin->freq_offset;
+       ffo->ffo = pin->freq_offset;
 
        return 0;
 }
@@ -1274,6 +1275,7 @@ zl3073x_dpll_freq_monitor_set(const struct dpll_device *dpll,
 }
 
 static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
+       .supported_ffo = BIT(DPLL_FFO_PORT_RXTX_RATE),
        .direction_get = zl3073x_dpll_pin_direction_get,
        .esync_get = zl3073x_dpll_input_pin_esync_get,
        .esync_set = zl3073x_dpll_input_pin_esync_set,
index bce72e8d1bc31c6e7531792704b0e8e95f5a1b5a..7c69d9029bfa4f78813bfac5f5d17fb36838b8ce 100644 (file)
@@ -300,7 +300,8 @@ static int mlx5_dpll_state_on_dpll_set(const struct dpll_pin *pin,
 
 static int mlx5_dpll_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)
+                            struct dpll_ffo_param *ffo,
+                            struct netlink_ext_ack *extack)
 {
        struct mlx5_dpll_synce_status synce_status;
        struct mlx5_dpll *mdpll = pin_priv;
@@ -309,10 +310,11 @@ static int mlx5_dpll_ffo_get(const struct dpll_pin *pin, void *pin_priv,
        err = mlx5_dpll_synce_status_get(mdpll->mdev, &synce_status);
        if (err)
                return err;
-       return mlx5_dpll_pin_ffo_get(&synce_status, ffo);
+       return mlx5_dpll_pin_ffo_get(&synce_status, &ffo->ffo);
 }
 
 static const struct dpll_pin_ops mlx5_dpll_pins_ops = {
+       .supported_ffo = BIT(DPLL_FFO_PORT_RXTX_RATE),
        .direction_get = mlx5_dpll_pin_direction_get,
        .state_on_dpll_get = mlx5_dpll_state_on_dpll_get,
        .state_on_dpll_set = mlx5_dpll_state_on_dpll_set,
index 193abfb6ce66bd6b0aa105e554ac1a897da2a56d..82dfadd9f593b780c11d1d05cd00b62a32d4f534 100644 (file)
@@ -60,7 +60,20 @@ struct dpll_device_ops {
                                struct netlink_ext_ack *extack);
 };
 
+enum dpll_ffo_type {
+       DPLL_FFO_PORT_RXTX_RATE,
+       DPLL_FFO_PIN_DEVICE,
+
+       __DPLL_FFO_TYPE_MAX,
+};
+
+struct dpll_ffo_param {
+       enum dpll_ffo_type type;
+       s64 ffo;
+};
+
 struct dpll_pin_ops {
+       unsigned long supported_ffo;
        int (*frequency_set)(const struct dpll_pin *pin, void *pin_priv,
                             const struct dpll_device *dpll, void *dpll_priv,
                             const u64 frequency,
@@ -121,7 +134,8 @@ struct dpll_pin_ops {
                                struct netlink_ext_ack *extack);
        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);
+                      struct dpll_ffo_param *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,