Netdev List
 help / color / mirror / Atom feed
* [PATCH net-next v2 0/2] dpll: use pin owner's dpll ref for pin-level set callbacks
@ 2026-08-03 12:02 Ivan Vecera
  2026-08-03 12:02 ` [PATCH net-next v2 1/2] dpll: zl3073x: update all DPLL channels on ref_sync_set Ivan Vecera
  2026-08-03 12:02 ` [PATCH net-next v2 2/2] dpll: use pin owner's dpll ref for pin-level attribute setting Ivan Vecera
  0 siblings, 2 replies; 6+ messages in thread
From: Ivan Vecera @ 2026-08-03 12:02 UTC (permalink / raw)
  To: netdev
  Cc: Arkadiusz Kubalewski, Jakub Kicinski, Jiri Pirko, Paolo Abeni,
	Petr Oros, Prathosh Satish, Richard Cochran, Shuah Khan,
	Vadim Fedorenko, linux-kernel

Pin-level attributes (frequency, phase adjust, embedded sync, reference
sync) are properties of the pin itself. The get callbacks already use
only the pin owner's DPLL reference, but the set callbacks iterate over
all registered DPLL devices — resulting in redundant HW writes for
drivers that share a pin across multiple DPLLs.

This series simplifies the set side to match the get side: call the set
callback only through the owner's reference.

Patch 1 prepares the zl3073x driver whose ref_sync_set callback had
per-channel behavior (setting priority on a single DPLL channel). It now
iterates all channels internally so it remains correct when invoked only
once.

Patch 2 drops the xa_for_each loops from dpll_pin_freq_set(),
dpll_pin_esync_set(), dpll_pin_ref_sync_state_set() and
dpll_pin_phase_adj_set(), along with the rollback logic and the per-ref
-EOPNOTSUPP validation scan. The dpll.rst documentation is updated to
reflect the new behavior.

v2:
  - Split zl3073x ref_sync_set fix into a separate preparation patch
  - Update dpll.rst documentation to match the new behavior
  - Expand commit message to describe the -EOPNOTSUPP check change

Ivan Vecera (2):
  dpll: zl3073x: update all DPLL channels on ref_sync_set
  dpll: use pin owner's dpll ref for pin-level attribute setting

 Documentation/driver-api/dpll.rst |  10 +-
 drivers/dpll/dpll_netlink.c       | 213 +++++++-----------------------
 drivers/dpll/zl3073x/dpll.c       |  46 +++++--
 3 files changed, 89 insertions(+), 180 deletions(-)


base-commit: 69963a0678a347d57c4ac8b16939dba216eb95ce
-- 
2.54.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH net-next v2 1/2] dpll: zl3073x: update all DPLL channels on ref_sync_set
  2026-08-03 12:02 [PATCH net-next v2 0/2] dpll: use pin owner's dpll ref for pin-level set callbacks Ivan Vecera
@ 2026-08-03 12:02 ` Ivan Vecera
  2026-08-06 13:07   ` Ivan Vecera
  2026-08-03 12:02 ` [PATCH net-next v2 2/2] dpll: use pin owner's dpll ref for pin-level attribute setting Ivan Vecera
  1 sibling, 1 reply; 6+ messages in thread
From: Ivan Vecera @ 2026-08-03 12:02 UTC (permalink / raw)
  To: netdev
  Cc: Arkadiusz Kubalewski, Jakub Kicinski, Jiri Pirko, Paolo Abeni,
	Petr Oros, Prathosh Satish, Richard Cochran, Shuah Khan,
	Vadim Fedorenko, linux-kernel

zl3073x_dpll_input_pin_ref_sync_set() excludes the sync source from
automatic reference selection by setting its priority to NONE, but
currently only does this on the single DPLL channel whose pin_priv
was passed to the callback.

Since input pins are registered with every DPLL channel, the priority
update must cover all channels to prevent the sync source from
remaining a selectable candidate on the other channels. This is
a preparation for the following patch which changes the DPLL core to
invoke pin-level set callbacks only through the pin owner's reference
instead of iterating over all registered DPLL devices.

Replace the single-channel priority write with a list_for_each_entry()
loop over all DPLL channels. Each channel's lock is acquired
individually for its read-modify-write sequence. The guard(mutex) is
replaced with explicit mutex_lock/mutex_unlock to allow releasing the
owner's lock before iterating, avoiding nested locking of the same
mutex class.

Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
 drivers/dpll/zl3073x/dpll.c | 46 +++++++++++++++++++++++++++----------
 1 file changed, 34 insertions(+), 12 deletions(-)

diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
index 0488ae6ac486c8..1ea924a9b986b2 100644
--- a/drivers/dpll/zl3073x/dpll.c
+++ b/drivers/dpll/zl3073x/dpll.c
@@ -265,7 +265,7 @@ zl3073x_dpll_input_pin_ref_sync_set(const struct dpll_pin *dpll_pin,
 	struct zl3073x_ref ref;
 	int rc;
 
-	guard(mutex)(&zldpll->lock);
+	mutex_lock(&zldpll->lock);
 
 	ref_id = zl3073x_input_pin_ref_get(pin->id);
 	sync_ref_id = zl3073x_input_pin_ref_get(sync_pin->id);
@@ -285,17 +285,20 @@ zl3073x_dpll_input_pin_ref_sync_set(const struct dpll_pin *dpll_pin,
 		if (sync_freq > 8000) {
 			NL_SET_ERR_MSG(extack,
 				       "sync frequency must be 8 kHz or less");
-			return -EINVAL;
+			rc = -EINVAL;
+			goto unlock;
 		}
 		if (ref_freq < 1000) {
 			NL_SET_ERR_MSG(extack,
 				       "clock frequency must be 1 kHz or more");
-			return -EINVAL;
+			rc = -EINVAL;
+			goto unlock;
 		}
 		if (ref_freq <= sync_freq) {
 			NL_SET_ERR_MSG(extack,
 				       "clock frequency must be higher than sync frequency");
-			return -EINVAL;
+			rc = -EINVAL;
+			goto unlock;
 		}
 
 		zl3073x_ref_sync_pair_set(&ref, sync_ref_id);
@@ -308,20 +311,39 @@ zl3073x_dpll_input_pin_ref_sync_set(const struct dpll_pin *dpll_pin,
 
 	rc = zl3073x_ref_state_set(zldev, ref_id, &ref);
 	if (rc)
-		return rc;
+		goto unlock;
 
-	/* Exclude sync source from automatic reference selection by setting
-	 * its priority to NONE. On disconnect the priority is left as NONE
-	 * and the user must explicitly make the pin selectable again.
+	/* All code paths accessing per-channel reference priorities are
+	 * serialized by the subsystem dpll_lock, so it is safe to release
+	 * our lock here before iterating over the other channels.
+	 */
+	mutex_unlock(&zldpll->lock);
+
+	/* The datasheet recommends excluding the sync source from automatic
+	 * reference selection by setting its priority to NONE on all DPLL
+	 * channels. This is advisory - the ref sync pair is already
+	 * configured, so a failure here is not fatal. On disconnect the
+	 * priority is left as NONE and the user must explicitly make the
+	 * pin selectable again.
 	 */
 	if (state == DPLL_PIN_STATE_CONNECTED) {
-		chan = *zl3073x_chan_state_get(zldev, zldpll->id);
-		zl3073x_chan_ref_prio_set(&chan, sync_ref_id,
-					  ZL_DPLL_REF_PRIO_NONE);
-		return zl3073x_chan_state_set(zldev, zldpll->id, &chan);
+		list_for_each_entry(zldpll, &zldev->dplls, list) {
+			mutex_lock(&zldpll->lock);
+			chan = *zl3073x_chan_state_get(zldev, zldpll->id);
+			zl3073x_chan_ref_prio_set(&chan, sync_ref_id,
+						  ZL_DPLL_REF_PRIO_NONE);
+			if (zl3073x_chan_state_set(zldev, zldpll->id, &chan))
+				dev_warn(zldev->dev,
+					 "Failed to set ref prio on DPLL%u\n",
+					 zldpll->id);
+			mutex_unlock(&zldpll->lock);
+		}
 	}
 
 	return 0;
+unlock:
+	mutex_unlock(&zldpll->lock);
+	return rc;
 }
 
 static int
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH net-next v2 2/2] dpll: use pin owner's dpll ref for pin-level attribute setting
  2026-08-03 12:02 [PATCH net-next v2 0/2] dpll: use pin owner's dpll ref for pin-level set callbacks Ivan Vecera
  2026-08-03 12:02 ` [PATCH net-next v2 1/2] dpll: zl3073x: update all DPLL channels on ref_sync_set Ivan Vecera
@ 2026-08-03 12:02 ` Ivan Vecera
  2026-08-06  9:41   ` Paolo Abeni
  1 sibling, 1 reply; 6+ messages in thread
From: Ivan Vecera @ 2026-08-03 12:02 UTC (permalink / raw)
  To: netdev
  Cc: Arkadiusz Kubalewski, Jakub Kicinski, Jiri Pirko, Paolo Abeni,
	Petr Oros, Prathosh Satish, Richard Cochran, Shuah Khan,
	Vadim Fedorenko, linux-kernel

Pin-level attributes (frequency, phase adjust, embedded sync, reference
sync) are properties of the pin itself, not of a particular DPLL device.
The get callbacks already use only the pin owner's DPLL reference
(via dpll_pin_own_dpll_ref_first()), but the set callbacks iterate over
all registered DPLL references and invoke the set operation on each one.

This is redundant because a pin is a single physical entity — setting
its frequency or phase adjust once through the owner's ops is sufficient.
Calling set on every registered DPLL just results in duplicate HW writes
for drivers that share a pin across multiple DPLL devices (e.g. ice
registers each input pin with both the EEC and PPS DPLL, zl3073x
registers input pins with every DPLL channel).

Simplify dpll_pin_freq_set(), dpll_pin_esync_set(),
dpll_pin_ref_sync_state_set() and dpll_pin_phase_adj_set() to call the
set callback only through the owner's DPLL reference, matching the
existing get-side behavior. This removes the xa_for_each iteration
loops, the now-unnecessary rollback logic, and several local variables.

The -EOPNOTSUPP validation loop, which checked ops support across all
owner-matching references, is replaced with a direct check on the
single owner reference returned by dpll_pin_own_dpll_ref_first().

The documentation in dpll.rst is updated to reflect that pin-level
attributes are set through the pin owner's dpll reference only.

No existing driver is affected:
  - ptp_ocp and mlx5 register each pin with a single DPLL.
  - ice registers input pins with two DPLLs (EEC and PPS) using
    identical ops and pin_priv; the set callbacks address the HW by
    pin index, not by DPLL, so the second call was a no-op.
  - zl3073x registers input pins with every DPLL channel; the set
    callbacks address HW by pin/ref ID regardless of DPLL. The
    ref_sync_set callback was the only one with per-channel behavior,
    addressed by the preceding patch.

Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
 Documentation/driver-api/dpll.rst |  10 +-
 drivers/dpll/dpll_netlink.c       | 213 +++++++-----------------------
 2 files changed, 55 insertions(+), 168 deletions(-)

diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-api/dpll.rst
index f83150917814e2..6fb50e53475c09 100644
--- a/Documentation/driver-api/dpll.rst
+++ b/Documentation/driver-api/dpll.rst
@@ -116,8 +116,8 @@ Shared pins
 A single pin object can be attached to multiple dpll devices.
 Then there are two groups of configuration knobs:
 
-1) Set on a pin - the configuration affects all dpll devices pin is
-   registered to (i.e., ``DPLL_A_PIN_FREQUENCY``),
+1) Set on a pin - the configuration is performed through the pin owner's
+   dpll reference only (i.e., ``DPLL_A_PIN_FREQUENCY``),
 2) Set on a pin-dpll tuple - the configuration affects only selected
    dpll device (i.e., ``DPLL_A_PIN_PRIO``, ``DPLL_A_PIN_STATE``,
    ``DPLL_A_PIN_DIRECTION``).
@@ -507,9 +507,9 @@ as well as parameter being configured (``DPLL_A_MODE``).
 ``DPLL_CMD_PIN_SET`` - to target a pin user must provide a
 ``DPLL_A_PIN_ID``, which is unique identifier of a pin in the system.
 Also configured pin parameters must be added.
-If ``DPLL_A_PIN_FREQUENCY`` is configured, this affects all the dpll
-devices that are connected with the pin, that is why frequency attribute
-shall not be enclosed in ``DPLL_A_PIN_PARENT_DEVICE``.
+If ``DPLL_A_PIN_FREQUENCY`` is configured, it is set through the pin
+owner's dpll reference only.  The frequency attribute shall not be
+enclosed in ``DPLL_A_PIN_PARENT_DEVICE``.
 Other attributes: ``DPLL_A_PIN_PRIO``, ``DPLL_A_PIN_STATE`` or
 ``DPLL_A_PIN_DIRECTION`` must be enclosed in
 ``DPLL_A_PIN_PARENT_DEVICE`` as their configuration relates to only one
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index afb31c0040382c..a909cd4451b008 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -1079,10 +1079,9 @@ dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
 		  struct netlink_ext_ack *extack)
 {
 	u64 freq = nla_get_u64(a), old_freq;
-	struct dpll_pin_ref *ref, *failed;
 	const struct dpll_pin_ops *ops;
+	struct dpll_pin_ref *ref;
 	struct dpll_device *dpll;
-	unsigned long i;
 	int ret;
 
 	if (!dpll_pin_is_freq_supported(pin, freq)) {
@@ -1090,22 +1089,17 @@ dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
 		return -EINVAL;
 	}
 
-	xa_for_each(&pin->dpll_refs, i, ref) {
-		ops = dpll_pin_ops(ref);
-		if ((!ops->frequency_set || !ops->frequency_get) &&
-		    ref->dpll->module == pin->module &&
-		    ref->dpll->clock_id == pin->clock_id) {
-			NL_SET_ERR_MSG(extack,
-				       "frequency set not supported by the device");
-			return -EOPNOTSUPP;
-		}
-	}
 	ref = dpll_pin_own_dpll_ref_first(pin);
 	if (!ref) {
 		NL_SET_ERR_MSG(extack, "pin owner dpll not found");
 		return -ENODEV;
 	}
 	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;
+	}
 	dpll = ref->dpll;
 	ret = ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
 				 dpll_priv(dpll), &old_freq, extack);
@@ -1116,68 +1110,42 @@ dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
 	if (freq == old_freq)
 		return 0;
 
-	xa_for_each(&pin->dpll_refs, i, ref) {
-		ops = dpll_pin_ops(ref);
-		if (!ops->frequency_set)
-			continue;
-		dpll = ref->dpll;
-		ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
-					 dpll, dpll_priv(dpll), freq, extack);
-		if (ret) {
-			failed = ref;
-			NL_SET_ERR_MSG_FMT(extack, "frequency set failed for dpll_id:%u",
-					   dpll->id);
-			goto rollback;
-		}
+	ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
+				 dpll, dpll_priv(dpll), freq, extack);
+	if (ret) {
+		NL_SET_ERR_MSG_FMT(extack,
+				   "frequency set failed for dpll_id:%u",
+				   dpll->id);
+		return ret;
 	}
 	__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);
-		if (!ops->frequency_set)
-			continue;
-		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
 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_pin_ref *ref;
 	struct dpll_device *dpll;
 	bool supported = false;
-	unsigned long i;
-	int ret;
+	int ret, i;
 
-	xa_for_each(&pin->dpll_refs, i, ref) {
-		ops = dpll_pin_ops(ref);
-		if ((!ops->esync_set || !ops->esync_get) &&
-		    ref->dpll->module == pin->module &&
-		    ref->dpll->clock_id == pin->clock_id) {
-			NL_SET_ERR_MSG(extack,
-				       "embedded sync feature is not supported by this device");
-			return -EOPNOTSUPP;
-		}
-	}
 	ref = dpll_pin_own_dpll_ref_first(pin);
 	if (!ref) {
 		NL_SET_ERR_MSG(extack, "pin owner dpll not found");
 		return -ENODEV;
 	}
 	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;
+	}
 	dpll = ref->dpll;
 	ret = ops->esync_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
 			     dpll_priv(dpll), &esync, extack);
@@ -1196,44 +1164,17 @@ dpll_pin_esync_set(struct dpll_pin *pin, struct nlattr *a,
 		return -EINVAL;
 	}
 
-	xa_for_each(&pin->dpll_refs, i, ref) {
-		void *pin_dpll_priv;
-
-		ops = dpll_pin_ops(ref);
-		if (!ops->esync_set)
-			continue;
-		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;
-		}
+	ret = ops->esync_set(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
+			     dpll_priv(dpll), freq, extack);
+	if (ret) {
+		NL_SET_ERR_MSG_FMT(extack,
+				   "embedded sync frequency set failed for dpll_id: %u",
+				   dpll->id);
+		return ret;
 	}
 	__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);
-		if (!ops->esync_set)
-			continue;
-		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
@@ -1241,14 +1182,12 @@ 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_pin_ref *ref;
 	struct dpll_device *dpll;
-	unsigned long i;
 	int ret;
 
 	ref_sync_pin = xa_find(&pin->ref_sync_pins, &ref_sync_pin_idx,
@@ -1282,42 +1221,20 @@ dpll_pin_ref_sync_state_set(struct dpll_pin *pin,
 	}
 	if (state == old_state)
 		return 0;
-	xa_for_each(&pin->dpll_refs, i, ref) {
-		ops = dpll_pin_ops(ref);
-		if (!ops->ref_sync_set)
-			continue;
-		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;
-		}
+
+	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) {
+		NL_SET_ERR_MSG_FMT(extack,
+				   "reference sync set failed for dpll_id:%u",
+				   dpll->id);
+		return ret;
 	}
 	__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);
-		if (!ops->ref_sync_set)
-			continue;
-		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
@@ -1478,11 +1395,10 @@ 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_pin_ref *ref;
 	struct dpll_device *dpll;
-	unsigned long i;
 	int ret;
 
 	phase_adj = nla_get_s32(phase_adj_attr);
@@ -1499,21 +1415,16 @@ dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr,
 		return -EINVAL;
 	}
 
-	xa_for_each(&pin->dpll_refs, i, ref) {
-		ops = dpll_pin_ops(ref);
-		if ((!ops->phase_adjust_set || !ops->phase_adjust_get) &&
-		    ref->dpll->module == pin->module &&
-		    ref->dpll->clock_id == pin->clock_id) {
-			NL_SET_ERR_MSG(extack, "phase adjust not supported");
-			return -EOPNOTSUPP;
-		}
-	}
 	ref = dpll_pin_own_dpll_ref_first(pin);
 	if (!ref) {
 		NL_SET_ERR_MSG(extack, "pin owner dpll not found");
 		return -ENODEV;
 	}
 	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;
+	}
 	dpll = ref->dpll;
 	ret = ops->phase_adjust_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
 				    dpll, dpll_priv(dpll), &old_phase_adj,
@@ -1525,41 +1436,17 @@ dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr,
 	if (phase_adj == old_phase_adj)
 		return 0;
 
-	xa_for_each(&pin->dpll_refs, i, ref) {
-		ops = dpll_pin_ops(ref);
-		if (!ops->phase_adjust_set)
-			continue;
-		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;
-		}
+	ret = ops->phase_adjust_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
+				    dpll, dpll_priv(dpll), phase_adj, extack);
+	if (ret) {
+		NL_SET_ERR_MSG_FMT(extack,
+				   "phase adjust set failed for dpll_id:%u",
+				   dpll->id);
+		return ret;
 	}
 	__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);
-		if (!ops->phase_adjust_set)
-			continue;
-		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
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH net-next v2 2/2] dpll: use pin owner's dpll ref for pin-level attribute setting
  2026-08-03 12:02 ` [PATCH net-next v2 2/2] dpll: use pin owner's dpll ref for pin-level attribute setting Ivan Vecera
@ 2026-08-06  9:41   ` Paolo Abeni
  2026-08-06 13:11     ` Ivan Vecera
  0 siblings, 1 reply; 6+ messages in thread
From: Paolo Abeni @ 2026-08-06  9:41 UTC (permalink / raw)
  To: Ivan Vecera, netdev
  Cc: Arkadiusz Kubalewski, Jakub Kicinski, Jiri Pirko, Petr Oros,
	Prathosh Satish, Richard Cochran, Shuah Khan, Vadim Fedorenko,
	linux-kernel

On 8/3/26 2:02 PM, Ivan Vecera wrote:
> Pin-level attributes (frequency, phase adjust, embedded sync, reference
> sync) are properties of the pin itself, not of a particular DPLL device.
> The get callbacks already use only the pin owner's DPLL reference
> (via dpll_pin_own_dpll_ref_first()), but the set callbacks iterate over
> all registered DPLL references and invoke the set operation on each one.
> 
> This is redundant because a pin is a single physical entity — setting
> its frequency or phase adjust once through the owner's ops is sufficient.
> Calling set on every registered DPLL just results in duplicate HW writes
> for drivers that share a pin across multiple DPLL devices (e.g. ice
> registers each input pin with both the EEC and PPS DPLL, zl3073x
> registers input pins with every DPLL channel).
> 
> Simplify dpll_pin_freq_set(), dpll_pin_esync_set(),
> dpll_pin_ref_sync_state_set() and dpll_pin_phase_adj_set() to call the
> set callback only through the owner's DPLL reference, matching the
> existing get-side behavior. This removes the xa_for_each iteration
> loops, the now-unnecessary rollback logic, and several local variables.
> 
> The -EOPNOTSUPP validation loop, which checked ops support across all
> owner-matching references, is replaced with a direct check on the
> single owner reference returned by dpll_pin_own_dpll_ref_first().
> 
> The documentation in dpll.rst is updated to reflect that pin-level
> attributes are set through the pin owner's dpll reference only.
> 
> No existing driver is affected:
>   - ptp_ocp and mlx5 register each pin with a single DPLL.
>   - ice registers input pins with two DPLLs (EEC and PPS) using
>     identical ops and pin_priv; the set callbacks address the HW by
>     pin index, not by DPLL, so the second call was a no-op.
>   - zl3073x registers input pins with every DPLL channel; the set
>     callbacks address HW by pin/ref ID regardless of DPLL. The
>     ref_sync_set callback was the only one with per-channel behavior,
>     addressed by the preceding patch.
> 
> Signed-off-by: Ivan Vecera <ivecera@redhat.com>
> ---
>  Documentation/driver-api/dpll.rst |  10 +-
>  drivers/dpll/dpll_netlink.c       | 213 +++++++-----------------------
>  2 files changed, 55 insertions(+), 168 deletions(-)
> 
> diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-api/dpll.rst
> index f83150917814e2..6fb50e53475c09 100644
> --- a/Documentation/driver-api/dpll.rst
> +++ b/Documentation/driver-api/dpll.rst
> @@ -116,8 +116,8 @@ Shared pins
>  A single pin object can be attached to multiple dpll devices.
>  Then there are two groups of configuration knobs:
>  
> -1) Set on a pin - the configuration affects all dpll devices pin is
> -   registered to (i.e., ``DPLL_A_PIN_FREQUENCY``),
> +1) Set on a pin - the configuration is performed through the pin owner's
> +   dpll reference only (i.e., ``DPLL_A_PIN_FREQUENCY``),

I find the new text confusing; it seems to me that the pin configuration
now affects a single DPLL.

Sashiko nipa has several comments, please have a look:

https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260803120245.56046-1-ivecera%40redhat.com

and also please be aware of net-next commit c82ff94592fb.

/P

/P


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net-next v2 1/2] dpll: zl3073x: update all DPLL channels on ref_sync_set
  2026-08-03 12:02 ` [PATCH net-next v2 1/2] dpll: zl3073x: update all DPLL channels on ref_sync_set Ivan Vecera
@ 2026-08-06 13:07   ` Ivan Vecera
  0 siblings, 0 replies; 6+ messages in thread
From: Ivan Vecera @ 2026-08-06 13:07 UTC (permalink / raw)
  To: netdev
  Cc: Arkadiusz Kubalewski, Jakub Kicinski, Jiri Pirko, Paolo Abeni,
	Petr Oros, Prathosh Satish, Richard Cochran, Shuah Khan,
	Vadim Fedorenko, linux-kernel

Replies to Sashiko findings:

 > Is this only a preparation change? ... Should this carry a Fixes: tag
 > so it gets picked up for stable?

It is preparation only. The old code is correct — the DPLL core's
xa_for_each loop calls ref_sync_set once per registered DPLL, and
since zl3073x registers input pins with every channel, each channel
gets its own callback invocation with the correct pin_priv. All
channels receive the priority update. The bug would only appear
after patch 2 removes the xa_for_each, which is why this patch moves
the iteration into the driver first.

 > Is the invariant in this comment accurate? ... 
zl3073x_dpll_pin_register()
 > reads them with no lock at all ... can a ref-sync DPLL_CMD_PIN_SET on
 > channel 0's pin enter the new loop and write zldev->chan[1].ref_prio
 > while channel 1's zl3073x_dpll_pin_register() is reading 
chan[1].ref_prio?

No race. dpll_lock is held across the entire dpll_pin_register()
call (acquired in dpll_core.c) and across the entire netlink
DPLL_CMD_PIN_SET handler (acquired in dpll_netlink.c). The device
startup loop calls zl3073x_dpll_register() -> dpll_pin_register()
for each channel under dpll_lock, and ref_sync_set also runs under
dpll_lock, so they cannot overlap.

 > Should the sync pin get a change notification for the other channels?

Good catch. Will add a sync_notify flag — set it when a channel's
priority is actually changed from non-NONE to NONE, and call
__dpll_pin_change_ntf(sync_pin->dpll_pin) after the loop if set.

 > Is the downgrade of this failure to a warning intentional? ...
 > The new comment calls the priority step advisory and "not fatal",
 > which reads as the opposite of the changelog statement that "the
 > priority update must cover all channels".

The ref sync pair configuration (zl3073x_ref_state_set) is the
essential operation whose failure is propagated. The per-channel
priority exclusion is a datasheet recommendation — the ref sync pair
functions regardless. Will align the changelog to say "recommended"
instead of "must". A mailbox failure here would indicate a serious
HW/bus problem that would surface through other error paths.

Thanks,
Ivan


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net-next v2 2/2] dpll: use pin owner's dpll ref for pin-level attribute setting
  2026-08-06  9:41   ` Paolo Abeni
@ 2026-08-06 13:11     ` Ivan Vecera
  0 siblings, 0 replies; 6+ messages in thread
From: Ivan Vecera @ 2026-08-06 13:11 UTC (permalink / raw)
  To: Paolo Abeni, netdev
  Cc: Arkadiusz Kubalewski, Jakub Kicinski, Jiri Pirko, Petr Oros,
	Prathosh Satish, Richard Cochran, Shuah Khan, Vadim Fedorenko,
	linux-kernel

On 8/6/26 11:41 AM, Paolo Abeni wrote:
> On 8/3/26 2:02 PM, Ivan Vecera wrote:
>> ...
>> Signed-off-by: Ivan Vecera <ivecera@redhat.com>
>> ---
>>   Documentation/driver-api/dpll.rst |  10 +-
>>   drivers/dpll/dpll_netlink.c       | 213 +++++++-----------------------
>>   2 files changed, 55 insertions(+), 168 deletions(-)
>>
>> diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-api/dpll.rst
>> index f83150917814e2..6fb50e53475c09 100644
>> --- a/Documentation/driver-api/dpll.rst
>> +++ b/Documentation/driver-api/dpll.rst
>> @@ -116,8 +116,8 @@ Shared pins
>>   A single pin object can be attached to multiple dpll devices.
>>   Then there are two groups of configuration knobs:
>>   
>> -1) Set on a pin - the configuration affects all dpll devices pin is
>> -   registered to (i.e., ``DPLL_A_PIN_FREQUENCY``),
>> +1) Set on a pin - the configuration is performed through the pin owner's
>> +   dpll reference only (i.e., ``DPLL_A_PIN_FREQUENCY``),
> 
> I find the new text confusing; it seems to me that the pin configuration
> now affects a single DPLL.
> 
> Sashiko nipa has several comments, please have a look:
> 
> https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260803120245.56046-1-ivecera%40redhat.com
> 
> and also please be aware of net-next commit c82ff94592fb.

Paolo, thanks for pointing out...

Regarding Sashiko's findings in this patch...

 > Should this list item keep describing the observable effect rather than
 > the kernel call path? ... The new text reads as if the frequency change
 > is scoped to a single dpll device, while the commit message argues the
 > opposite.

You are right. The documentation should describe the user-visible
behavior, not the implementation. The pin is one physical entity and
the frequency change is visible on every dpll the pin is registered
to. Will reword to keep the observable-effect perspective.

 > The removed sentence carried the reason why the attribute must not be
 > nested in DPLL_A_PIN_PARENT_DEVICE ... After the rewording the uAPI
 > requirement is stated without any rationale.

Good point. Will restore the rationale — the attribute is not nested
in DPLL_A_PIN_PARENT_DEVICE because it is a property of the pin
itself, not of a specific pin-dpll relationship.

 > Does the zl3073x implementation added by the preceding patch still
 > report errors here? ... the per-channel priority commit failure is
 > only warned about, and the function then falls through to return 0.

See the reply to the preceding patch. The ref sync pair configuration
(zl3073x_ref_state_set) is the essential part and its failure is
propagated. The per-channel priority exclusion is advisory per the
datasheet — a failure there does not invalidate the ref sync pair
and is reported via dev_warn. The old code propagated the error only
because it was a single-channel write that happened to be the last
statement.

Thanks,
Ivan


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-08-06 13:11 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-03 12:02 [PATCH net-next v2 0/2] dpll: use pin owner's dpll ref for pin-level set callbacks Ivan Vecera
2026-08-03 12:02 ` [PATCH net-next v2 1/2] dpll: zl3073x: update all DPLL channels on ref_sync_set Ivan Vecera
2026-08-06 13:07   ` Ivan Vecera
2026-08-03 12:02 ` [PATCH net-next v2 2/2] dpll: use pin owner's dpll ref for pin-level attribute setting Ivan Vecera
2026-08-06  9:41   ` Paolo Abeni
2026-08-06 13:11     ` Ivan Vecera

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox