Commit c5cb31c9 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'dpll-add-phase-offset-averaging-factor'

Ivan Vecera says:

====================
dpll: add phase offset averaging factor

For some hardware, the phase shift may result from averaging previous values
and the newly measured value. In this case, the averaging is controlled by
a configurable averaging factor.

Add new device level attribute phase-offset-avg-factor, appropriate
callbacks and implement them in zl3073x driver.
====================

Link: https://patch.msgid.link/20250927084912.2343597-1-ivecera@redhat.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 377ea331 9363b483
Loading
Loading
Loading
Loading
+17 −1
Original line number Diff line number Diff line
@@ -179,7 +179,23 @@ Phase offset measurement and adjustment
Device may provide ability to measure a phase difference between signals
on a pin and its parent dpll device. If pin-dpll phase offset measurement
is supported, it shall be provided with ``DPLL_A_PIN_PHASE_OFFSET``
attribute for each parent dpll device.
attribute for each parent dpll device. The reported phase offset may be
computed as the average of prior values and the current measurement, using
the following formula:

.. math::
   curr\_avg = prev\_avg * \frac{2^N-1}{2^N} + new\_val * \frac{1}{2^N}

where `curr_avg` is the current reported phase offset, `prev_avg` is the
previously reported value, `new_val` is the current measurement, and `N` is
the averaging factor. Configured averaging factor value is provided with
``DPLL_A_PHASE_OFFSET_AVG_FACTOR`` attribute of a device and value change can
be requested with the same attribute with ``DPLL_CMD_DEVICE_SET`` command.

  ================================== ======================================
  ``DPLL_A_PHASE_OFFSET_AVG_FACTOR`` attr configured value of phase offset
                                     averaging factor
  ================================== ======================================

Device may also provide ability to adjust a signal phase on a pin.
If pin phase adjustment is supported, minimal and maximal values that pin
+6 −0
Original line number Diff line number Diff line
@@ -315,6 +315,10 @@ attribute-sets:
          If enabled, dpll device shall monitor and notify all currently
          available inputs for changes of their phase offset against the
          dpll device.
      -
        name: phase-offset-avg-factor
        type: u32
        doc: Averaging factor applied to calculation of reported phase offset.
  -
    name: pin
    enum-name: dpll_a_pin
@@ -523,6 +527,7 @@ operations:
            - clock-id
            - type
            - phase-offset-monitor
            - phase-offset-avg-factor

      dump:
        reply: *dev-attrs
@@ -540,6 +545,7 @@ operations:
          attributes:
            - id
            - phase-offset-monitor
            - phase-offset-avg-factor
    -
      name: device-create-ntf
      doc: Notification about device appearing
+59 −7
Original line number Diff line number Diff line
@@ -164,6 +164,27 @@ dpll_msg_add_phase_offset_monitor(struct sk_buff *msg, struct dpll_device *dpll,
	return 0;
}

static int
dpll_msg_add_phase_offset_avg_factor(struct sk_buff *msg,
				     struct dpll_device *dpll,
				     struct netlink_ext_ack *extack)
{
	const struct dpll_device_ops *ops = dpll_device_ops(dpll);
	u32 factor;
	int ret;

	if (ops->phase_offset_avg_factor_get) {
		ret = ops->phase_offset_avg_factor_get(dpll, dpll_priv(dpll),
						       &factor, extack);
		if (ret)
			return ret;
		if (nla_put_u32(msg, DPLL_A_PHASE_OFFSET_AVG_FACTOR, factor))
			return -EMSGSIZE;
	}

	return 0;
}

static int
dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device *dpll,
			 struct netlink_ext_ack *extack)
@@ -675,6 +696,9 @@ dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
	if (nla_put_u32(msg, DPLL_A_TYPE, dpll->type))
		return -EMSGSIZE;
	ret = dpll_msg_add_phase_offset_monitor(msg, dpll, extack);
	if (ret)
		return ret;
	ret = dpll_msg_add_phase_offset_avg_factor(msg, dpll, extack);
	if (ret)
		return ret;

@@ -839,6 +863,23 @@ dpll_phase_offset_monitor_set(struct dpll_device *dpll, struct nlattr *a,
					     extack);
}

static int
dpll_phase_offset_avg_factor_set(struct dpll_device *dpll, struct nlattr *a,
				 struct netlink_ext_ack *extack)
{
	const struct dpll_device_ops *ops = dpll_device_ops(dpll);
	u32 factor = nla_get_u32(a);

	if (!ops->phase_offset_avg_factor_set) {
		NL_SET_ERR_MSG_ATTR(extack, a,
				    "device not capable of changing phase offset average factor");
		return -EOPNOTSUPP;
	}

	return ops->phase_offset_avg_factor_set(dpll, dpll_priv(dpll), factor,
						extack);
}

static int
dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
		  struct netlink_ext_ack *extack)
@@ -1736,14 +1777,25 @@ int dpll_nl_device_get_doit(struct sk_buff *skb, struct genl_info *info)
static int
dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
{
	int ret;

	if (info->attrs[DPLL_A_PHASE_OFFSET_MONITOR]) {
		struct nlattr *a = info->attrs[DPLL_A_PHASE_OFFSET_MONITOR];
	struct nlattr *a;
	int rem, ret;

		ret = dpll_phase_offset_monitor_set(dpll, a, info->extack);
	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
			  genlmsg_len(info->genlhdr), rem) {
		switch (nla_type(a)) {
		case DPLL_A_PHASE_OFFSET_MONITOR:
			ret = dpll_phase_offset_monitor_set(dpll, a,
							    info->extack);
			if (ret)
				return ret;
			break;
		case DPLL_A_PHASE_OFFSET_AVG_FACTOR:
			ret = dpll_phase_offset_avg_factor_set(dpll, a,
							       info->extack);
			if (ret)
				return ret;
			break;
		}
	}

	return 0;
+3 −2
Original line number Diff line number Diff line
@@ -42,9 +42,10 @@ 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_MONITOR + 1] = {
static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_PHASE_OFFSET_AVG_FACTOR + 1] = {
	[DPLL_A_ID] = { .type = NLA_U32, },
	[DPLL_A_PHASE_OFFSET_MONITOR] = NLA_POLICY_MAX(NLA_U32, 1),
	[DPLL_A_PHASE_OFFSET_AVG_FACTOR] = { .type = NLA_U32, },
};

/* DPLL_CMD_PIN_ID_GET - do */
@@ -112,7 +113,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_MONITOR,
		.maxattr	= DPLL_A_PHASE_OFFSET_AVG_FACTOR,
		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
	},
	{
+34 −4
Original line number Diff line number Diff line
@@ -956,6 +956,32 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
				   msecs_to_jiffies(500));
}

int zl3073x_dev_phase_avg_factor_set(struct zl3073x_dev *zldev, u8 factor)
{
	u8 dpll_meas_ctrl, value;
	int rc;

	/* Read DPLL phase measurement control register */
	rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, &dpll_meas_ctrl);
	if (rc)
		return rc;

	/* Convert requested factor to register value */
	value = (factor + 1) & 0x0f;

	/* Update phase measurement control register */
	dpll_meas_ctrl &= ~ZL_DPLL_MEAS_CTRL_AVG_FACTOR;
	dpll_meas_ctrl |= FIELD_PREP(ZL_DPLL_MEAS_CTRL_AVG_FACTOR, value);
	rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, dpll_meas_ctrl);
	if (rc)
		return rc;

	/* Save the new factor */
	zldev->phase_avg_factor = factor;

	return 0;
}

/**
 * zl3073x_dev_phase_meas_setup - setup phase offset measurement
 * @zldev: pointer to zl3073x_dev structure
@@ -972,15 +998,16 @@ zl3073x_dev_phase_meas_setup(struct zl3073x_dev *zldev)
	u8 dpll_meas_ctrl, mask = 0;
	int rc;

	/* Setup phase measurement averaging factor */
	rc = zl3073x_dev_phase_avg_factor_set(zldev, zldev->phase_avg_factor);
	if (rc)
		return rc;

	/* Read DPLL phase measurement control register */
	rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, &dpll_meas_ctrl);
	if (rc)
		return rc;

	/* Setup phase measurement averaging factor */
	dpll_meas_ctrl &= ~ZL_DPLL_MEAS_CTRL_AVG_FACTOR;
	dpll_meas_ctrl |= FIELD_PREP(ZL_DPLL_MEAS_CTRL_AVG_FACTOR, 3);

	/* Enable DPLL measurement block */
	dpll_meas_ctrl |= ZL_DPLL_MEAS_CTRL_EN;

@@ -1208,6 +1235,9 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
	 */
	zldev->clock_id = get_random_u64();

	/* Default phase offset averaging factor */
	zldev->phase_avg_factor = 2;

	/* Initialize mutex for operations where multiple reads, writes
	 * and/or polls are required to be done atomically.
	 */
Loading