Commit 99620ea0 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'dpll-phase-offset-phase-adjust'



Arkadiusz Kubalewski says:

====================
dpll: add phase-offset and phase-adjust

Improve monitoring and control over dpll devices.
Allow user to receive measurement of phase difference between signals
on pin and dpll (phase-offset).
Allow user to receive and control adjustable value of pin's signal
phase (phase-adjust).

v4->v5:
- rebase series on top of net-next/main, fix conflict - remove redundant
  attribute type definition in subset definition

v3->v4:
- do not increase do version of uAPI header as it is not needed (v3 did
  not have this change)
- fix spelling around commit messages, argument descriptions and docs
- add missing extack errors on failure set callbacks for pin phase
  adjust and frequency
- remove ice check if value is already set, now redundant as checked in
  the dpll subsystem

v2->v3:
- do not increase do version of uAPI header as it is not needed

v1->v2:
- improve handling for error case of requesting the phase adjust set
- align handling for error case of frequency set request with the
approach introduced for phase adjust
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents cc30c634 20f66772
Loading
Loading
Loading
Loading
+52 −1
Original line number Diff line number Diff line
@@ -173,6 +173,47 @@ in order to configure active input of a MUX-type pin, the user needs to
request desired pin state of the child pin on the parent pin,
as described in the ``MUX-type pins`` chapter.

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.

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
handle shall be provide to the user on ``DPLL_CMD_PIN_GET`` respond
with ``DPLL_A_PIN_PHASE_ADJUST_MIN`` and ``DPLL_A_PIN_PHASE_ADJUST_MAX``
attributes. Configured phase adjust value is provided with
``DPLL_A_PIN_PHASE_ADJUST`` attribute of a pin, and value change can be
requested with the same attribute with ``DPLL_CMD_PIN_SET`` command.

  =============================== ======================================
  ``DPLL_A_PIN_ID``               configured pin id
  ``DPLL_A_PIN_PHASE_ADJUST_MIN`` attr minimum value of phase adjustment
  ``DPLL_A_PIN_PHASE_ADJUST_MAX`` attr maximum value of phase adjustment
  ``DPLL_A_PIN_PHASE_ADJUST``     attr configured value of phase
                                  adjustment on parent dpll device
  ``DPLL_A_PIN_PARENT_DEVICE``    nested attribute for requesting
                                  configuration on given parent dpll
                                  device
    ``DPLL_A_PIN_PARENT_ID``      parent dpll device id
    ``DPLL_A_PIN_PHASE_OFFSET``   attr measured phase difference
                                  between a pin and parent dpll device
  =============================== ======================================

All phase related values are provided in pico seconds, which represents
time difference between signals phase. The negative value means that
phase of signal on pin is earlier in time than dpll's signal. Positive
value means that phase of signal on pin is later in time than signal of
a dpll.

Phase adjust (also min and max) values are integers, but measured phase
offset values are fractional with 3-digit decimal places and shell be
divided with ``DPLL_PIN_PHASE_OFFSET_DIVIDER`` to get integer part and
modulo divided to get fractional part.

Configuration commands group
============================

@@ -263,6 +304,12 @@ according to attribute purpose.
                                       frequencies
      ``DPLL_A_PIN_ANY_FREQUENCY_MIN`` attr minimum value of frequency
      ``DPLL_A_PIN_ANY_FREQUENCY_MAX`` attr maximum value of frequency
    ``DPLL_A_PIN_PHASE_ADJUST_MIN``    attr minimum value of phase
                                       adjustment
    ``DPLL_A_PIN_PHASE_ADJUST_MAX``    attr maximum value of phase
                                       adjustment
    ``DPLL_A_PIN_PHASE_ADJUST``        attr configured value of phase
                                       adjustment on parent device
    ``DPLL_A_PIN_PARENT_DEVICE``       nested attr for each parent device
                                       the pin is connected with
      ``DPLL_A_PIN_PARENT_ID``         attr parent dpll device id
@@ -272,6 +319,8 @@ according to attribute purpose.
                                       dpll device
      ``DPLL_A_PIN_DIRECTION``         attr direction of a pin on the
                                       parent dpll device
      ``DPLL_A_PIN_PHASE_OFFSET``      attr measured phase difference
                                       between a pin and parent dpll
    ``DPLL_A_PIN_PARENT_PIN``          nested attr for each parent pin
                                       the pin is connected with
      ``DPLL_A_PIN_PARENT_ID``         attr parent pin id
@@ -284,6 +333,8 @@ according to attribute purpose.
  ``DPLL_CMD_PIN_SET``                 command to set pins configuration
    ``DPLL_A_PIN_ID``                  attr unique a pin ID
    ``DPLL_A_PIN_FREQUENCY``           attr requested frequency of a pin
    ``DPLL_A_PIN_PHASE_ADJUST``        attr requested value of phase
                                       adjustment on parent device
    ``DPLL_A_PIN_PARENT_DEVICE``       nested attr for each parent dpll
                                       device configuration request
      ``DPLL_A_PIN_PARENT_ID``         attr parent dpll device id
+30 −0
Original line number Diff line number Diff line
@@ -164,6 +164,18 @@ definitions:
      -
        name: state-can-change
        doc: pin state can be changed
  -
    type: const
    name: phase-offset-divider
    value: 1000
    doc: |
      phase offset divider allows userspace to calculate a value of
      measured signal phase difference between a pin and dpll device
      as a fractional value with three digit decimal precision.
      Value of (DPLL_A_PHASE_OFFSET / DPLL_PHASE_OFFSET_DIVIDER) is an
      integer part of a measured phase offset value.
      Value of (DPLL_A_PHASE_OFFSET % DPLL_PHASE_OFFSET_DIVIDER) is a
      fractional part of a measured phase offset value.

attribute-sets:
  -
@@ -272,6 +284,18 @@ attribute-sets:
        type: nest
        multi-attr: true
        nested-attributes: pin-parent-pin
      -
        name: phase-adjust-min
        type: s32
      -
        name: phase-adjust-max
        type: s32
      -
        name: phase-adjust
        type: s32
      -
        name: phase-offset
        type: s64
  -
    name: pin-parent-device
    subset-of: pin
@@ -284,6 +308,8 @@ attribute-sets:
        name: prio
      -
        name: state
      -
        name: phase-offset
  -
    name: pin-parent-pin
    subset-of: pin
@@ -431,6 +457,9 @@ operations:
            - capabilities
            - parent-device
            - parent-pin
            - phase-adjust-min
            - phase-adjust-max
            - phase-adjust

      dump:
        pre: dpll-lock-dumpit
@@ -458,6 +487,7 @@ operations:
            - state
            - parent-device
            - parent-pin
            - phase-adjust
    -
      name: pin-create-ntf
      doc: Notification about pin appearing
+179 −9
Original line number Diff line number Diff line
@@ -212,6 +212,53 @@ dpll_msg_add_pin_direction(struct sk_buff *msg, struct dpll_pin *pin,
	return 0;
}

static int
dpll_msg_add_pin_phase_adjust(struct sk_buff *msg, struct dpll_pin *pin,
			      struct dpll_pin_ref *ref,
			      struct netlink_ext_ack *extack)
{
	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
	struct dpll_device *dpll = ref->dpll;
	s32 phase_adjust;
	int ret;

	if (!ops->phase_adjust_get)
		return 0;
	ret = ops->phase_adjust_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
				    dpll, dpll_priv(dpll),
				    &phase_adjust, extack);
	if (ret)
		return ret;
	if (nla_put_s32(msg, DPLL_A_PIN_PHASE_ADJUST, phase_adjust))
		return -EMSGSIZE;

	return 0;
}

static int
dpll_msg_add_phase_offset(struct sk_buff *msg, struct dpll_pin *pin,
			  struct dpll_pin_ref *ref,
			  struct netlink_ext_ack *extack)
{
	const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
	struct dpll_device *dpll = ref->dpll;
	s64 phase_offset;
	int ret;

	if (!ops->phase_offset_get)
		return 0;
	ret = ops->phase_offset_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
				    dpll, dpll_priv(dpll), &phase_offset,
				    extack);
	if (ret)
		return ret;
	if (nla_put_64bit(msg, DPLL_A_PIN_PHASE_OFFSET, sizeof(phase_offset),
			  &phase_offset, 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)
@@ -330,6 +377,9 @@ dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin,
		if (ret)
			goto nest_cancel;
		ret = dpll_msg_add_pin_direction(msg, pin, ref, extack);
		if (ret)
			goto nest_cancel;
		ret = dpll_msg_add_phase_offset(msg, pin, ref, extack);
		if (ret)
			goto nest_cancel;
		nla_nest_end(msg, attr);
@@ -377,6 +427,15 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
	if (nla_put_u32(msg, DPLL_A_PIN_CAPABILITIES, prop->capabilities))
		return -EMSGSIZE;
	ret = dpll_msg_add_pin_freq(msg, pin, ref, extack);
	if (ret)
		return ret;
	if (nla_put_s32(msg, DPLL_A_PIN_PHASE_ADJUST_MIN,
			prop->phase_range.min))
		return -EMSGSIZE;
	if (nla_put_s32(msg, DPLL_A_PIN_PHASE_ADJUST_MAX,
			prop->phase_range.max))
		return -EMSGSIZE;
	ret = dpll_msg_add_pin_phase_adjust(msg, pin, ref, extack);
	if (ret)
		return ret;
	if (xa_empty(&pin->parent_refs))
@@ -416,7 +475,7 @@ dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
	if (nla_put_u32(msg, DPLL_A_TYPE, dpll->type))
		return -EMSGSIZE;

	return ret;
	return 0;
}

static int
@@ -556,8 +615,10 @@ static int
dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
		  struct netlink_ext_ack *extack)
{
	u64 freq = nla_get_u64(a);
	struct dpll_pin_ref *ref;
	u64 freq = nla_get_u64(a), old_freq;
	struct dpll_pin_ref *ref, *failed;
	const struct dpll_pin_ops *ops;
	struct dpll_device *dpll;
	unsigned long i;
	int ret;

@@ -567,19 +628,51 @@ dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
	}

	xa_for_each(&pin->dpll_refs, i, ref) {
		const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
		struct dpll_device *dpll = ref->dpll;

		if (!ops->frequency_set)
		ops = dpll_pin_ops(ref);
		if (!ops->frequency_set || !ops->frequency_get) {
			NL_SET_ERR_MSG(extack, "frequency set not supported by the device");
			return -EOPNOTSUPP;
		}
	}
	ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
	ops = dpll_pin_ops(ref);
	dpll = ref->dpll;
	ret = ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
				 dpll_priv(dpll), &old_freq, extack);
	if (ret) {
		NL_SET_ERR_MSG(extack, "unable to get old frequency value");
		return ret;
	}
	if (freq == old_freq)
		return 0;

	xa_for_each(&pin->dpll_refs, i, ref) {
		ops = dpll_pin_ops(ref);
		dpll = ref->dpll;
		ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
					 dpll, dpll_priv(dpll), freq, extack);
		if (ret)
			return ret;
		if (ret) {
			failed = ref;
			NL_SET_ERR_MSG_FMT(extack, "frequency set failed for dpll_id:%u",
					   dpll->id);
			goto rollback;
		}
	}
	__dpll_pin_change_ntf(pin);

	return 0;

rollback:
	xa_for_each(&pin->dpll_refs, i, ref) {
		if (ref == failed)
			break;
		ops = dpll_pin_ops(ref);
		dpll = ref->dpll;
		if (ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
				       dpll, dpll_priv(dpll), old_freq, extack))
			NL_SET_ERR_MSG(extack, "set frequency rollback failed");
	}
	return ret;
}

static int
@@ -705,6 +798,78 @@ dpll_pin_direction_set(struct dpll_pin *pin, struct dpll_device *dpll,
	return 0;
}

static int
dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr,
		       struct netlink_ext_ack *extack)
{
	struct dpll_pin_ref *ref, *failed;
	const struct dpll_pin_ops *ops;
	s32 phase_adj, old_phase_adj;
	struct dpll_device *dpll;
	unsigned long i;
	int ret;

	phase_adj = nla_get_s32(phase_adj_attr);
	if (phase_adj > pin->prop->phase_range.max ||
	    phase_adj < pin->prop->phase_range.min) {
		NL_SET_ERR_MSG_ATTR(extack, phase_adj_attr,
				    "phase adjust value not supported");
		return -EINVAL;
	}

	xa_for_each(&pin->dpll_refs, i, ref) {
		ops = dpll_pin_ops(ref);
		if (!ops->phase_adjust_set || !ops->phase_adjust_get) {
			NL_SET_ERR_MSG(extack, "phase adjust not supported");
			return -EOPNOTSUPP;
		}
	}
	ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
	ops = dpll_pin_ops(ref);
	dpll = ref->dpll;
	ret = ops->phase_adjust_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
				    dpll, dpll_priv(dpll), &old_phase_adj,
				    extack);
	if (ret) {
		NL_SET_ERR_MSG(extack, "unable to get old phase adjust value");
		return ret;
	}
	if (phase_adj == old_phase_adj)
		return 0;

	xa_for_each(&pin->dpll_refs, i, ref) {
		ops = dpll_pin_ops(ref);
		dpll = ref->dpll;
		ret = ops->phase_adjust_set(pin,
					    dpll_pin_on_dpll_priv(dpll, pin),
					    dpll, dpll_priv(dpll), phase_adj,
					    extack);
		if (ret) {
			failed = ref;
			NL_SET_ERR_MSG_FMT(extack,
					   "phase adjust set failed for dpll_id:%u",
					   dpll->id);
			goto rollback;
		}
	}
	__dpll_pin_change_ntf(pin);

	return 0;

rollback:
	xa_for_each(&pin->dpll_refs, i, ref) {
		if (ref == failed)
			break;
		ops = dpll_pin_ops(ref);
		dpll = ref->dpll;
		if (ops->phase_adjust_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
					  dpll, dpll_priv(dpll), old_phase_adj,
					  extack))
			NL_SET_ERR_MSG(extack, "set phase adjust rollback failed");
	}
	return ret;
}

static int
dpll_pin_parent_device_set(struct dpll_pin *pin, struct nlattr *parent_nest,
			   struct netlink_ext_ack *extack)
@@ -793,6 +958,11 @@ dpll_pin_set_from_nlattr(struct dpll_pin *pin, struct genl_info *info)
			if (ret)
				return ret;
			break;
		case DPLL_A_PIN_PHASE_ADJUST:
			ret = dpll_pin_phase_adj_set(pin, a, info->extack);
			if (ret)
				return ret;
			break;
		case DPLL_A_PIN_PARENT_DEVICE:
			ret = dpll_pin_parent_device_set(pin, a, info->extack);
			if (ret)
+5 −3
Original line number Diff line number Diff line
@@ -11,11 +11,12 @@
#include <uapi/linux/dpll.h>

/* Common nested types */
const struct nla_policy dpll_pin_parent_device_nl_policy[DPLL_A_PIN_STATE + 1] = {
const struct nla_policy dpll_pin_parent_device_nl_policy[DPLL_A_PIN_PHASE_OFFSET + 1] = {
	[DPLL_A_PIN_PARENT_ID] = { .type = NLA_U32, },
	[DPLL_A_PIN_DIRECTION] = NLA_POLICY_RANGE(NLA_U32, 1, 2),
	[DPLL_A_PIN_PRIO] = { .type = NLA_U32, },
	[DPLL_A_PIN_STATE] = NLA_POLICY_RANGE(NLA_U32, 1, 3),
	[DPLL_A_PIN_PHASE_OFFSET] = { .type = NLA_S64, },
};

const struct nla_policy dpll_pin_parent_pin_nl_policy[DPLL_A_PIN_STATE + 1] = {
@@ -61,7 +62,7 @@ static const struct nla_policy dpll_pin_get_dump_nl_policy[DPLL_A_PIN_ID + 1] =
};

/* DPLL_CMD_PIN_SET - do */
static const struct nla_policy dpll_pin_set_nl_policy[DPLL_A_PIN_PARENT_PIN + 1] = {
static const struct nla_policy dpll_pin_set_nl_policy[DPLL_A_PIN_PHASE_ADJUST + 1] = {
	[DPLL_A_PIN_ID] = { .type = NLA_U32, },
	[DPLL_A_PIN_FREQUENCY] = { .type = NLA_U64, },
	[DPLL_A_PIN_DIRECTION] = NLA_POLICY_RANGE(NLA_U32, 1, 2),
@@ -69,6 +70,7 @@ static const struct nla_policy dpll_pin_set_nl_policy[DPLL_A_PIN_PARENT_PIN + 1]
	[DPLL_A_PIN_STATE] = NLA_POLICY_RANGE(NLA_U32, 1, 3),
	[DPLL_A_PIN_PARENT_DEVICE] = NLA_POLICY_NESTED(dpll_pin_parent_device_nl_policy),
	[DPLL_A_PIN_PARENT_PIN] = NLA_POLICY_NESTED(dpll_pin_parent_pin_nl_policy),
	[DPLL_A_PIN_PHASE_ADJUST] = { .type = NLA_S32, },
};

/* Ops table for dpll */
@@ -140,7 +142,7 @@ static const struct genl_split_ops dpll_nl_ops[] = {
		.doit		= dpll_nl_pin_set_doit,
		.post_doit	= dpll_pin_post_doit,
		.policy		= dpll_pin_set_nl_policy,
		.maxattr	= DPLL_A_PIN_PARENT_PIN,
		.maxattr	= DPLL_A_PIN_PHASE_ADJUST,
		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
	},
};
+1 −1
Original line number Diff line number Diff line
@@ -12,7 +12,7 @@
#include <uapi/linux/dpll.h>

/* Common nested types */
extern const struct nla_policy dpll_pin_parent_device_nl_policy[DPLL_A_PIN_STATE + 1];
extern const struct nla_policy dpll_pin_parent_device_nl_policy[DPLL_A_PIN_PHASE_OFFSET + 1];
extern const struct nla_policy dpll_pin_parent_pin_nl_policy[DPLL_A_PIN_STATE + 1];

int dpll_lock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
Loading