dpll: add fractional frequency offset to pin-parent-device

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>
This commit is contained in:
Ivan Vecera 2026-05-11 17:58:15 +02:00 committed by Jakub Kicinski
parent 28702a215c
commit 9c11fcb2e9
8 changed files with 81 additions and 30 deletions

View 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
=================

View 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

View 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);

View 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);

View 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] = {

View 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,

View 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,

View 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,