Commit 4f5710ed authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'dpll-add-reference-sync-feature'

Arkadiusz Kubalewski says:

====================
dpll: add Reference SYNC feature

The device may support the Reference SYNC feature, which allows the
combination of two inputs into a input pair. In this configuration,
clock signals from both inputs are used to synchronize the DPLL device.
The higher frequency signal is utilized for the loop bandwidth of the DPLL,
while the lower frequency signal is used to syntonize the output signal of
the DPLL device. This feature enables the provision of a high-quality loop
bandwidth signal from an external source.

A capable input provides a list of inputs that can be bound with to create
Reference SYNC. To control this feature, the user must request a
desired state for a target pin: use ``DPLL_PIN_STATE_CONNECTED`` to
enable or ``DPLL_PIN_STATE_DISCONNECTED`` to disable the feature. An input
pin can be bound to only one other pin at any given time.

Verify pins bind state/capabilities:
$ ./tools/net/ynl/pyynl/cli.py \
 --spec Documentation/netlink/specs/dpll.yaml \
 --do pin-get \
 --json '{"id":0}'
{'board-label': 'CVL-SDP22',
 'id': 0,
 [...]
 'reference-sync': [{'id': 1, 'state': 'disconnected'}],
 [...]}

Bind the pins by setting connected state between them:
$ ./tools/net/ynl/pyynl/cli.py \
 --spec Documentation/netlink/specs/dpll.yaml \
 --do pin-set \
 --json '{"id":0, "reference-sync":{"id":1, "state":"connected"}}'

Verify pins bind state:
$ ./tools/net/ynl/pyynl/cli.py \
 --spec Documentation/netlink/specs/dpll.yaml \
 --do pin-get \
 --json '{"id":0}'
{'board-label': 'CVL-SDP22',
 'id': 0,
 [...]
 'reference-sync': [{'id': 1, 'state': 'connected'}],
 [...]}

Unbind the pins by setting disconnected state between them:
$ ./tools/net/ynl/pyynl/cli.py \
 --spec Documentation/netlink/specs/dpll.yaml \
 --do pin-set \
 --json '{"id":0, "reference-sync":{"id":1, "state":"disconnected"}}'
====================

Link: https://patch.msgid.link/20250626135219.1769350-1-arkadiusz.kubalewski@intel.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 8c72b2a2 5bcea241
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
@@ -253,6 +253,31 @@ the pin.
  ``DPLL_A_PIN_ESYNC_PULSE``                pulse type of Embedded SYNC
  ========================================= =================================

Reference SYNC
==============

The device may support the Reference SYNC feature, which allows the combination
of two inputs into a input pair. In this configuration, clock signals
from both inputs are used to synchronize the DPLL device. The higher frequency
signal is utilized for the loop bandwidth of the DPLL, while the lower frequency
signal is used to syntonize the output signal of the DPLL device. This feature
enables the provision of a high-quality loop bandwidth signal from an external
source.

A capable input provides a list of inputs that can be bound with to create
Reference SYNC. To control this feature, the user must request a desired
state for a target pin: use ``DPLL_PIN_STATE_CONNECTED`` to enable or
``DPLL_PIN_STATE_DISCONNECTED`` to disable the feature. An input pin can be
bound to only one other pin at any given time.

  ============================== ==========================================
  ``DPLL_A_PIN_REFERENCE_SYNC``  nested attribute for providing info or
                                 requesting configuration of the Reference
                                 SYNC feature
    ``DPLL_A_PIN_ID``            target pin id for Reference SYNC feature
    ``DPLL_A_PIN_STATE``         state of Reference SYNC connection
  ============================== ==========================================

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

+19 −0
Original line number Diff line number Diff line
@@ -428,6 +428,15 @@ attribute-sets:
        doc: |
          A ratio of high to low state of a SYNC signal pulse embedded
          into base clock frequency. Value is in percents.
      -
        name: reference-sync
        type: nest
        multi-attr: true
        nested-attributes: reference-sync
        doc: |
          Capable pin provides list of pins that can be bound to create a
          reference-sync pin pair.

  -
    name: pin-parent-device
    subset-of: pin
@@ -458,6 +467,14 @@ attribute-sets:
        name: frequency-min
      -
        name: frequency-max
  -
    name: reference-sync
    subset-of: pin
    attributes:
      -
        name: id
      -
        name: state

operations:
  enum-name: dpll_cmd
@@ -598,6 +615,7 @@ operations:
            - esync-frequency
            - esync-frequency-supported
            - esync-pulse
            - reference-sync

      dump:
        request:
@@ -625,6 +643,7 @@ operations:
            - parent-pin
            - phase-adjust
            - esync-frequency
            - reference-sync
    -
      name: pin-create-ntf
      doc: Notification about pin appearing
+45 −0
Original line number Diff line number Diff line
@@ -506,6 +506,7 @@ dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module,
	refcount_set(&pin->refcount, 1);
	xa_init_flags(&pin->dpll_refs, XA_FLAGS_ALLOC);
	xa_init_flags(&pin->parent_refs, XA_FLAGS_ALLOC);
	xa_init_flags(&pin->ref_sync_pins, XA_FLAGS_ALLOC);
	ret = xa_alloc_cyclic(&dpll_pin_xa, &pin->id, pin, xa_limit_32b,
			      &dpll_pin_xa_id, GFP_KERNEL);
	if (ret < 0)
@@ -514,6 +515,7 @@ dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module,
err_xa_alloc:
	xa_destroy(&pin->dpll_refs);
	xa_destroy(&pin->parent_refs);
	xa_destroy(&pin->ref_sync_pins);
	dpll_pin_prop_free(&pin->prop);
err_pin_prop:
	kfree(pin);
@@ -595,6 +597,7 @@ void dpll_pin_put(struct dpll_pin *pin)
		xa_erase(&dpll_pin_xa, pin->id);
		xa_destroy(&pin->dpll_refs);
		xa_destroy(&pin->parent_refs);
		xa_destroy(&pin->ref_sync_pins);
		dpll_pin_prop_free(&pin->prop);
		kfree_rcu(pin, rcu);
	}
@@ -659,11 +662,26 @@ dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
}
EXPORT_SYMBOL_GPL(dpll_pin_register);

static void dpll_pin_ref_sync_pair_del(u32 ref_sync_pin_id)
{
	struct dpll_pin *pin, *ref_sync_pin;
	unsigned long i;

	xa_for_each(&dpll_pin_xa, i, pin) {
		ref_sync_pin = xa_load(&pin->ref_sync_pins, ref_sync_pin_id);
		if (ref_sync_pin) {
			xa_erase(&pin->ref_sync_pins, ref_sync_pin_id);
			__dpll_pin_change_ntf(pin);
		}
	}
}

static void
__dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
		      const struct dpll_pin_ops *ops, void *priv, void *cookie)
{
	ASSERT_DPLL_PIN_REGISTERED(pin);
	dpll_pin_ref_sync_pair_del(pin->id);
	dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv, cookie);
	dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll, ops, priv, cookie);
	if (xa_empty(&pin->dpll_refs))
@@ -783,6 +801,33 @@ void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
}
EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister);

/**
 * dpll_pin_ref_sync_pair_add - create a reference sync signal pin pair
 * @pin: pin which produces the base frequency
 * @ref_sync_pin: pin which produces the sync signal
 *
 * Once pins are paired, the user-space configuration of reference sync pair
 * is possible.
 * Context: Acquires a lock (dpll_lock)
 * Return:
 * * 0 on success
 * * negative - error value
 */
int dpll_pin_ref_sync_pair_add(struct dpll_pin *pin,
			       struct dpll_pin *ref_sync_pin)
{
	int ret;

	mutex_lock(&dpll_lock);
	ret = xa_insert(&pin->ref_sync_pins, ref_sync_pin->id,
			ref_sync_pin, GFP_KERNEL);
	__dpll_pin_change_ntf(pin);
	mutex_unlock(&dpll_lock);

	return ret;
}
EXPORT_SYMBOL_GPL(dpll_pin_ref_sync_pair_add);

static struct dpll_device_registration *
dpll_device_registration_first(struct dpll_device *dpll)
{
+2 −0
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ struct dpll_device {
 * @module:		module of creator
 * @dpll_refs:		hold referencees to dplls pin was registered with
 * @parent_refs:	hold references to parent pins pin was registered with
 * @ref_sync_pins:	hold references to pins for Reference SYNC feature
 * @prop:		pin properties copied from the registerer
 * @refcount:		refcount
 * @rcu:		rcu_head for kfree_rcu()
@@ -55,6 +56,7 @@ struct dpll_pin {
	struct module *module;
	struct xarray dpll_refs;
	struct xarray parent_refs;
	struct xarray ref_sync_pins;
	struct dpll_pin_properties prop;
	refcount_t refcount;
	struct rcu_head rcu;
+171 −19
Original line number Diff line number Diff line
@@ -48,6 +48,24 @@ dpll_msg_add_dev_parent_handle(struct sk_buff *msg, u32 id)
	return 0;
}

static bool dpll_pin_available(struct dpll_pin *pin)
{
	struct dpll_pin_ref *par_ref;
	unsigned long i;

	if (!xa_get_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED))
		return false;
	xa_for_each(&pin->parent_refs, i, par_ref)
		if (xa_get_mark(&dpll_pin_xa, par_ref->pin->id,
				DPLL_REGISTERED))
			return true;
	xa_for_each(&pin->dpll_refs, i, par_ref)
		if (xa_get_mark(&dpll_device_xa, par_ref->dpll->id,
				DPLL_REGISTERED))
			return true;
	return false;
}

/**
 * dpll_msg_add_pin_handle - attach pin handle attribute to a given message
 * @msg: pointer to sk_buff message to attach a pin handle
@@ -428,6 +446,47 @@ dpll_msg_add_pin_esync(struct sk_buff *msg, struct dpll_pin *pin,
	return -EMSGSIZE;
}

static int
dpll_msg_add_pin_ref_sync(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;
	void *pin_priv, *ref_sync_pin_priv;
	struct dpll_pin *ref_sync_pin;
	enum dpll_pin_state state;
	struct nlattr *nest;
	unsigned long index;
	int ret;

	pin_priv = dpll_pin_on_dpll_priv(dpll, pin);
	xa_for_each(&pin->ref_sync_pins, index, ref_sync_pin) {
		if (!dpll_pin_available(ref_sync_pin))
			continue;
		ref_sync_pin_priv = dpll_pin_on_dpll_priv(dpll, ref_sync_pin);
		if (WARN_ON(!ops->ref_sync_get))
			return -EOPNOTSUPP;
		ret = ops->ref_sync_get(pin, pin_priv, ref_sync_pin,
					ref_sync_pin_priv, &state, extack);
		if (ret)
			return ret;
		nest = nla_nest_start(msg, DPLL_A_PIN_REFERENCE_SYNC);
		if (!nest)
			return -EMSGSIZE;
		if (nla_put_s32(msg, DPLL_A_PIN_ID, ref_sync_pin->id))
			goto nest_cancel;
		if (nla_put_s32(msg, DPLL_A_PIN_STATE, state))
			goto nest_cancel;
		nla_nest_end(msg, nest);
	}
	return 0;

nest_cancel:
	nla_nest_cancel(msg, nest);
	return -EMSGSIZE;
}

static bool dpll_pin_is_freq_supported(struct dpll_pin *pin, u32 freq)
{
	int fs;
@@ -570,6 +629,10 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
	if (ret)
		return ret;
	ret = dpll_msg_add_pin_esync(msg, pin, ref, extack);
	if (ret)
		return ret;
	if (!xa_empty(&pin->ref_sync_pins))
		ret = dpll_msg_add_pin_ref_sync(msg, pin, ref, extack);
	if (ret)
		return ret;
	if (xa_empty(&pin->parent_refs))
@@ -665,24 +728,6 @@ __dpll_device_change_ntf(struct dpll_device *dpll)
	return dpll_device_event_send(DPLL_CMD_DEVICE_CHANGE_NTF, dpll);
}

static bool dpll_pin_available(struct dpll_pin *pin)
{
	struct dpll_pin_ref *par_ref;
	unsigned long i;

	if (!xa_get_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED))
		return false;
	xa_for_each(&pin->parent_refs, i, par_ref)
		if (xa_get_mark(&dpll_pin_xa, par_ref->pin->id,
				DPLL_REGISTERED))
			return true;
	xa_for_each(&pin->dpll_refs, i, par_ref)
		if (xa_get_mark(&dpll_device_xa, par_ref->dpll->id,
				DPLL_REGISTERED))
			return true;
	return false;
}

/**
 * dpll_device_change_ntf - notify that the dpll device has been changed
 * @dpll: registered dpll pointer
@@ -745,7 +790,7 @@ int dpll_pin_delete_ntf(struct dpll_pin *pin)
	return dpll_pin_event_send(DPLL_CMD_PIN_DELETE_NTF, pin);
}

static int __dpll_pin_change_ntf(struct dpll_pin *pin)
int __dpll_pin_change_ntf(struct dpll_pin *pin)
{
	return dpll_pin_event_send(DPLL_CMD_PIN_CHANGE_NTF, pin);
}
@@ -935,6 +980,108 @@ dpll_pin_esync_set(struct dpll_pin *pin, struct nlattr *a,
	return ret;
}

static int
dpll_pin_ref_sync_state_set(struct dpll_pin *pin,
			    unsigned long ref_sync_pin_idx,
			    const enum dpll_pin_state state,
			    struct netlink_ext_ack *extack)

{
	struct dpll_pin_ref *ref, *failed;
	const struct dpll_pin_ops *ops;
	enum dpll_pin_state old_state;
	struct dpll_pin *ref_sync_pin;
	struct dpll_device *dpll;
	unsigned long i;
	int ret;

	ref_sync_pin = xa_find(&pin->ref_sync_pins, &ref_sync_pin_idx,
			       ULONG_MAX, XA_PRESENT);
	if (!ref_sync_pin) {
		NL_SET_ERR_MSG(extack, "reference sync pin not found");
		return -EINVAL;
	}
	if (!dpll_pin_available(ref_sync_pin)) {
		NL_SET_ERR_MSG(extack, "reference sync pin not available");
		return -EINVAL;
	}
	ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
	ASSERT_NOT_NULL(ref);
	ops = dpll_pin_ops(ref);
	if (!ops->ref_sync_set || !ops->ref_sync_get) {
		NL_SET_ERR_MSG(extack, "reference sync not supported by this pin");
		return -EOPNOTSUPP;
	}
	dpll = ref->dpll;
	ret = ops->ref_sync_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
				ref_sync_pin,
				dpll_pin_on_dpll_priv(dpll, ref_sync_pin),
				&old_state, extack);
	if (ret) {
		NL_SET_ERR_MSG(extack, "unable to get old reference sync state");
		return ret;
	}
	if (state == old_state)
		return 0;
	xa_for_each(&pin->dpll_refs, i, ref) {
		ops = dpll_pin_ops(ref);
		dpll = ref->dpll;
		ret = ops->ref_sync_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
					ref_sync_pin,
					dpll_pin_on_dpll_priv(dpll,
							      ref_sync_pin),
					state, extack);
		if (ret) {
			failed = ref;
			NL_SET_ERR_MSG_FMT(extack, "reference sync 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->ref_sync_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
				      ref_sync_pin,
				      dpll_pin_on_dpll_priv(dpll, ref_sync_pin),
				      old_state, extack))
			NL_SET_ERR_MSG(extack, "set reference sync rollback failed");
	}
	return ret;
}

static int
dpll_pin_ref_sync_set(struct dpll_pin *pin, struct nlattr *nest,
		      struct netlink_ext_ack *extack)
{
	struct nlattr *tb[DPLL_A_PIN_MAX + 1];
	enum dpll_pin_state state;
	u32 sync_pin_id;

	nla_parse_nested(tb, DPLL_A_PIN_MAX, nest,
			 dpll_reference_sync_nl_policy, extack);
	if (!tb[DPLL_A_PIN_ID]) {
		NL_SET_ERR_MSG(extack, "sync pin id expected");
		return -EINVAL;
	}
	sync_pin_id = nla_get_u32(tb[DPLL_A_PIN_ID]);

	if (!tb[DPLL_A_PIN_STATE]) {
		NL_SET_ERR_MSG(extack, "sync pin state expected");
		return -EINVAL;
	}
	state = nla_get_u32(tb[DPLL_A_PIN_STATE]);

	return dpll_pin_ref_sync_state_set(pin, sync_pin_id, state, extack);
}

static int
dpll_pin_on_pin_state_set(struct dpll_pin *pin, u32 parent_idx,
			  enum dpll_pin_state state,
@@ -1241,6 +1388,11 @@ dpll_pin_set_from_nlattr(struct dpll_pin *pin, struct genl_info *info)
			if (ret)
				return ret;
			break;
		case DPLL_A_PIN_REFERENCE_SYNC:
			ret = dpll_pin_ref_sync_set(pin, a, info->extack);
			if (ret)
				return ret;
			break;
		}
	}

Loading