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

Merge branch 'add-embedded-sync-feature-for-a-dpll-s-pin'



Arkadiusz Kubalewski says:

====================
Add Embedded SYNC feature for a dpll's pin

Introduce and allow DPLL subsystem users to get/set capabilities of
Embedded SYNC on a dpll's pin.

Signed-off-by: default avatarArkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
====================

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


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 2c163922 87abc566
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -214,6 +214,27 @@ 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.

Embedded SYNC
=============

Device may provide ability to use Embedded SYNC feature. It allows
to embed additional SYNC signal into the base frequency of a pin - a one
special pulse of base frequency signal every time SYNC signal pulse
happens. The user can configure the frequency of Embedded SYNC.
The Embedded SYNC capability is always related to a given base frequency
and HW capabilities. The user is provided a range of Embedded SYNC
frequencies supported, depending on current base frequency configured for
the pin.

  ========================================= =================================
  ``DPLL_A_PIN_ESYNC_FREQUENCY``            current Embedded SYNC frequency
  ``DPLL_A_PIN_ESYNC_FREQUENCY_SUPPORTED``  nest available Embedded SYNC
                                            frequency ranges
    ``DPLL_A_PIN_FREQUENCY_MIN``            attr minimum value of frequency
    ``DPLL_A_PIN_FREQUENCY_MAX``            attr maximum value of frequency
  ``DPLL_A_PIN_ESYNC_PULSE``                pulse type of Embedded SYNC
  ========================================= =================================

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

+24 −0
Original line number Diff line number Diff line
@@ -345,6 +345,26 @@ attribute-sets:
          Value is in PPM (parts per million).
          This may be implemented for example for pin of type
          PIN_TYPE_SYNCE_ETH_PORT.
      -
        name: esync-frequency
        type: u64
        doc: |
          Frequency of Embedded SYNC signal. If provided, the pin is configured
          with a SYNC signal embedded into its base clock frequency.
      -
        name: esync-frequency-supported
        type: nest
        multi-attr: true
        nested-attributes: frequency-range
        doc: |
          If provided a pin is capable of embedding a SYNC signal (within given
          range) into its base frequency signal.
      -
        name: esync-pulse
        type: u32
        doc: |
          A ratio of high to low state of a SYNC signal pulse embedded
          into base clock frequency. Value is in percents.
  -
    name: pin-parent-device
    subset-of: pin
@@ -510,6 +530,9 @@ operations:
            - phase-adjust-max
            - phase-adjust
            - fractional-frequency-offset
            - esync-frequency
            - esync-frequency-supported
            - esync-pulse

      dump:
        request:
@@ -536,6 +559,7 @@ operations:
            - parent-device
            - parent-pin
            - phase-adjust
            - esync-frequency
    -
      name: pin-create-ntf
      doc: Notification about pin appearing
+130 −0
Original line number Diff line number Diff line
@@ -342,6 +342,51 @@ dpll_msg_add_pin_freq(struct sk_buff *msg, struct dpll_pin *pin,
	return 0;
}

static int
dpll_msg_add_pin_esync(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;
	struct dpll_pin_esync esync;
	struct nlattr *nest;
	int ret, i;

	if (!ops->esync_get)
		return 0;
	ret = ops->esync_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
			     dpll_priv(dpll), &esync, extack);
	if (ret == -EOPNOTSUPP)
		return 0;
	else if (ret)
		return ret;
	if (nla_put_64bit(msg, DPLL_A_PIN_ESYNC_FREQUENCY, sizeof(esync.freq),
			  &esync.freq, DPLL_A_PIN_PAD))
		return -EMSGSIZE;
	if (nla_put_u32(msg, DPLL_A_PIN_ESYNC_PULSE, esync.pulse))
		return -EMSGSIZE;
	for (i = 0; i < esync.range_num; i++) {
		nest = nla_nest_start(msg,
				      DPLL_A_PIN_ESYNC_FREQUENCY_SUPPORTED);
		if (!nest)
			return -EMSGSIZE;
		if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MIN,
				  sizeof(esync.range[i].min),
				  &esync.range[i].min, DPLL_A_PIN_PAD))
			goto nest_cancel;
		if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MAX,
				  sizeof(esync.range[i].max),
				  &esync.range[i].max, DPLL_A_PIN_PAD))
			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;
@@ -481,6 +526,9 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
	if (ret)
		return ret;
	ret = dpll_msg_add_ffo(msg, pin, ref, extack);
	if (ret)
		return ret;
	ret = dpll_msg_add_pin_esync(msg, pin, ref, extack);
	if (ret)
		return ret;
	if (xa_empty(&pin->parent_refs))
@@ -738,6 +786,83 @@ dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
	return ret;
}

static int
dpll_pin_esync_set(struct dpll_pin *pin, struct nlattr *a,
		   struct netlink_ext_ack *extack)
{
	struct dpll_pin_ref *ref, *failed;
	const struct dpll_pin_ops *ops;
	struct dpll_pin_esync esync;
	u64 freq = nla_get_u64(a);
	struct dpll_device *dpll;
	bool supported = false;
	unsigned long i;
	int ret;

	xa_for_each(&pin->dpll_refs, i, ref) {
		ops = dpll_pin_ops(ref);
		if (!ops->esync_set || !ops->esync_get) {
			NL_SET_ERR_MSG(extack,
				       "embedded sync feature is not supported by this device");
			return -EOPNOTSUPP;
		}
	}
	ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
	ops = dpll_pin_ops(ref);
	dpll = ref->dpll;
	ret = ops->esync_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
			     dpll_priv(dpll), &esync, extack);
	if (ret) {
		NL_SET_ERR_MSG(extack, "unable to get current embedded sync frequency value");
		return ret;
	}
	if (freq == esync.freq)
		return 0;
	for (i = 0; i < esync.range_num; i++)
		if (freq <= esync.range[i].max && freq >= esync.range[i].min)
			supported = true;
	if (!supported) {
		NL_SET_ERR_MSG_ATTR(extack, a,
				    "requested embedded sync frequency value is not supported by this device");
		return -EINVAL;
	}

	xa_for_each(&pin->dpll_refs, i, ref) {
		void *pin_dpll_priv;

		ops = dpll_pin_ops(ref);
		dpll = ref->dpll;
		pin_dpll_priv = dpll_pin_on_dpll_priv(dpll, pin);
		ret = ops->esync_set(pin, pin_dpll_priv, dpll, dpll_priv(dpll),
				      freq, extack);
		if (ret) {
			failed = ref;
			NL_SET_ERR_MSG_FMT(extack,
					   "embedded sync 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) {
		void *pin_dpll_priv;

		if (ref == failed)
			break;
		ops = dpll_pin_ops(ref);
		dpll = ref->dpll;
		pin_dpll_priv = dpll_pin_on_dpll_priv(dpll, pin);
		if (ops->esync_set(pin, pin_dpll_priv, dpll, dpll_priv(dpll),
				   esync.freq, extack))
			NL_SET_ERR_MSG(extack, "set embedded sync frequency rollback failed");
	}
	return ret;
}

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

+3 −2
Original line number Diff line number Diff line
@@ -62,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_PHASE_ADJUST + 1] = {
static const struct nla_policy dpll_pin_set_nl_policy[DPLL_A_PIN_ESYNC_FREQUENCY + 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),
@@ -71,6 +71,7 @@ static const struct nla_policy dpll_pin_set_nl_policy[DPLL_A_PIN_PHASE_ADJUST +
	[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, },
	[DPLL_A_PIN_ESYNC_FREQUENCY] = { .type = NLA_U64, },
};

/* Ops table for dpll */
@@ -138,7 +139,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_PHASE_ADJUST,
		.maxattr	= DPLL_A_PIN_ESYNC_FREQUENCY,
		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
	},
};
+220 −3
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#define ICE_CGU_STATE_ACQ_ERR_THRESHOLD		50
#define ICE_DPLL_PIN_IDX_INVALID		0xff
#define ICE_DPLL_RCLK_NUM_PER_PF		1
#define ICE_DPLL_PIN_ESYNC_PULSE_HIGH_PERCENT	25

/**
 * enum ice_dpll_pin_type - enumerate ice pin types:
@@ -30,6 +31,10 @@ static const char * const pin_type_name[] = {
	[ICE_DPLL_PIN_TYPE_RCLK_INPUT] = "rclk-input",
};

static const struct dpll_pin_frequency ice_esync_range[] = {
	DPLL_PIN_FREQUENCY_RANGE(0, DPLL_PIN_FREQUENCY_1_HZ),
};

/**
 * ice_dpll_is_reset - check if reset is in progress
 * @pf: private board structure
@@ -394,8 +399,8 @@ ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,

	switch (pin_type) {
	case ICE_DPLL_PIN_TYPE_INPUT:
		ret = ice_aq_get_input_pin_cfg(&pf->hw, pin->idx, NULL, NULL,
					       NULL, &pin->flags[0],
		ret = ice_aq_get_input_pin_cfg(&pf->hw, pin->idx, &pin->status,
					       NULL, NULL, &pin->flags[0],
					       &pin->freq, &pin->phase_adjust);
		if (ret)
			goto err;
@@ -430,7 +435,7 @@ ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
			goto err;

		parent &= ICE_AQC_GET_CGU_OUT_CFG_DPLL_SRC_SEL;
		if (ICE_AQC_SET_CGU_OUT_CFG_OUT_EN & pin->flags[0]) {
		if (ICE_AQC_GET_CGU_OUT_CFG_OUT_EN & pin->flags[0]) {
			pin->state[pf->dplls.eec.dpll_idx] =
				parent == pf->dplls.eec.dpll_idx ?
				DPLL_PIN_STATE_CONNECTED :
@@ -1098,6 +1103,214 @@ ice_dpll_phase_offset_get(const struct dpll_pin *pin, void *pin_priv,
	return 0;
}

/**
 * ice_dpll_output_esync_set - callback for setting embedded sync
 * @pin: pointer to a pin
 * @pin_priv: private data pointer passed on pin registration
 * @dpll: registered dpll pointer
 * @dpll_priv: private data pointer passed on dpll registration
 * @freq: requested embedded sync frequency
 * @extack: error reporting
 *
 * Dpll subsystem callback. Handler for setting embedded sync frequency value
 * on output pin.
 *
 * Context: Acquires pf->dplls.lock
 * Return:
 * * 0 - success
 * * negative - error
 */
static int
ice_dpll_output_esync_set(const struct dpll_pin *pin, void *pin_priv,
			  const struct dpll_device *dpll, void *dpll_priv,
			  u64 freq, struct netlink_ext_ack *extack)
{
	struct ice_dpll_pin *p = pin_priv;
	struct ice_dpll *d = dpll_priv;
	struct ice_pf *pf = d->pf;
	u8 flags = 0;
	int ret;

	if (ice_dpll_is_reset(pf, extack))
		return -EBUSY;
	mutex_lock(&pf->dplls.lock);
	if (p->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_OUT_EN)
		flags = ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
	if (freq == DPLL_PIN_FREQUENCY_1_HZ) {
		if (p->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN) {
			ret = 0;
		} else {
			flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
			ret = ice_aq_set_output_pin_cfg(&pf->hw, p->idx, flags,
							0, 0, 0);
		}
	} else {
		if (!(p->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)) {
			ret = 0;
		} else {
			flags &= ~ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
			ret = ice_aq_set_output_pin_cfg(&pf->hw, p->idx, flags,
							0, 0, 0);
		}
	}
	mutex_unlock(&pf->dplls.lock);

	return ret;
}

/**
 * ice_dpll_output_esync_get - callback for getting embedded sync config
 * @pin: pointer to a pin
 * @pin_priv: private data pointer passed on pin registration
 * @dpll: registered dpll pointer
 * @dpll_priv: private data pointer passed on dpll registration
 * @esync: on success holds embedded sync pin properties
 * @extack: error reporting
 *
 * Dpll subsystem callback. Handler for getting embedded sync frequency value
 * and capabilities on output pin.
 *
 * Context: Acquires pf->dplls.lock
 * Return:
 * * 0 - success
 * * negative - error
 */
static int
ice_dpll_output_esync_get(const struct dpll_pin *pin, void *pin_priv,
			  const struct dpll_device *dpll, void *dpll_priv,
			  struct dpll_pin_esync *esync,
			  struct netlink_ext_ack *extack)
{
	struct ice_dpll_pin *p = pin_priv;
	struct ice_dpll *d = dpll_priv;
	struct ice_pf *pf = d->pf;

	if (ice_dpll_is_reset(pf, extack))
		return -EBUSY;
	mutex_lock(&pf->dplls.lock);
	if (!(p->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_ABILITY) ||
	    p->freq != DPLL_PIN_FREQUENCY_10_MHZ) {
		mutex_unlock(&pf->dplls.lock);
		return -EOPNOTSUPP;
	}
	esync->range = ice_esync_range;
	esync->range_num = ARRAY_SIZE(ice_esync_range);
	if (p->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN) {
		esync->freq = DPLL_PIN_FREQUENCY_1_HZ;
		esync->pulse = ICE_DPLL_PIN_ESYNC_PULSE_HIGH_PERCENT;
	} else {
		esync->freq = 0;
		esync->pulse = 0;
	}
	mutex_unlock(&pf->dplls.lock);

	return 0;
}

/**
 * ice_dpll_input_esync_set - callback for setting embedded sync
 * @pin: pointer to a pin
 * @pin_priv: private data pointer passed on pin registration
 * @dpll: registered dpll pointer
 * @dpll_priv: private data pointer passed on dpll registration
 * @freq: requested embedded sync frequency
 * @extack: error reporting
 *
 * Dpll subsystem callback. Handler for setting embedded sync frequency value
 * on input pin.
 *
 * Context: Acquires pf->dplls.lock
 * Return:
 * * 0 - success
 * * negative - error
 */
static int
ice_dpll_input_esync_set(const struct dpll_pin *pin, void *pin_priv,
			 const struct dpll_device *dpll, void *dpll_priv,
			 u64 freq, struct netlink_ext_ack *extack)
{
	struct ice_dpll_pin *p = pin_priv;
	struct ice_dpll *d = dpll_priv;
	struct ice_pf *pf = d->pf;
	u8 flags_en = 0;
	int ret;

	if (ice_dpll_is_reset(pf, extack))
		return -EBUSY;
	mutex_lock(&pf->dplls.lock);
	if (p->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN)
		flags_en = ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN;
	if (freq == DPLL_PIN_FREQUENCY_1_HZ) {
		if (p->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN) {
			ret = 0;
		} else {
			flags_en |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
			ret = ice_aq_set_input_pin_cfg(&pf->hw, p->idx, 0,
						       flags_en, 0, 0);
		}
	} else {
		if (!(p->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)) {
			ret = 0;
		} else {
			flags_en &= ~ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
			ret = ice_aq_set_input_pin_cfg(&pf->hw, p->idx, 0,
						       flags_en, 0, 0);
		}
	}
	mutex_unlock(&pf->dplls.lock);

	return ret;
}

/**
 * ice_dpll_input_esync_get - callback for getting embedded sync config
 * @pin: pointer to a pin
 * @pin_priv: private data pointer passed on pin registration
 * @dpll: registered dpll pointer
 * @dpll_priv: private data pointer passed on dpll registration
 * @esync: on success holds embedded sync pin properties
 * @extack: error reporting
 *
 * Dpll subsystem callback. Handler for getting embedded sync frequency value
 * and capabilities on input pin.
 *
 * Context: Acquires pf->dplls.lock
 * Return:
 * * 0 - success
 * * negative - error
 */
static int
ice_dpll_input_esync_get(const struct dpll_pin *pin, void *pin_priv,
			 const struct dpll_device *dpll, void *dpll_priv,
			 struct dpll_pin_esync *esync,
			 struct netlink_ext_ack *extack)
{
	struct ice_dpll_pin *p = pin_priv;
	struct ice_dpll *d = dpll_priv;
	struct ice_pf *pf = d->pf;

	if (ice_dpll_is_reset(pf, extack))
		return -EBUSY;
	mutex_lock(&pf->dplls.lock);
	if (!(p->status & ICE_AQC_GET_CGU_IN_CFG_STATUS_ESYNC_CAP) ||
	    p->freq != DPLL_PIN_FREQUENCY_10_MHZ) {
		mutex_unlock(&pf->dplls.lock);
		return -EOPNOTSUPP;
	}
	esync->range = ice_esync_range;
	esync->range_num = ARRAY_SIZE(ice_esync_range);
	if (p->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN) {
		esync->freq = DPLL_PIN_FREQUENCY_1_HZ;
		esync->pulse = ICE_DPLL_PIN_ESYNC_PULSE_HIGH_PERCENT;
	} else {
		esync->freq = 0;
		esync->pulse = 0;
	}
	mutex_unlock(&pf->dplls.lock);

	return 0;
}

/**
 * ice_dpll_rclk_state_on_pin_set - set a state on rclk pin
 * @pin: pointer to a pin
@@ -1222,6 +1435,8 @@ static const struct dpll_pin_ops ice_dpll_input_ops = {
	.phase_adjust_get = ice_dpll_pin_phase_adjust_get,
	.phase_adjust_set = ice_dpll_input_phase_adjust_set,
	.phase_offset_get = ice_dpll_phase_offset_get,
	.esync_set = ice_dpll_input_esync_set,
	.esync_get = ice_dpll_input_esync_get,
};

static const struct dpll_pin_ops ice_dpll_output_ops = {
@@ -1232,6 +1447,8 @@ static const struct dpll_pin_ops ice_dpll_output_ops = {
	.direction_get = ice_dpll_output_direction,
	.phase_adjust_get = ice_dpll_pin_phase_adjust_get,
	.phase_adjust_set = ice_dpll_output_phase_adjust_set,
	.esync_set = ice_dpll_output_esync_set,
	.esync_get = ice_dpll_output_esync_get,
};

static const struct dpll_device_ops ice_dpll_ops = {
Loading