netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH RFC net-next 00/13] dpll: Core improvements and ice E825-C SyncE support
@ 2025-12-11 19:47 Ivan Vecera
  2025-12-11 19:47 ` [PATCH RFC net-next 01/13] dt-bindings: net: ethernet-controller: Add DPLL pin properties Ivan Vecera
                   ` (12 more replies)
  0 siblings, 13 replies; 27+ messages in thread
From: Ivan Vecera @ 2025-12-11 19:47 UTC (permalink / raw)
  To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Jiri Pirko, Petr Oros, Michal Schmidt,
	Prathosh Satish, Tony Nguyen, Przemek Kitszel, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Richard Cochran,
	Jonathan Lemon, Simon Horman, Alexander Lobakin, Willem de Bruijn,
	Stefan Wahren, devicetree, linux-kernel, intel-wired-lan,
	linux-rdma

This series introduces Synchronous Ethernet (SyncE) support for
the Intel E825-C Ethernet controller. Unlike previous generations where
DPLL connections were implicitly assumed, the E825-C architecture relies
on the platform firmware to describe the physical connections between
the network controller and external DPLLs (such as the ZL3073x).

To accommodate this, the series extends the DPLL subsystem to support
firmware node (fwnode) associations, asynchronous discovery via notifiers,
and dynamic pin management. Additionally, a significant refactor of
the DPLL reference counting logic is included to ensure robustness and
debuggability.

DPLL Core Extensions:
* Firmware Node Support: Pins can now be registered with an associated
  struct fwnode_handle. This allows consumer drivers to lookup pins based
  on device properties (dpll-pins).
* Asynchronous Notifiers: A raw notifier chain is added to the DPLL core.
  This allows the network driver (ice driver in this series) to subscribe
  to events and react when the platform DPLL driver registers the parent
  pins, resolving probe ordering dependencies.
* Dynamic Indexing: Drivers can now request DPLL_PIN_IDX_UNSPEC to have
  the core automatically allocate a unique pin index, simplifying driver
  implementation for virtual or non-indexed pins.

Reference Counting & Debugging:
* Refactor: The reference counting logic in the core is consolidated.
  Internal list management helpers now automatically handle hold/put
  operations, removing fragile open-coded logic in the registration paths.
* Duplicate Checks: The core now strictly rejects duplicate registration
  attempts for the same pin/device context.
* Reference Tracking: A new Kconfig option DPLL_REFCNT_TRACKER is added
  (using the kernel's REF_TRACKER infrastructure). This allows developers
  to instrument and debug reference leaks by recording stack traces for
  every get/put operation.

Driver Updates:
* zl3073x: Updated to register pins with their firmware nodes and support
  the 'mux' pin type.
* ice: Implements the E825-C specific hardware configuration for SyncE
  (CGU registers). It utilizes the new notifier and fwnode APIs to
  dynamically discover and attach to the platform DPLLs.

Patch Summary:
* Patch 1-3:
  DT bindings and helper functions for finding DPLL pins via fwnode.
* Patch 4:
  Updates zl3073x to register pins with fwnode.
* Patch 5-6:
  Adds notifiers and dynamic pin index allocation to DPLL core.
* Patch 7:
  Adds 'mux' pin type support to zl3073x.
* Patch 8-9:
  Refactors DPLL core refcounting and adds duplicate registration checks.
* Patch 10-12:
  Adds REF_TRACKER infrastructure and updates drivers (zl3073x, ice) to
  support it.
* Patch 13:
  Implements the E825-C SyncE logic in the ice driver using the new
  infrastructure.

Arkadiusz Kubalewski (1):
  ice: dpll: Support E825-C SyncE and dynamic pin discovery

Ivan Vecera (11):
  dt-bindings: net: ethernet-controller: Add DPLL pin properties
  dpll: Allow registering pin with firmware node
  net: eth: Add helpers to find DPLL pin firmware node
  dpll: zl3073x: register pins with fwnode handle
  dpll: Support dynamic pin index allocation
  dpll: zl3073x: Add support for mux pin type
  dpll: Enhance and consolidate reference counting logic
  dpll: Prevent duplicate registrations
  dpll: Add reference count tracking support
  dpll: zl3073x: Enable reference count tracking
  ice: dpll: Enable reference count tracking

Petr Oros (1):
  dpll: Add notifier chain for dpll events

 .../bindings/net/ethernet-controller.yaml     |  13 +
 drivers/dpll/Kconfig                          |  15 +
 drivers/dpll/dpll_core.c                      | 290 ++++-
 drivers/dpll/dpll_core.h                      |  11 +
 drivers/dpll/dpll_netlink.c                   |   6 +
 drivers/dpll/zl3073x/dpll.c                   |  15 +-
 drivers/dpll/zl3073x/dpll.h                   |   1 +
 drivers/dpll/zl3073x/prop.c                   |   2 +
 drivers/net/ethernet/intel/ice/ice_dpll.c     | 995 ++++++++++++++++--
 drivers/net/ethernet/intel/ice/ice_dpll.h     |  33 +
 drivers/net/ethernet/intel/ice/ice_lib.c      |   3 +
 drivers/net/ethernet/intel/ice/ice_ptp.c      |  29 +
 drivers/net/ethernet/intel/ice/ice_ptp_hw.c   |   9 +-
 drivers/net/ethernet/intel/ice/ice_ptp_hw.h   |   1 +
 drivers/net/ethernet/intel/ice/ice_tspll.c    | 223 ++++
 drivers/net/ethernet/intel/ice/ice_tspll.h    |  14 +-
 drivers/net/ethernet/intel/ice/ice_type.h     |   6 +
 .../net/ethernet/mellanox/mlx5/core/dpll.c    |  13 +-
 drivers/ptp/ptp_ocp.c                         |  16 +-
 include/linux/dpll.h                          |  58 +-
 include/linux/etherdevice.h                   |   4 +
 net/ethernet/eth.c                            |  20 +
 22 files changed, 1616 insertions(+), 161 deletions(-)

-- 
2.51.2


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

* [PATCH RFC net-next 01/13] dt-bindings: net: ethernet-controller: Add DPLL pin properties
  2025-12-11 19:47 [PATCH RFC net-next 00/13] dpll: Core improvements and ice E825-C SyncE support Ivan Vecera
@ 2025-12-11 19:47 ` Ivan Vecera
  2025-12-11 19:56   ` Andrew Lunn
  2025-12-11 19:47 ` [PATCH RFC net-next 02/13] dpll: Allow registering pin with firmware node Ivan Vecera
                   ` (11 subsequent siblings)
  12 siblings, 1 reply; 27+ messages in thread
From: Ivan Vecera @ 2025-12-11 19:47 UTC (permalink / raw)
  To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Jiri Pirko, Petr Oros, Michal Schmidt,
	Prathosh Satish, Tony Nguyen, Przemek Kitszel, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Richard Cochran,
	Jonathan Lemon, Simon Horman, Alexander Lobakin, Willem de Bruijn,
	Stefan Wahren, devicetree, linux-kernel, intel-wired-lan,
	linux-rdma

Ethernet controllers may be connected to DPLL (Digital Phase Locked Loop)
pins for frequency synchronization purposes, such as in Synchronous
Ethernet (SyncE) configurations.

Add 'dpll-pins' and 'dpll-pin-names' properties to the generic
ethernet-controller schema. This allows describing the physical
connections between the Ethernet controller and the DPLL subsystem pins
in the Device Tree, enabling drivers to request and manage these
resources.

Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
 .../bindings/net/ethernet-controller.yaml           | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/ethernet-controller.yaml b/Documentation/devicetree/bindings/net/ethernet-controller.yaml
index 1bafd687dcb18..03d91f786294e 100644
--- a/Documentation/devicetree/bindings/net/ethernet-controller.yaml
+++ b/Documentation/devicetree/bindings/net/ethernet-controller.yaml
@@ -13,6 +13,19 @@ properties:
   $nodename:
     pattern: "^ethernet(@.*)?$"
 
+  dpll-pins:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    description:
+      List of phandle to a DPLL pin node of the pins that are
+      connected with this ethernet controller.
+
+  dpll-pin-names:
+    $ref: /schemas/types.yaml#/definitions/string-array
+    description:
+      List of DPLL pin name strings in the same order as the dpll-pins,
+      with one name per pin. The dpll-pin-names can be used to match and
+      get a specific DPLL pin.
+
   label:
     description: Human readable label on a port of a box.
 
-- 
2.51.2


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

* [PATCH RFC net-next 02/13] dpll: Allow registering pin with firmware node
  2025-12-11 19:47 [PATCH RFC net-next 00/13] dpll: Core improvements and ice E825-C SyncE support Ivan Vecera
  2025-12-11 19:47 ` [PATCH RFC net-next 01/13] dt-bindings: net: ethernet-controller: Add DPLL pin properties Ivan Vecera
@ 2025-12-11 19:47 ` Ivan Vecera
  2025-12-12 11:25   ` Jiri Pirko
  2025-12-11 19:47 ` [PATCH RFC net-next 03/13] net: eth: Add helpers to find DPLL pin " Ivan Vecera
                   ` (10 subsequent siblings)
  12 siblings, 1 reply; 27+ messages in thread
From: Ivan Vecera @ 2025-12-11 19:47 UTC (permalink / raw)
  To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Jiri Pirko, Petr Oros, Michal Schmidt,
	Prathosh Satish, Tony Nguyen, Przemek Kitszel, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Richard Cochran,
	Jonathan Lemon, Simon Horman, Alexander Lobakin, Willem de Bruijn,
	Stefan Wahren, devicetree, linux-kernel, intel-wired-lan,
	linux-rdma

Extend the DPLL core to support associating a DPLL pin with a firmware
node (fwnode). This association is required to allow other subsystems
(such as network drivers) to locate and request specific DPLL pins
defined in the Device Tree or ACPI.

Modify dpll_pin_get() to accept an optional fwnode_handle parameter.
If provided, the fwnode is stored in the dpll_pin structure, and its
reference count is incremented. The reference is released in dpll_pin_put().

Add fwnode_dpll_pin_find() as an exported symbol. This helper allows
drivers to search for a registered DPLL pin using its associated fwnode
handle. The caller should use dpll_pin_put() to release dpll pin
returned by fwnode_dpll_pin_find().

Update all existing callers of dpll_pin_get() in the ice, mlx5, ptp_ocp,
and zl3073x drivers to pass NULL for the new argument, preserving existing
behavior.

Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
 drivers/dpll/dpll_core.c                      | 46 +++++++++++++++++--
 drivers/dpll/dpll_core.h                      |  2 +
 drivers/dpll/zl3073x/dpll.c                   |  2 +-
 drivers/net/ethernet/intel/ice/ice_dpll.c     |  2 +-
 .../net/ethernet/mellanox/mlx5/core/dpll.c    |  3 +-
 drivers/ptp/ptp_ocp.c                         |  3 +-
 include/linux/dpll.h                          | 12 ++++-
 7 files changed, 61 insertions(+), 9 deletions(-)

diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
index a461095efd8ac..773783fd14f71 100644
--- a/drivers/dpll/dpll_core.c
+++ b/drivers/dpll/dpll_core.c
@@ -10,6 +10,7 @@
 
 #include <linux/device.h>
 #include <linux/err.h>
+#include <linux/property.h>
 #include <linux/slab.h>
 #include <linux/string.h>
 
@@ -484,7 +485,8 @@ static int dpll_pin_prop_dup(const struct dpll_pin_properties *src,
 
 static struct dpll_pin *
 dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module,
-	       const struct dpll_pin_properties *prop)
+	       const struct dpll_pin_properties *prop,
+	       struct fwnode_handle *fwnode)
 {
 	struct dpll_pin *pin;
 	int ret;
@@ -511,6 +513,7 @@ dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module,
 			      &dpll_pin_xa_id, GFP_KERNEL);
 	if (ret < 0)
 		goto err_xa_alloc;
+	pin->fwnode = fwnode_handle_get(fwnode);
 	return pin;
 err_xa_alloc:
 	xa_destroy(&pin->dpll_refs);
@@ -548,6 +551,7 @@ EXPORT_SYMBOL(dpll_netdev_pin_clear);
  * @pin_idx: idx given by dev driver
  * @module: reference to registering module
  * @prop: dpll pin properties
+ * @fwnode: optional reference to firmware node
  *
  * Get existing object of a pin (unique for given arguments) or create new
  * if doesn't exist yet.
@@ -559,7 +563,8 @@ EXPORT_SYMBOL(dpll_netdev_pin_clear);
  */
 struct dpll_pin *
 dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
-	     const struct dpll_pin_properties *prop)
+	     const struct dpll_pin_properties *prop,
+	     struct fwnode_handle *fwnode)
 {
 	struct dpll_pin *pos, *ret = NULL;
 	unsigned long i;
@@ -568,14 +573,15 @@ dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
 	xa_for_each(&dpll_pin_xa, i, pos) {
 		if (pos->clock_id == clock_id &&
 		    pos->pin_idx == pin_idx &&
-		    pos->module == module) {
+		    pos->module == module &&
+		    pos->fwnode == fwnode) {
 			ret = pos;
 			refcount_inc(&ret->refcount);
 			break;
 		}
 	}
 	if (!ret)
-		ret = dpll_pin_alloc(clock_id, pin_idx, module, prop);
+		ret = dpll_pin_alloc(clock_id, pin_idx, module, prop, fwnode);
 	mutex_unlock(&dpll_lock);
 
 	return ret;
@@ -599,12 +605,44 @@ void dpll_pin_put(struct dpll_pin *pin)
 		xa_destroy(&pin->parent_refs);
 		xa_destroy(&pin->ref_sync_pins);
 		dpll_pin_prop_free(&pin->prop);
+		fwnode_handle_put(pin->fwnode);
 		kfree_rcu(pin, rcu);
 	}
 	mutex_unlock(&dpll_lock);
 }
 EXPORT_SYMBOL_GPL(dpll_pin_put);
 
+/**
+ * fwnode_dpll_pin_find - find dpll pin by firmware node reference
+ * @fwnode: reference to firmware node
+ *
+ * Get existing object of a pin that is associated with given firmware node
+ * reference.
+ *
+ * Context: Acquires a lock (dpll_lock)
+ * Return:
+ * * valid dpll_pin struct pointer if succeeded
+ * * ERR_PTR(X) - error
+ */
+struct dpll_pin *fwnode_dpll_pin_find(struct fwnode_handle *fwnode)
+{
+	struct dpll_pin *pin, *ret = NULL;
+	unsigned long index;
+
+	mutex_lock(&dpll_lock);
+	xa_for_each(&dpll_pin_xa, index, pin) {
+		if (pin->fwnode == fwnode) {
+			ret = pin;
+			refcount_inc(&ret->refcount);
+			break;
+		}
+	}
+	mutex_unlock(&dpll_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(fwnode_dpll_pin_find);
+
 static int
 __dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
 		    const struct dpll_pin_ops *ops, void *priv, void *cookie)
diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h
index 8ce969bbeb64e..d3e17ff0ecef0 100644
--- a/drivers/dpll/dpll_core.h
+++ b/drivers/dpll/dpll_core.h
@@ -42,6 +42,7 @@ struct dpll_device {
  * @pin_idx:		index of a pin given by dev driver
  * @clock_id:		clock_id of creator
  * @module:		module of creator
+ * @fwnode:		optional reference to firmware node
  * @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
@@ -54,6 +55,7 @@ struct dpll_pin {
 	u32 pin_idx;
 	u64 clock_id;
 	struct module *module;
+	struct fwnode_handle *fwnode;
 	struct xarray dpll_refs;
 	struct xarray parent_refs;
 	struct xarray ref_sync_pins;
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
index 9879d85d29af0..cf2ad3031e5d7 100644
--- a/drivers/dpll/zl3073x/dpll.c
+++ b/drivers/dpll/zl3073x/dpll.c
@@ -1368,7 +1368,7 @@ zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index)
 
 	/* Create or get existing DPLL pin */
 	pin->dpll_pin = dpll_pin_get(zldpll->dev->clock_id, index, THIS_MODULE,
-				     &props->dpll_props);
+				     &props->dpll_props, NULL);
 	if (IS_ERR(pin->dpll_pin)) {
 		rc = PTR_ERR(pin->dpll_pin);
 		goto err_pin_get;
diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c
index 53b54e395a2ed..1c28af5a2d5e0 100644
--- a/drivers/net/ethernet/intel/ice/ice_dpll.c
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -2840,7 +2840,7 @@ ice_dpll_get_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
 
 	for (i = 0; i < count; i++) {
 		pins[i].pin = dpll_pin_get(clock_id, i + start_idx, THIS_MODULE,
-					   &pins[i].prop);
+					   &pins[i].prop, NULL);
 		if (IS_ERR(pins[i].pin)) {
 			ret = PTR_ERR(pins[i].pin);
 			goto release_pins;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/dpll.c b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c
index 1e5522a194839..9e71164f463df 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/dpll.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c
@@ -451,7 +451,8 @@ static int mlx5_dpll_probe(struct auxiliary_device *adev,
 
 	/* Multiple mdev instances might share one DPLL pin. */
 	mdpll->dpll_pin = dpll_pin_get(clock_id, mlx5_get_dev_index(mdev),
-				       THIS_MODULE, &mlx5_dpll_pin_properties);
+				       THIS_MODULE, &mlx5_dpll_pin_properties,
+				       NULL);
 	if (IS_ERR(mdpll->dpll_pin)) {
 		err = PTR_ERR(mdpll->dpll_pin);
 		goto err_unregister_dpll_device;
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index 65fe05cac8c42..fd7d1132afdf0 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -4800,7 +4800,8 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 		goto out;
 
 	for (i = 0; i < OCP_SMA_NUM; i++) {
-		bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &bp->sma[i].dpll_prop);
+		bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE,
+						   &bp->sma[i].dpll_prop, NULL);
 		if (IS_ERR(bp->sma[i].dpll_pin)) {
 			err = PTR_ERR(bp->sma[i].dpll_pin);
 			goto out_dpll;
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index 562f520b23c27..315245dbdfb96 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -16,6 +16,7 @@
 struct dpll_device;
 struct dpll_pin;
 struct dpll_pin_esync;
+struct fwnode_handle;
 
 struct dpll_device_ops {
 	int (*mode_get)(const struct dpll_device *dpll, void *dpll_priv,
@@ -173,6 +174,8 @@ void dpll_netdev_pin_clear(struct net_device *dev);
 size_t dpll_netdev_pin_handle_size(const struct net_device *dev);
 int dpll_netdev_add_pin_handle(struct sk_buff *msg,
 			       const struct net_device *dev);
+
+struct dpll_pin *fwnode_dpll_pin_find(struct fwnode_handle *fwnode);
 #else
 static inline void
 dpll_netdev_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin) { }
@@ -188,6 +191,12 @@ dpll_netdev_add_pin_handle(struct sk_buff *msg, const struct net_device *dev)
 {
 	return 0;
 }
+
+static inline struct dpll_pin *
+fwnode_dpll_pin_find(struct fwnode_handle *fwnode)
+{
+	return NULL;
+}
 #endif
 
 struct dpll_device *
@@ -203,7 +212,8 @@ void dpll_device_unregister(struct dpll_device *dpll,
 
 struct dpll_pin *
 dpll_pin_get(u64 clock_id, u32 dev_driver_id, struct module *module,
-	     const struct dpll_pin_properties *prop);
+	     const struct dpll_pin_properties *prop,
+	     struct fwnode_handle *fwnode);
 
 int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
 		      const struct dpll_pin_ops *ops, void *priv);
-- 
2.51.2


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

* [PATCH RFC net-next 03/13] net: eth: Add helpers to find DPLL pin firmware node
  2025-12-11 19:47 [PATCH RFC net-next 00/13] dpll: Core improvements and ice E825-C SyncE support Ivan Vecera
  2025-12-11 19:47 ` [PATCH RFC net-next 01/13] dt-bindings: net: ethernet-controller: Add DPLL pin properties Ivan Vecera
  2025-12-11 19:47 ` [PATCH RFC net-next 02/13] dpll: Allow registering pin with firmware node Ivan Vecera
@ 2025-12-11 19:47 ` Ivan Vecera
  2025-12-11 19:47 ` [PATCH RFC net-next 04/13] dpll: zl3073x: register pins with fwnode handle Ivan Vecera
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 27+ messages in thread
From: Ivan Vecera @ 2025-12-11 19:47 UTC (permalink / raw)
  To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Jiri Pirko, Petr Oros, Michal Schmidt,
	Prathosh Satish, Tony Nguyen, Przemek Kitszel, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Richard Cochran,
	Jonathan Lemon, Simon Horman, Alexander Lobakin, Willem de Bruijn,
	Stefan Wahren, devicetree, linux-kernel, intel-wired-lan,
	linux-rdma

Add helper functions to retrieve a DPLL pin's firmware node handle
based on the "dpll-pins" and "dpll-pin-names" device tree properties.

* fwnode_get_dpll_pin_node(): matches the given name against the
  "dpll-pin-names" property to find the correct index, then retrieves
  the reference from "dpll-pins".
* device_get_dpll_pin_node(): a wrapper around the fwnode helper for
  convenience when using a `struct device`.

These helpers simplify the process for Ethernet drivers to look up
their associated DPLL pins defined in the Device Tree, which can then
be passed to the DPLL subsystem to acquire the pin object.

Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
 include/linux/etherdevice.h |  4 ++++
 net/ethernet/eth.c          | 20 ++++++++++++++++++++
 2 files changed, 24 insertions(+)

diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h
index 9a1eacf35d370..e342e522ea0e8 100644
--- a/include/linux/etherdevice.h
+++ b/include/linux/etherdevice.h
@@ -35,6 +35,10 @@ int nvmem_get_mac_address(struct device *dev, void *addrbuf);
 int device_get_mac_address(struct device *dev, char *addr);
 int device_get_ethdev_address(struct device *dev, struct net_device *netdev);
 int fwnode_get_mac_address(struct fwnode_handle *fwnode, char *addr);
+struct fwnode_handle *fwnode_get_dpll_pin_node(struct fwnode_handle *fwnode,
+					       const char *name);
+struct fwnode_handle *device_get_dpll_pin_node(struct device *dev,
+					       const char *name);
 
 u32 eth_get_headlen(const struct net_device *dev, const void *data, u32 len);
 __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev);
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index 13a63b48b7eeb..9081dc02ba91e 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -639,3 +639,23 @@ int device_get_ethdev_address(struct device *dev, struct net_device *netdev)
 	return ret;
 }
 EXPORT_SYMBOL(device_get_ethdev_address);
+
+struct fwnode_handle *fwnode_get_dpll_pin_node(struct fwnode_handle *fwnode,
+					       const char *name)
+{
+	int index = 0;
+
+	if (name)
+		index = fwnode_property_match_string(fwnode, "dpll-pin-names",
+						     name);
+
+	return fwnode_find_reference(fwnode, "dpll-pins", index);
+}
+EXPORT_SYMBOL(fwnode_get_dpll_pin_node);
+
+struct fwnode_handle *device_get_dpll_pin_node(struct device *dev,
+					       const char *name)
+{
+	return fwnode_get_dpll_pin_node(dev_fwnode(dev), name);
+}
+EXPORT_SYMBOL(device_get_dpll_pin_node);
-- 
2.51.2


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

* [PATCH RFC net-next 04/13] dpll: zl3073x: register pins with fwnode handle
  2025-12-11 19:47 [PATCH RFC net-next 00/13] dpll: Core improvements and ice E825-C SyncE support Ivan Vecera
                   ` (2 preceding siblings ...)
  2025-12-11 19:47 ` [PATCH RFC net-next 03/13] net: eth: Add helpers to find DPLL pin " Ivan Vecera
@ 2025-12-11 19:47 ` Ivan Vecera
  2025-12-11 19:47 ` [PATCH RFC net-next 05/13] dpll: Add notifier chain for dpll events Ivan Vecera
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 27+ messages in thread
From: Ivan Vecera @ 2025-12-11 19:47 UTC (permalink / raw)
  To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Jiri Pirko, Petr Oros, Michal Schmidt,
	Prathosh Satish, Tony Nguyen, Przemek Kitszel, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Richard Cochran,
	Jonathan Lemon, Simon Horman, Alexander Lobakin, Willem de Bruijn,
	Stefan Wahren, devicetree, linux-kernel, intel-wired-lan,
	linux-rdma

Pass the firmware node handle to dpll_pin_get() when registering pins
in the zl3073x driver.

This allows the DPLL core to associate the created pin object with its
corresponding firmware node. Consequently, this enables consumer
drivers (such as network drivers) to locate and request this specific
pin using the fwnode_dpll_pin_find() helper.

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

diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
index cf2ad3031e5d7..198cc2b703e96 100644
--- a/drivers/dpll/zl3073x/dpll.c
+++ b/drivers/dpll/zl3073x/dpll.c
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/netlink.h>
 #include <linux/platform_device.h>
+#include <linux/property.h>
 #include <linux/slab.h>
 #include <linux/sprintf.h>
 
@@ -1368,7 +1369,7 @@ zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index)
 
 	/* Create or get existing DPLL pin */
 	pin->dpll_pin = dpll_pin_get(zldpll->dev->clock_id, index, THIS_MODULE,
-				     &props->dpll_props, NULL);
+				     &props->dpll_props, props->fwnode);
 	if (IS_ERR(pin->dpll_pin)) {
 		rc = PTR_ERR(pin->dpll_pin);
 		goto err_pin_get;
-- 
2.51.2


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

* [PATCH RFC net-next 05/13] dpll: Add notifier chain for dpll events
  2025-12-11 19:47 [PATCH RFC net-next 00/13] dpll: Core improvements and ice E825-C SyncE support Ivan Vecera
                   ` (3 preceding siblings ...)
  2025-12-11 19:47 ` [PATCH RFC net-next 04/13] dpll: zl3073x: register pins with fwnode handle Ivan Vecera
@ 2025-12-11 19:47 ` Ivan Vecera
  2025-12-11 19:47 ` [PATCH RFC net-next 06/13] dpll: Support dynamic pin index allocation Ivan Vecera
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 27+ messages in thread
From: Ivan Vecera @ 2025-12-11 19:47 UTC (permalink / raw)
  To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Jiri Pirko, Petr Oros, Michal Schmidt,
	Prathosh Satish, Tony Nguyen, Przemek Kitszel, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Richard Cochran,
	Jonathan Lemon, Simon Horman, Alexander Lobakin, Willem de Bruijn,
	Stefan Wahren, devicetree, linux-kernel, intel-wired-lan,
	linux-rdma

From: Petr Oros <poros@redhat.com>

Currently, the DPLL subsystem reports events (creation, deletion, changes)
to userspace via Netlink. However, there is no mechanism for other kernel
components to be notified of these events directly.

Add a raw notifier chain to the DPLL core protected by dpll_lock. This
allows other kernel subsystems or drivers to register callbacks and
receive notifications when DPLL devices or pins are created, deleted,
or modified.

Define the following:
- Registration helpers: {,un}register_dpll_notifier()
- Event types: DPLL_DEVICE_CREATED, DPLL_PIN_CREATED, etc.
- Context structures: dpll_{device,pin}_notifier_info  to pass relevant
  data to the listeners.

The notification chain is invoked alongside the existing Netlink event
generation to ensure in-kernel listeners are kept in sync with the
subsystem state.

Co-developed-by: Ivan Vecera <ivecera@redhat.com>
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
Signed-off-by: Petr Oros <poros@redhat.com>
---
 drivers/dpll/dpll_core.c    | 57 +++++++++++++++++++++++++++++++++++++
 drivers/dpll/dpll_core.h    |  4 +++
 drivers/dpll/dpll_netlink.c |  6 ++++
 include/linux/dpll.h        | 29 +++++++++++++++++++
 4 files changed, 96 insertions(+)

diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
index 773783fd14f71..fecc3d97acf5b 100644
--- a/drivers/dpll/dpll_core.c
+++ b/drivers/dpll/dpll_core.c
@@ -23,6 +23,8 @@ DEFINE_MUTEX(dpll_lock);
 DEFINE_XARRAY_FLAGS(dpll_device_xa, XA_FLAGS_ALLOC);
 DEFINE_XARRAY_FLAGS(dpll_pin_xa, XA_FLAGS_ALLOC);
 
+static RAW_NOTIFIER_HEAD(dpll_notifier_chain);
+
 static u32 dpll_device_xa_id;
 static u32 dpll_pin_xa_id;
 
@@ -46,6 +48,39 @@ struct dpll_pin_registration {
 	void *cookie;
 };
 
+static int call_dpll_notifiers(unsigned long action, void *info)
+{
+	lockdep_assert_held(&dpll_lock);
+	return raw_notifier_call_chain(&dpll_notifier_chain, action, info);
+}
+
+void dpll_device_notify(struct dpll_device *dpll, unsigned long action)
+{
+	struct dpll_device_notifier_info info = {
+		.dpll = dpll,
+		.id = dpll->id,
+		.idx = dpll->device_idx,
+		.clock_id = dpll->clock_id,
+		.type = dpll->type,
+	};
+
+	call_dpll_notifiers(action, &info);
+}
+
+void dpll_pin_notify(struct dpll_pin *pin, unsigned long action)
+{
+	struct dpll_pin_notifier_info info = {
+		.pin = pin,
+		.id = pin->id,
+		.idx = pin->pin_idx,
+		.clock_id = pin->clock_id,
+		.fwnode = pin->fwnode,
+		.prop = &pin->prop,
+	};
+
+	call_dpll_notifiers(action, &info);
+}
+
 struct dpll_device *dpll_device_get_by_id(int id)
 {
 	if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED))
@@ -545,6 +580,28 @@ void dpll_netdev_pin_clear(struct net_device *dev)
 }
 EXPORT_SYMBOL(dpll_netdev_pin_clear);
 
+int register_dpll_notifier(struct notifier_block *nb)
+{
+	int ret;
+
+	mutex_lock(&dpll_lock);
+	ret = raw_notifier_chain_register(&dpll_notifier_chain, nb);
+	mutex_unlock(&dpll_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(register_dpll_notifier);
+
+int unregister_dpll_notifier(struct notifier_block *nb)
+{
+	int ret;
+
+	mutex_lock(&dpll_lock);
+	ret = raw_notifier_chain_unregister(&dpll_notifier_chain, nb);
+	mutex_unlock(&dpll_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(unregister_dpll_notifier);
+
 /**
  * dpll_pin_get - find existing or create new dpll pin
  * @clock_id: clock_id of creator
diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h
index d3e17ff0ecef0..b7b4bb251f739 100644
--- a/drivers/dpll/dpll_core.h
+++ b/drivers/dpll/dpll_core.h
@@ -91,4 +91,8 @@ struct dpll_pin_ref *dpll_xa_ref_dpll_first(struct xarray *xa_refs);
 extern struct xarray dpll_device_xa;
 extern struct xarray dpll_pin_xa;
 extern struct mutex dpll_lock;
+
+void dpll_device_notify(struct dpll_device *dpll, unsigned long action);
+void dpll_pin_notify(struct dpll_pin *pin, unsigned long action);
+
 #endif
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index 64944f601ee5a..fe9c3d9073f0f 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -742,17 +742,20 @@ dpll_device_event_send(enum dpll_cmd event, struct dpll_device *dpll)
 
 int dpll_device_create_ntf(struct dpll_device *dpll)
 {
+	dpll_device_notify(dpll, DPLL_DEVICE_CREATED);
 	return dpll_device_event_send(DPLL_CMD_DEVICE_CREATE_NTF, dpll);
 }
 
 int dpll_device_delete_ntf(struct dpll_device *dpll)
 {
+	dpll_device_notify(dpll, DPLL_DEVICE_DELETED);
 	return dpll_device_event_send(DPLL_CMD_DEVICE_DELETE_NTF, dpll);
 }
 
 static int
 __dpll_device_change_ntf(struct dpll_device *dpll)
 {
+	dpll_device_notify(dpll, DPLL_DEVICE_CHANGED);
 	return dpll_device_event_send(DPLL_CMD_DEVICE_CHANGE_NTF, dpll);
 }
 
@@ -810,16 +813,19 @@ dpll_pin_event_send(enum dpll_cmd event, struct dpll_pin *pin)
 
 int dpll_pin_create_ntf(struct dpll_pin *pin)
 {
+	dpll_pin_notify(pin, DPLL_PIN_CREATED);
 	return dpll_pin_event_send(DPLL_CMD_PIN_CREATE_NTF, pin);
 }
 
 int dpll_pin_delete_ntf(struct dpll_pin *pin)
 {
+	dpll_pin_notify(pin, DPLL_PIN_DELETED);
 	return dpll_pin_event_send(DPLL_CMD_PIN_DELETE_NTF, pin);
 }
 
 int __dpll_pin_change_ntf(struct dpll_pin *pin)
 {
+	dpll_pin_notify(pin, DPLL_PIN_CHANGED);
 	return dpll_pin_event_send(DPLL_CMD_PIN_CHANGE_NTF, pin);
 }
 
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index 315245dbdfb96..441afb90d2a29 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -11,6 +11,7 @@
 #include <linux/device.h>
 #include <linux/netlink.h>
 #include <linux/netdevice.h>
+#include <linux/notifier.h>
 #include <linux/rtnetlink.h>
 
 struct dpll_device;
@@ -167,6 +168,30 @@ struct dpll_pin_properties {
 	u32 phase_gran;
 };
 
+#define DPLL_DEVICE_CREATED	1
+#define DPLL_DEVICE_DELETED	2
+#define DPLL_DEVICE_CHANGED	3
+#define DPLL_PIN_CREATED	4
+#define DPLL_PIN_DELETED	5
+#define DPLL_PIN_CHANGED	6
+
+struct dpll_device_notifier_info {
+	struct dpll_device *dpll;
+	u32 id;
+	u32 idx;
+	u64 clock_id;
+	enum dpll_type type;
+};
+
+struct dpll_pin_notifier_info {
+	struct dpll_pin *pin;
+	u32 id;
+	u32 idx;
+	u64 clock_id;
+	const struct fwnode_handle *fwnode;
+	const struct dpll_pin_properties *prop;
+};
+
 #if IS_ENABLED(CONFIG_DPLL)
 void dpll_netdev_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin);
 void dpll_netdev_pin_clear(struct net_device *dev);
@@ -236,4 +261,8 @@ int dpll_device_change_ntf(struct dpll_device *dpll);
 
 int dpll_pin_change_ntf(struct dpll_pin *pin);
 
+int register_dpll_notifier(struct notifier_block *nb);
+
+int unregister_dpll_notifier(struct notifier_block *nb);
+
 #endif
-- 
2.51.2


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

* [PATCH RFC net-next 06/13] dpll: Support dynamic pin index allocation
  2025-12-11 19:47 [PATCH RFC net-next 00/13] dpll: Core improvements and ice E825-C SyncE support Ivan Vecera
                   ` (4 preceding siblings ...)
  2025-12-11 19:47 ` [PATCH RFC net-next 05/13] dpll: Add notifier chain for dpll events Ivan Vecera
@ 2025-12-11 19:47 ` Ivan Vecera
  2025-12-15 14:10   ` Przemek Kitszel
  2025-12-11 19:47 ` [PATCH RFC net-next 07/13] dpll: zl3073x: Add support for mux pin type Ivan Vecera
                   ` (6 subsequent siblings)
  12 siblings, 1 reply; 27+ messages in thread
From: Ivan Vecera @ 2025-12-11 19:47 UTC (permalink / raw)
  To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Jiri Pirko, Petr Oros, Michal Schmidt,
	Prathosh Satish, Tony Nguyen, Przemek Kitszel, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Richard Cochran,
	Jonathan Lemon, Simon Horman, Alexander Lobakin, Willem de Bruijn,
	Stefan Wahren, devicetree, linux-kernel, intel-wired-lan,
	linux-rdma

Allow drivers to register DPLL pins without manually specifying a pin
index.

Currently, drivers must provide a unique pin index when calling
dpll_pin_get(). This works well for hardware-mapped pins but creates
friction for drivers handling virtual pins or those without a strict
hardware indexing scheme.

Introduce DPLL_PIN_IDX_UNSPEC (U32_MAX). When a driver passes this
value as the pin index:
1. The core allocates a unique index using an IDA
2. The allocated index is mapped to a range starting above `INT_MAX`

This separation ensures that dynamically allocated indices never collide
with standard driver-provided hardware indices, which are assumed to be
within the `0` to `INT_MAX` range. The index is automatically freed when
the pin is released in dpll_pin_put().

Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
 drivers/dpll/dpll_core.c | 48 ++++++++++++++++++++++++++++++++++++++--
 include/linux/dpll.h     |  2 ++
 2 files changed, 48 insertions(+), 2 deletions(-)

diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
index fecc3d97acf5b..79f60e0de27ac 100644
--- a/drivers/dpll/dpll_core.c
+++ b/drivers/dpll/dpll_core.c
@@ -10,6 +10,7 @@
 
 #include <linux/device.h>
 #include <linux/err.h>
+#include <linux/idr.h>
 #include <linux/property.h>
 #include <linux/slab.h>
 #include <linux/string.h>
@@ -24,6 +25,7 @@ DEFINE_XARRAY_FLAGS(dpll_device_xa, XA_FLAGS_ALLOC);
 DEFINE_XARRAY_FLAGS(dpll_pin_xa, XA_FLAGS_ALLOC);
 
 static RAW_NOTIFIER_HEAD(dpll_notifier_chain);
+static DEFINE_IDA(dpll_pin_idx_ida);
 
 static u32 dpll_device_xa_id;
 static u32 dpll_pin_xa_id;
@@ -468,6 +470,36 @@ void dpll_device_unregister(struct dpll_device *dpll,
 }
 EXPORT_SYMBOL_GPL(dpll_device_unregister);
 
+static int dpll_pin_idx_alloc(u32 *pin_idx)
+{
+	int ret;
+
+	if (!pin_idx)
+		return -EINVAL;
+
+	/* Alloc unique number from IDA. Number belongs to <0, INT_MAX> range */
+	ret = ida_alloc(&dpll_pin_idx_ida, GFP_KERNEL);
+	if (ret < 0)
+		return ret;
+
+	/* Map the value to dynamic pin index range <INT_MAX+1, U32_MAX> */
+	*pin_idx = (u32)ret + INT_MAX + 1;
+
+	return 0;
+}
+
+static void dpll_pin_idx_free(u32 pin_idx)
+{
+	if (pin_idx <= INT_MAX)
+		return; /* Not a dynamic pin index */
+
+	/* Map the index value from dynamic pin index range to IDA range and
+	 * free it.
+	 */
+	pin_idx -= INT_MAX - 1;
+	ida_free(&dpll_pin_idx_ida, pin_idx);
+}
+
 static void dpll_pin_prop_free(struct dpll_pin_properties *prop)
 {
 	kfree(prop->package_label);
@@ -526,9 +558,18 @@ dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module,
 	struct dpll_pin *pin;
 	int ret;
 
+	if (pin_idx == DPLL_PIN_IDX_UNSPEC) {
+		ret = dpll_pin_idx_alloc(&pin_idx);
+		if (ret)
+			return ERR_PTR(ret);
+	} else if (pin_idx > INT_MAX) {
+		return ERR_PTR(-EINVAL);
+	}
 	pin = kzalloc(sizeof(*pin), GFP_KERNEL);
-	if (!pin)
-		return ERR_PTR(-ENOMEM);
+	if (!pin) {
+		ret = -ENOMEM;
+		goto err_pin_alloc;
+	}
 	pin->pin_idx = pin_idx;
 	pin->clock_id = clock_id;
 	pin->module = module;
@@ -557,6 +598,8 @@ dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module,
 	dpll_pin_prop_free(&pin->prop);
 err_pin_prop:
 	kfree(pin);
+err_pin_alloc:
+	dpll_pin_idx_free(pin_idx);
 	return ERR_PTR(ret);
 }
 
@@ -663,6 +706,7 @@ void dpll_pin_put(struct dpll_pin *pin)
 		xa_destroy(&pin->ref_sync_pins);
 		dpll_pin_prop_free(&pin->prop);
 		fwnode_handle_put(pin->fwnode);
+		dpll_pin_idx_free(pin->pin_idx);
 		kfree_rcu(pin, rcu);
 	}
 	mutex_unlock(&dpll_lock);
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index 441afb90d2a29..8aa1df38ce563 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -235,6 +235,8 @@ int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
 void dpll_device_unregister(struct dpll_device *dpll,
 			    const struct dpll_device_ops *ops, void *priv);
 
+#define DPLL_PIN_IDX_UNSPEC	U32_MAX
+
 struct dpll_pin *
 dpll_pin_get(u64 clock_id, u32 dev_driver_id, struct module *module,
 	     const struct dpll_pin_properties *prop,
-- 
2.51.2


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

* [PATCH RFC net-next 07/13] dpll: zl3073x: Add support for mux pin type
  2025-12-11 19:47 [PATCH RFC net-next 00/13] dpll: Core improvements and ice E825-C SyncE support Ivan Vecera
                   ` (5 preceding siblings ...)
  2025-12-11 19:47 ` [PATCH RFC net-next 06/13] dpll: Support dynamic pin index allocation Ivan Vecera
@ 2025-12-11 19:47 ` Ivan Vecera
  2025-12-11 19:47 ` [PATCH RFC net-next 08/13] dpll: Enhance and consolidate reference counting logic Ivan Vecera
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 27+ messages in thread
From: Ivan Vecera @ 2025-12-11 19:47 UTC (permalink / raw)
  To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Jiri Pirko, Petr Oros, Michal Schmidt,
	Prathosh Satish, Tony Nguyen, Przemek Kitszel, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Richard Cochran,
	Jonathan Lemon, Simon Horman, Alexander Lobakin, Willem de Bruijn,
	Stefan Wahren, devicetree, linux-kernel, intel-wired-lan,
	linux-rdma

Add parsing for the "mux" string in the 'connection-type' pin property
mapping it to DPLL_PIN_TYPE_MUX.

Recognizing this type in the driver allows these pins to be taken as
parent pins for pin-on-pin pins coming from different modules (e.g.
network drivers).

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

diff --git a/drivers/dpll/zl3073x/prop.c b/drivers/dpll/zl3073x/prop.c
index 4ed153087570b..ad1f099cbe2b5 100644
--- a/drivers/dpll/zl3073x/prop.c
+++ b/drivers/dpll/zl3073x/prop.c
@@ -249,6 +249,8 @@ struct zl3073x_pin_props *zl3073x_pin_props_get(struct zl3073x_dev *zldev,
 			props->dpll_props.type = DPLL_PIN_TYPE_INT_OSCILLATOR;
 		else if (!strcmp(type, "synce"))
 			props->dpll_props.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
+		else if (!strcmp(type, "mux"))
+			props->dpll_props.type = DPLL_PIN_TYPE_MUX;
 		else
 			dev_warn(zldev->dev,
 				 "Unknown or unsupported pin type '%s'\n",
-- 
2.51.2


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

* [PATCH RFC net-next 08/13] dpll: Enhance and consolidate reference counting logic
  2025-12-11 19:47 [PATCH RFC net-next 00/13] dpll: Core improvements and ice E825-C SyncE support Ivan Vecera
                   ` (6 preceding siblings ...)
  2025-12-11 19:47 ` [PATCH RFC net-next 07/13] dpll: zl3073x: Add support for mux pin type Ivan Vecera
@ 2025-12-11 19:47 ` Ivan Vecera
  2025-12-11 19:47 ` [PATCH RFC net-next 09/13] dpll: Prevent duplicate registrations Ivan Vecera
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 27+ messages in thread
From: Ivan Vecera @ 2025-12-11 19:47 UTC (permalink / raw)
  To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Jiri Pirko, Petr Oros, Michal Schmidt,
	Prathosh Satish, Tony Nguyen, Przemek Kitszel, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Richard Cochran,
	Jonathan Lemon, Simon Horman, Alexander Lobakin, Willem de Bruijn,
	Stefan Wahren, devicetree, linux-kernel, intel-wired-lan,
	linux-rdma

Refactor the reference counting mechanism for DPLL devices and pins to
improve consistency and prevent potential lifetime issues.

Introduce internal helpers __dpll_{device,pin}_{hold,put}() to
centralize reference management.

Update the internal XArray reference helpers (dpll_xa_ref_*) to
automatically grab a reference to the target object when it is added to
a list, and release it when removed. This ensures that objects linked
internally (e.g., pins referenced by parent pins) are properly kept
alive without relying on the caller to manually manage the count.

Consequently, remove the now redundant manual `refcount_inc/dec` calls
in dpll_pin_on_pin_{,un}register()`, as ownership is now correctly handled
by the dpll_xa_ref_* functions.

Additionally, ensure that dpll_device_{,un}register()` takes/releases
a reference to the device, ensuring the device object remains valid for
the duration of its registration.

Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
 drivers/dpll/dpll_core.c | 74 +++++++++++++++++++++++++++-------------
 1 file changed, 50 insertions(+), 24 deletions(-)

diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
index 79f60e0de27ac..f4a4e17fb9b6c 100644
--- a/drivers/dpll/dpll_core.c
+++ b/drivers/dpll/dpll_core.c
@@ -83,6 +83,45 @@ void dpll_pin_notify(struct dpll_pin *pin, unsigned long action)
 	call_dpll_notifiers(action, &info);
 }
 
+static void __dpll_device_hold(struct dpll_device *dpll)
+{
+	refcount_inc(&dpll->refcount);
+}
+
+static void __dpll_device_put(struct dpll_device *dpll)
+{
+	if (refcount_dec_and_test(&dpll->refcount)) {
+		ASSERT_DPLL_NOT_REGISTERED(dpll);
+		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
+		xa_destroy(&dpll->pin_refs);
+		xa_erase(&dpll_device_xa, dpll->id);
+		WARN_ON(!list_empty(&dpll->registration_list));
+		kfree(dpll);
+	}
+}
+
+static void __dpll_pin_hold(struct dpll_pin *pin)
+{
+	refcount_inc(&pin->refcount);
+}
+
+static void dpll_pin_idx_free(u32 pin_idx);
+static void dpll_pin_prop_free(struct dpll_pin_properties *prop);
+
+static void __dpll_pin_put(struct dpll_pin *pin)
+{
+	if (refcount_dec_and_test(&pin->refcount)) {
+		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);
+		fwnode_handle_put(pin->fwnode);
+		dpll_pin_idx_free(pin->pin_idx);
+		kfree_rcu(pin, rcu);
+	}
+}
+
 struct dpll_device *dpll_device_get_by_id(int id)
 {
 	if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED))
@@ -154,6 +193,7 @@ dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin,
 	reg->ops = ops;
 	reg->priv = priv;
 	reg->cookie = cookie;
+	__dpll_pin_hold(pin);
 	if (ref_exists)
 		refcount_inc(&ref->refcount);
 	list_add_tail(&reg->list, &ref->registration_list);
@@ -176,6 +216,7 @@ static int dpll_xa_ref_pin_del(struct xarray *xa_pins, struct dpll_pin *pin,
 		if (WARN_ON(!reg))
 			return -EINVAL;
 		list_del(&reg->list);
+		__dpll_pin_put(pin);
 		kfree(reg);
 		if (refcount_dec_and_test(&ref->refcount)) {
 			xa_erase(xa_pins, i);
@@ -235,6 +276,7 @@ dpll_xa_ref_dpll_add(struct xarray *xa_dplls, struct dpll_device *dpll,
 	reg->ops = ops;
 	reg->priv = priv;
 	reg->cookie = cookie;
+	__dpll_device_hold(dpll);
 	if (ref_exists)
 		refcount_inc(&ref->refcount);
 	list_add_tail(&reg->list, &ref->registration_list);
@@ -257,6 +299,7 @@ dpll_xa_ref_dpll_del(struct xarray *xa_dplls, struct dpll_device *dpll,
 		if (WARN_ON(!reg))
 			return;
 		list_del(&reg->list);
+		__dpll_device_put(dpll);
 		kfree(reg);
 		if (refcount_dec_and_test(&ref->refcount)) {
 			xa_erase(xa_dplls, i);
@@ -327,8 +370,8 @@ dpll_device_get(u64 clock_id, u32 device_idx, struct module *module)
 		if (dpll->clock_id == clock_id &&
 		    dpll->device_idx == device_idx &&
 		    dpll->module == module) {
+			__dpll_device_hold(dpll);
 			ret = dpll;
-			refcount_inc(&ret->refcount);
 			break;
 		}
 	}
@@ -351,14 +394,7 @@ EXPORT_SYMBOL_GPL(dpll_device_get);
 void dpll_device_put(struct dpll_device *dpll)
 {
 	mutex_lock(&dpll_lock);
-	if (refcount_dec_and_test(&dpll->refcount)) {
-		ASSERT_DPLL_NOT_REGISTERED(dpll);
-		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
-		xa_destroy(&dpll->pin_refs);
-		xa_erase(&dpll_device_xa, dpll->id);
-		WARN_ON(!list_empty(&dpll->registration_list));
-		kfree(dpll);
-	}
+	__dpll_device_put(dpll);
 	mutex_unlock(&dpll_lock);
 }
 EXPORT_SYMBOL_GPL(dpll_device_put);
@@ -420,6 +456,7 @@ int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
 	reg->ops = ops;
 	reg->priv = priv;
 	dpll->type = type;
+	__dpll_device_hold(dpll);
 	first_registration = list_empty(&dpll->registration_list);
 	list_add_tail(&reg->list, &dpll->registration_list);
 	if (!first_registration) {
@@ -459,6 +496,7 @@ void dpll_device_unregister(struct dpll_device *dpll,
 		return;
 	}
 	list_del(&reg->list);
+	__dpll_device_put(dpll);
 	kfree(reg);
 
 	if (!list_empty(&dpll->registration_list)) {
@@ -675,8 +713,8 @@ dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
 		    pos->pin_idx == pin_idx &&
 		    pos->module == module &&
 		    pos->fwnode == fwnode) {
+			__dpll_pin_hold(pos);
 			ret = pos;
-			refcount_inc(&ret->refcount);
 			break;
 		}
 	}
@@ -699,16 +737,7 @@ EXPORT_SYMBOL_GPL(dpll_pin_get);
 void dpll_pin_put(struct dpll_pin *pin)
 {
 	mutex_lock(&dpll_lock);
-	if (refcount_dec_and_test(&pin->refcount)) {
-		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);
-		fwnode_handle_put(pin->fwnode);
-		dpll_pin_idx_free(pin->pin_idx);
-		kfree_rcu(pin, rcu);
-	}
+	__dpll_pin_put(pin);
 	mutex_unlock(&dpll_lock);
 }
 EXPORT_SYMBOL_GPL(dpll_pin_put);
@@ -733,8 +762,8 @@ struct dpll_pin *fwnode_dpll_pin_find(struct fwnode_handle *fwnode)
 	mutex_lock(&dpll_lock);
 	xa_for_each(&dpll_pin_xa, index, pin) {
 		if (pin->fwnode == fwnode) {
+			__dpll_pin_hold(pin);
 			ret = pin;
-			refcount_inc(&ret->refcount);
 			break;
 		}
 	}
@@ -886,7 +915,6 @@ int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
 	ret = dpll_xa_ref_pin_add(&pin->parent_refs, parent, ops, priv, pin);
 	if (ret)
 		goto unlock;
-	refcount_inc(&pin->refcount);
 	xa_for_each(&parent->dpll_refs, i, ref) {
 		ret = __dpll_pin_register(ref->dpll, pin, ops, priv, parent);
 		if (ret) {
@@ -906,7 +934,6 @@ int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
 					      parent);
 			dpll_pin_delete_ntf(pin);
 		}
-	refcount_dec(&pin->refcount);
 	dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv, pin);
 unlock:
 	mutex_unlock(&dpll_lock);
@@ -933,7 +960,6 @@ void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
 	mutex_lock(&dpll_lock);
 	dpll_pin_delete_ntf(pin);
 	dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv, pin);
-	refcount_dec(&pin->refcount);
 	xa_for_each(&pin->dpll_refs, i, ref)
 		__dpll_pin_unregister(ref->dpll, pin, ops, priv, parent);
 	mutex_unlock(&dpll_lock);
-- 
2.51.2


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

* [PATCH RFC net-next 09/13] dpll: Prevent duplicate registrations
  2025-12-11 19:47 [PATCH RFC net-next 00/13] dpll: Core improvements and ice E825-C SyncE support Ivan Vecera
                   ` (7 preceding siblings ...)
  2025-12-11 19:47 ` [PATCH RFC net-next 08/13] dpll: Enhance and consolidate reference counting logic Ivan Vecera
@ 2025-12-11 19:47 ` Ivan Vecera
  2025-12-11 19:47 ` [PATCH RFC net-next 10/13] dpll: Add reference count tracking support Ivan Vecera
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 27+ messages in thread
From: Ivan Vecera @ 2025-12-11 19:47 UTC (permalink / raw)
  To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Jiri Pirko, Petr Oros, Michal Schmidt,
	Prathosh Satish, Tony Nguyen, Przemek Kitszel, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Richard Cochran,
	Jonathan Lemon, Simon Horman, Alexander Lobakin, Willem de Bruijn,
	Stefan Wahren, devicetree, linux-kernel, intel-wired-lan,
	linux-rdma

Modify the internal registration helpers dpll_xa_ref_{dpll,pin}_add()
to reject duplicate registration attempts.

Previously, if a caller attempted to register the same pin multiple
times (with the same ops, priv, and cookie) on the same device, the core
silently increments the reference count and return success. This behavior
is incorrect because if the caller makes these duplicate registrations
then for the first one dpll_pin_registration is allocated and for others
the associated dpll_pin_ref.refcount is incremented. During the first
unregistration the associated dpll_pin_registration is freed and for
others WARN is fired.

Fix this by updating the logic to return `-EEXIST` if a matching
registration is found to enforce a strict "register once" policy.

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

diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
index f4a4e17fb9b6c..f9b60be8962a5 100644
--- a/drivers/dpll/dpll_core.c
+++ b/drivers/dpll/dpll_core.c
@@ -160,10 +160,8 @@ dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin,
 		if (ref->pin != pin)
 			continue;
 		reg = dpll_pin_registration_find(ref, ops, priv, cookie);
-		if (reg) {
-			refcount_inc(&ref->refcount);
-			return 0;
-		}
+		if (reg)
+			return -EEXIST;
 		ref_exists = true;
 		break;
 	}
@@ -243,10 +241,8 @@ dpll_xa_ref_dpll_add(struct xarray *xa_dplls, struct dpll_device *dpll,
 		if (ref->dpll != dpll)
 			continue;
 		reg = dpll_pin_registration_find(ref, ops, priv, cookie);
-		if (reg) {
-			refcount_inc(&ref->refcount);
-			return 0;
-		}
+		if (reg)
+			return -EEXIST;
 		ref_exists = true;
 		break;
 	}
-- 
2.51.2


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

* [PATCH RFC net-next 10/13] dpll: Add reference count tracking support
  2025-12-11 19:47 [PATCH RFC net-next 00/13] dpll: Core improvements and ice E825-C SyncE support Ivan Vecera
                   ` (8 preceding siblings ...)
  2025-12-11 19:47 ` [PATCH RFC net-next 09/13] dpll: Prevent duplicate registrations Ivan Vecera
@ 2025-12-11 19:47 ` Ivan Vecera
  2025-12-11 19:47 ` [PATCH RFC net-next 11/13] dpll: zl3073x: Enable reference count tracking Ivan Vecera
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 27+ messages in thread
From: Ivan Vecera @ 2025-12-11 19:47 UTC (permalink / raw)
  To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Jiri Pirko, Petr Oros, Michal Schmidt,
	Prathosh Satish, Tony Nguyen, Przemek Kitszel, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Richard Cochran,
	Jonathan Lemon, Simon Horman, Alexander Lobakin, Willem de Bruijn,
	Stefan Wahren, devicetree, linux-kernel, intel-wired-lan,
	linux-rdma

Add support for the REF_TRACKER infrastructure to the DPLL subsystem.

When enabled, this allows developers to track and debug reference counting
leaks or imbalances for dpll_device and dpll_pin objects. It records stack
traces for every get/put operation and exposes this information via
debugfs at:
  /sys/kernel/debug/ref_tracker/dpll_device_*
  /sys/kernel/debug/ref_tracker/dpll_pin_*

The following API changes are made to support this:
1. dpll_device_get() / dpll_device_put() now accept a 'dpll_tracker *'
   (which is a typedef to 'struct ref_tracker *' when enabled, or an empty
   struct otherwise).
2. dpll_pin_get() / dpll_pin_put() and fwnode_dpll_pin_find() similarly
   accept the tracker argument.
3. Internal registration structures now hold a tracker to associate the
   reference held by the registration with the specific owner.

All existing in-tree drivers (ice, mlx5, ptp_ocp, zl3073x) are updated
to pass NULL for the new tracker argument, maintaining current behavior
while enabling future debugging capabilities.

Co-developed-by: Petr Oros <poros@redhat.com>
Signed-off-by: Petr Oros <poros@redhat.com>
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
 drivers/dpll/Kconfig                          | 15 +++
 drivers/dpll/dpll_core.c                      | 93 ++++++++++++++-----
 drivers/dpll/dpll_core.h                      |  5 +
 drivers/dpll/zl3073x/dpll.c                   | 12 +--
 drivers/net/ethernet/intel/ice/ice_dpll.c     | 14 +--
 .../net/ethernet/mellanox/mlx5/core/dpll.c    | 12 +--
 drivers/ptp/ptp_ocp.c                         | 15 +--
 include/linux/dpll.h                          | 19 +++-
 8 files changed, 132 insertions(+), 53 deletions(-)

diff --git a/drivers/dpll/Kconfig b/drivers/dpll/Kconfig
index ade872c915ac6..df5640f526c6c 100644
--- a/drivers/dpll/Kconfig
+++ b/drivers/dpll/Kconfig
@@ -8,6 +8,21 @@ menu "DPLL device support"
 config DPLL
 	bool
 
+config DPLL_REFCNT_TRACKER
+	bool "DPLL reference count tracking"
+	depends on DPLL
+	select REF_TRACKER
+	help
+	  Enable reference count tracking for DPLL devices and pins.
+	  This helps debugging reference leaks and use-after-free bugs
+	  by recording stack traces for each get/put operation.
+
+	  The tracking information is exposed via debugfs at:
+	    /sys/kernel/debug/ref_tracker/dpll_device_*
+	    /sys/kernel/debug/ref_tracker/dpll_pin_*
+
+	  If unsure, say N.
+
 source "drivers/dpll/zl3073x/Kconfig"
 
 endmenu
diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
index f9b60be8962a5..b569a16d0fe8f 100644
--- a/drivers/dpll/dpll_core.c
+++ b/drivers/dpll/dpll_core.c
@@ -41,6 +41,7 @@ struct dpll_device_registration {
 	struct list_head list;
 	const struct dpll_device_ops *ops;
 	void *priv;
+	dpll_tracker tracker;
 };
 
 struct dpll_pin_registration {
@@ -48,6 +49,7 @@ struct dpll_pin_registration {
 	const struct dpll_pin_ops *ops;
 	void *priv;
 	void *cookie;
+	dpll_tracker tracker;
 };
 
 static int call_dpll_notifiers(unsigned long action, void *info)
@@ -83,33 +85,68 @@ void dpll_pin_notify(struct dpll_pin *pin, unsigned long action)
 	call_dpll_notifiers(action, &info);
 }
 
-static void __dpll_device_hold(struct dpll_device *dpll)
+static void dpll_device_tracker_alloc(struct dpll_device *dpll,
+				      dpll_tracker *tracker)
 {
+#ifdef CONFIG_DPLL_REFCNT_TRACKER
+	ref_tracker_alloc(&dpll->refcnt_tracker, tracker, GFP_KERNEL);
+#endif
+}
+
+static void dpll_device_tracker_free(struct dpll_device *dpll,
+				     dpll_tracker *tracker)
+{
+#ifdef CONFIG_DPLL_REFCNT_TRACKER
+	ref_tracker_free(&dpll->refcnt_tracker, tracker);
+#endif
+}
+
+static void __dpll_device_hold(struct dpll_device *dpll, dpll_tracker *tracker)
+{
+	dpll_device_tracker_alloc(dpll, tracker);
 	refcount_inc(&dpll->refcount);
 }
 
-static void __dpll_device_put(struct dpll_device *dpll)
+static void __dpll_device_put(struct dpll_device *dpll, dpll_tracker *tracker)
 {
+	dpll_device_tracker_free(dpll, tracker);
 	if (refcount_dec_and_test(&dpll->refcount)) {
 		ASSERT_DPLL_NOT_REGISTERED(dpll);
 		WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
 		xa_destroy(&dpll->pin_refs);
 		xa_erase(&dpll_device_xa, dpll->id);
 		WARN_ON(!list_empty(&dpll->registration_list));
+		ref_tracker_dir_exit(&dpll->refcnt_tracker);
 		kfree(dpll);
 	}
 }
 
-static void __dpll_pin_hold(struct dpll_pin *pin)
+static void dpll_pin_tracker_alloc(struct dpll_pin *pin, dpll_tracker *tracker)
 {
+#ifdef CONFIG_DPLL_REFCNT_TRACKER
+	ref_tracker_alloc(&pin->refcnt_tracker, tracker, GFP_KERNEL);
+#endif
+}
+
+static void dpll_pin_tracker_free(struct dpll_pin *pin, dpll_tracker *tracker)
+{
+#ifdef CONFIG_DPLL_REFCNT_TRACKER
+	ref_tracker_free(&pin->refcnt_tracker, tracker);
+#endif
+}
+
+static void __dpll_pin_hold(struct dpll_pin *pin, dpll_tracker *tracker)
+{
+	dpll_pin_tracker_alloc(pin, tracker);
 	refcount_inc(&pin->refcount);
 }
 
 static void dpll_pin_idx_free(u32 pin_idx);
 static void dpll_pin_prop_free(struct dpll_pin_properties *prop);
 
-static void __dpll_pin_put(struct dpll_pin *pin)
+static void __dpll_pin_put(struct dpll_pin *pin, dpll_tracker *tracker)
 {
+	dpll_pin_tracker_free(pin, tracker);
 	if (refcount_dec_and_test(&pin->refcount)) {
 		xa_erase(&dpll_pin_xa, pin->id);
 		xa_destroy(&pin->dpll_refs);
@@ -118,6 +155,7 @@ static void __dpll_pin_put(struct dpll_pin *pin)
 		dpll_pin_prop_free(&pin->prop);
 		fwnode_handle_put(pin->fwnode);
 		dpll_pin_idx_free(pin->pin_idx);
+		ref_tracker_dir_exit(&pin->refcnt_tracker);
 		kfree_rcu(pin, rcu);
 	}
 }
@@ -191,7 +229,7 @@ dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin,
 	reg->ops = ops;
 	reg->priv = priv;
 	reg->cookie = cookie;
-	__dpll_pin_hold(pin);
+	__dpll_pin_hold(pin, &reg->tracker);
 	if (ref_exists)
 		refcount_inc(&ref->refcount);
 	list_add_tail(&reg->list, &ref->registration_list);
@@ -214,7 +252,7 @@ static int dpll_xa_ref_pin_del(struct xarray *xa_pins, struct dpll_pin *pin,
 		if (WARN_ON(!reg))
 			return -EINVAL;
 		list_del(&reg->list);
-		__dpll_pin_put(pin);
+		__dpll_pin_put(pin, &reg->tracker);
 		kfree(reg);
 		if (refcount_dec_and_test(&ref->refcount)) {
 			xa_erase(xa_pins, i);
@@ -272,7 +310,7 @@ dpll_xa_ref_dpll_add(struct xarray *xa_dplls, struct dpll_device *dpll,
 	reg->ops = ops;
 	reg->priv = priv;
 	reg->cookie = cookie;
-	__dpll_device_hold(dpll);
+	__dpll_device_hold(dpll, &reg->tracker);
 	if (ref_exists)
 		refcount_inc(&ref->refcount);
 	list_add_tail(&reg->list, &ref->registration_list);
@@ -295,7 +333,7 @@ dpll_xa_ref_dpll_del(struct xarray *xa_dplls, struct dpll_device *dpll,
 		if (WARN_ON(!reg))
 			return;
 		list_del(&reg->list);
-		__dpll_device_put(dpll);
+		__dpll_device_put(dpll, &reg->tracker);
 		kfree(reg);
 		if (refcount_dec_and_test(&ref->refcount)) {
 			xa_erase(xa_dplls, i);
@@ -337,6 +375,7 @@ dpll_device_alloc(const u64 clock_id, u32 device_idx, struct module *module)
 		return ERR_PTR(ret);
 	}
 	xa_init_flags(&dpll->pin_refs, XA_FLAGS_ALLOC);
+	ref_tracker_dir_init(&dpll->refcnt_tracker, 128, "dpll_device");
 
 	return dpll;
 }
@@ -356,7 +395,8 @@ dpll_device_alloc(const u64 clock_id, u32 device_idx, struct module *module)
  * * ERR_PTR(X) - error
  */
 struct dpll_device *
-dpll_device_get(u64 clock_id, u32 device_idx, struct module *module)
+dpll_device_get(u64 clock_id, u32 device_idx, struct module *module,
+		dpll_tracker *tracker)
 {
 	struct dpll_device *dpll, *ret = NULL;
 	unsigned long index;
@@ -366,13 +406,17 @@ dpll_device_get(u64 clock_id, u32 device_idx, struct module *module)
 		if (dpll->clock_id == clock_id &&
 		    dpll->device_idx == device_idx &&
 		    dpll->module == module) {
-			__dpll_device_hold(dpll);
+			__dpll_device_hold(dpll, tracker);
 			ret = dpll;
 			break;
 		}
 	}
-	if (!ret)
+	if (!ret) {
 		ret = dpll_device_alloc(clock_id, device_idx, module);
+		if (!IS_ERR(ret))
+			dpll_device_tracker_alloc(ret, tracker);
+	}
+
 	mutex_unlock(&dpll_lock);
 
 	return ret;
@@ -387,10 +431,10 @@ EXPORT_SYMBOL_GPL(dpll_device_get);
  * Drop reference for a dpll device, if all references are gone, delete
  * dpll device object.
  */
-void dpll_device_put(struct dpll_device *dpll)
+void dpll_device_put(struct dpll_device *dpll, dpll_tracker *tracker)
 {
 	mutex_lock(&dpll_lock);
-	__dpll_device_put(dpll);
+	__dpll_device_put(dpll, tracker);
 	mutex_unlock(&dpll_lock);
 }
 EXPORT_SYMBOL_GPL(dpll_device_put);
@@ -452,7 +496,7 @@ int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
 	reg->ops = ops;
 	reg->priv = priv;
 	dpll->type = type;
-	__dpll_device_hold(dpll);
+	__dpll_device_hold(dpll, &reg->tracker);
 	first_registration = list_empty(&dpll->registration_list);
 	list_add_tail(&reg->list, &dpll->registration_list);
 	if (!first_registration) {
@@ -492,7 +536,7 @@ void dpll_device_unregister(struct dpll_device *dpll,
 		return;
 	}
 	list_del(&reg->list);
-	__dpll_device_put(dpll);
+	__dpll_device_put(dpll, &reg->tracker);
 	kfree(reg);
 
 	if (!list_empty(&dpll->registration_list)) {
@@ -624,6 +668,7 @@ dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module,
 	if (ret < 0)
 		goto err_xa_alloc;
 	pin->fwnode = fwnode_handle_get(fwnode);
+	ref_tracker_dir_init(&pin->refcnt_tracker, 128, "dpll_pin");
 	return pin;
 err_xa_alloc:
 	xa_destroy(&pin->dpll_refs);
@@ -698,7 +743,7 @@ EXPORT_SYMBOL_GPL(unregister_dpll_notifier);
 struct dpll_pin *
 dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
 	     const struct dpll_pin_properties *prop,
-	     struct fwnode_handle *fwnode)
+	     struct fwnode_handle *fwnode, dpll_tracker *tracker)
 {
 	struct dpll_pin *pos, *ret = NULL;
 	unsigned long i;
@@ -709,13 +754,16 @@ dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
 		    pos->pin_idx == pin_idx &&
 		    pos->module == module &&
 		    pos->fwnode == fwnode) {
-			__dpll_pin_hold(pos);
+			__dpll_pin_hold(pos, tracker);
 			ret = pos;
 			break;
 		}
 	}
-	if (!ret)
+	if (!ret) {
 		ret = dpll_pin_alloc(clock_id, pin_idx, module, prop, fwnode);
+		if (!IS_ERR(ret))
+			dpll_pin_tracker_alloc(ret, tracker);
+	}
 	mutex_unlock(&dpll_lock);
 
 	return ret;
@@ -730,10 +778,10 @@ EXPORT_SYMBOL_GPL(dpll_pin_get);
  *
  * Context: Acquires a lock (dpll_lock)
  */
-void dpll_pin_put(struct dpll_pin *pin)
+void dpll_pin_put(struct dpll_pin *pin, dpll_tracker *tracker)
 {
 	mutex_lock(&dpll_lock);
-	__dpll_pin_put(pin);
+	__dpll_pin_put(pin, tracker);
 	mutex_unlock(&dpll_lock);
 }
 EXPORT_SYMBOL_GPL(dpll_pin_put);
@@ -750,7 +798,8 @@ EXPORT_SYMBOL_GPL(dpll_pin_put);
  * * valid dpll_pin struct pointer if succeeded
  * * ERR_PTR(X) - error
  */
-struct dpll_pin *fwnode_dpll_pin_find(struct fwnode_handle *fwnode)
+struct dpll_pin *fwnode_dpll_pin_find(struct fwnode_handle *fwnode,
+				      dpll_tracker *tracker)
 {
 	struct dpll_pin *pin, *ret = NULL;
 	unsigned long index;
@@ -758,7 +807,7 @@ struct dpll_pin *fwnode_dpll_pin_find(struct fwnode_handle *fwnode)
 	mutex_lock(&dpll_lock);
 	xa_for_each(&dpll_pin_xa, index, pin) {
 		if (pin->fwnode == fwnode) {
-			__dpll_pin_hold(pin);
+			__dpll_pin_hold(pin, tracker);
 			ret = pin;
 			break;
 		}
diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h
index b7b4bb251f739..71ac88ef20172 100644
--- a/drivers/dpll/dpll_core.h
+++ b/drivers/dpll/dpll_core.h
@@ -10,6 +10,7 @@
 #include <linux/dpll.h>
 #include <linux/list.h>
 #include <linux/refcount.h>
+#include <linux/ref_tracker.h>
 #include "dpll_nl.h"
 
 #define DPLL_REGISTERED		XA_MARK_1
@@ -23,6 +24,7 @@
  * @type:		type of a dpll
  * @pin_refs:		stores pins registered within a dpll
  * @refcount:		refcount
+ * @refcnt_tracker:	ref_tracker directory for debugging reference leaks
  * @registration_list:	list of registered ops and priv data of dpll owners
  **/
 struct dpll_device {
@@ -33,6 +35,7 @@ struct dpll_device {
 	enum dpll_type type;
 	struct xarray pin_refs;
 	refcount_t refcount;
+	struct ref_tracker_dir refcnt_tracker;
 	struct list_head registration_list;
 };
 
@@ -48,6 +51,7 @@ struct dpll_device {
  * @ref_sync_pins:	hold references to pins for Reference SYNC feature
  * @prop:		pin properties copied from the registerer
  * @refcount:		refcount
+ * @refcnt_tracker:	ref_tracker directory for debugging reference leaks
  * @rcu:		rcu_head for kfree_rcu()
  **/
 struct dpll_pin {
@@ -61,6 +65,7 @@ struct dpll_pin {
 	struct xarray ref_sync_pins;
 	struct dpll_pin_properties prop;
 	refcount_t refcount;
+	struct ref_tracker_dir refcnt_tracker;
 	struct rcu_head rcu;
 };
 
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
index 198cc2b703e96..4135c621ec1aa 100644
--- a/drivers/dpll/zl3073x/dpll.c
+++ b/drivers/dpll/zl3073x/dpll.c
@@ -1369,7 +1369,7 @@ zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index)
 
 	/* Create or get existing DPLL pin */
 	pin->dpll_pin = dpll_pin_get(zldpll->dev->clock_id, index, THIS_MODULE,
-				     &props->dpll_props, props->fwnode);
+				     &props->dpll_props, props->fwnode, NULL);
 	if (IS_ERR(pin->dpll_pin)) {
 		rc = PTR_ERR(pin->dpll_pin);
 		goto err_pin_get;
@@ -1391,7 +1391,7 @@ zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index)
 	return 0;
 
 err_register:
-	dpll_pin_put(pin->dpll_pin);
+	dpll_pin_put(pin->dpll_pin, NULL);
 err_prio_get:
 	pin->dpll_pin = NULL;
 err_pin_get:
@@ -1422,7 +1422,7 @@ zl3073x_dpll_pin_unregister(struct zl3073x_dpll_pin *pin)
 	/* Unregister the pin */
 	dpll_pin_unregister(zldpll->dpll_dev, pin->dpll_pin, ops, pin);
 
-	dpll_pin_put(pin->dpll_pin);
+	dpll_pin_put(pin->dpll_pin, NULL);
 	pin->dpll_pin = NULL;
 }
 
@@ -1596,7 +1596,7 @@ zl3073x_dpll_device_register(struct zl3073x_dpll *zldpll)
 				       dpll_mode_refsel);
 
 	zldpll->dpll_dev = dpll_device_get(zldev->clock_id, zldpll->id,
-					   THIS_MODULE);
+					   THIS_MODULE, NULL);
 	if (IS_ERR(zldpll->dpll_dev)) {
 		rc = PTR_ERR(zldpll->dpll_dev);
 		zldpll->dpll_dev = NULL;
@@ -1608,7 +1608,7 @@ zl3073x_dpll_device_register(struct zl3073x_dpll *zldpll)
 				  zl3073x_prop_dpll_type_get(zldev, zldpll->id),
 				  &zl3073x_dpll_device_ops, zldpll);
 	if (rc) {
-		dpll_device_put(zldpll->dpll_dev);
+		dpll_device_put(zldpll->dpll_dev, NULL);
 		zldpll->dpll_dev = NULL;
 	}
 
@@ -1631,7 +1631,7 @@ zl3073x_dpll_device_unregister(struct zl3073x_dpll *zldpll)
 
 	dpll_device_unregister(zldpll->dpll_dev, &zl3073x_dpll_device_ops,
 			       zldpll);
-	dpll_device_put(zldpll->dpll_dev);
+	dpll_device_put(zldpll->dpll_dev, NULL);
 	zldpll->dpll_dev = NULL;
 }
 
diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c
index 1c28af5a2d5e0..39cf26987b35c 100644
--- a/drivers/net/ethernet/intel/ice/ice_dpll.c
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -2814,7 +2814,7 @@ static void ice_dpll_release_pins(struct ice_dpll_pin *pins, int count)
 	int i;
 
 	for (i = 0; i < count; i++)
-		dpll_pin_put(pins[i].pin);
+		dpll_pin_put(pins[i].pin, NULL);
 }
 
 /**
@@ -2840,7 +2840,7 @@ ice_dpll_get_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
 
 	for (i = 0; i < count; i++) {
 		pins[i].pin = dpll_pin_get(clock_id, i + start_idx, THIS_MODULE,
-					   &pins[i].prop, NULL);
+					   &pins[i].prop, NULL, NULL);
 		if (IS_ERR(pins[i].pin)) {
 			ret = PTR_ERR(pins[i].pin);
 			goto release_pins;
@@ -2851,7 +2851,7 @@ ice_dpll_get_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
 
 release_pins:
 	while (--i >= 0)
-		dpll_pin_put(pins[i].pin);
+		dpll_pin_put(pins[i].pin, NULL);
 	return ret;
 }
 
@@ -3037,7 +3037,7 @@ static void ice_dpll_deinit_rclk_pin(struct ice_pf *pf)
 	if (WARN_ON_ONCE(!vsi || !vsi->netdev))
 		return;
 	dpll_netdev_pin_clear(vsi->netdev);
-	dpll_pin_put(rclk->pin);
+	dpll_pin_put(rclk->pin, NULL);
 }
 
 /**
@@ -3247,7 +3247,7 @@ ice_dpll_deinit_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu)
 {
 	if (cgu)
 		dpll_device_unregister(d->dpll, d->ops, d);
-	dpll_device_put(d->dpll);
+	dpll_device_put(d->dpll, NULL);
 }
 
 /**
@@ -3271,7 +3271,7 @@ ice_dpll_init_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu,
 	u64 clock_id = pf->dplls.clock_id;
 	int ret;
 
-	d->dpll = dpll_device_get(clock_id, d->dpll_idx, THIS_MODULE);
+	d->dpll = dpll_device_get(clock_id, d->dpll_idx, THIS_MODULE, NULL);
 	if (IS_ERR(d->dpll)) {
 		ret = PTR_ERR(d->dpll);
 		dev_err(ice_pf_to_dev(pf),
@@ -3287,7 +3287,7 @@ ice_dpll_init_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu,
 		ice_dpll_update_state(pf, d, true);
 		ret = dpll_device_register(d->dpll, type, ops, d);
 		if (ret) {
-			dpll_device_put(d->dpll);
+			dpll_device_put(d->dpll, NULL);
 			return ret;
 		}
 		d->ops = ops;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/dpll.c b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c
index 9e71164f463df..9de520c4501d7 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/dpll.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c
@@ -438,7 +438,7 @@ static int mlx5_dpll_probe(struct auxiliary_device *adev,
 	auxiliary_set_drvdata(adev, mdpll);
 
 	/* Multiple mdev instances might share one DPLL device. */
-	mdpll->dpll = dpll_device_get(clock_id, 0, THIS_MODULE);
+	mdpll->dpll = dpll_device_get(clock_id, 0, THIS_MODULE, NULL);
 	if (IS_ERR(mdpll->dpll)) {
 		err = PTR_ERR(mdpll->dpll);
 		goto err_free_mdpll;
@@ -452,7 +452,7 @@ static int mlx5_dpll_probe(struct auxiliary_device *adev,
 	/* Multiple mdev instances might share one DPLL pin. */
 	mdpll->dpll_pin = dpll_pin_get(clock_id, mlx5_get_dev_index(mdev),
 				       THIS_MODULE, &mlx5_dpll_pin_properties,
-				       NULL);
+				       NULL, NULL);
 	if (IS_ERR(mdpll->dpll_pin)) {
 		err = PTR_ERR(mdpll->dpll_pin);
 		goto err_unregister_dpll_device;
@@ -480,11 +480,11 @@ static int mlx5_dpll_probe(struct auxiliary_device *adev,
 	dpll_pin_unregister(mdpll->dpll, mdpll->dpll_pin,
 			    &mlx5_dpll_pins_ops, mdpll);
 err_put_dpll_pin:
-	dpll_pin_put(mdpll->dpll_pin);
+	dpll_pin_put(mdpll->dpll_pin, NULL);
 err_unregister_dpll_device:
 	dpll_device_unregister(mdpll->dpll, &mlx5_dpll_device_ops, mdpll);
 err_put_dpll_device:
-	dpll_device_put(mdpll->dpll);
+	dpll_device_put(mdpll->dpll, NULL);
 err_free_mdpll:
 	kfree(mdpll);
 	return err;
@@ -500,9 +500,9 @@ static void mlx5_dpll_remove(struct auxiliary_device *adev)
 	destroy_workqueue(mdpll->wq);
 	dpll_pin_unregister(mdpll->dpll, mdpll->dpll_pin,
 			    &mlx5_dpll_pins_ops, mdpll);
-	dpll_pin_put(mdpll->dpll_pin);
+	dpll_pin_put(mdpll->dpll_pin, NULL);
 	dpll_device_unregister(mdpll->dpll, &mlx5_dpll_device_ops, mdpll);
-	dpll_device_put(mdpll->dpll);
+	dpll_device_put(mdpll->dpll, NULL);
 	kfree(mdpll);
 
 	mlx5_dpll_synce_status_set(mdev,
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index fd7d1132afdf0..65b743a53e63d 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -4788,7 +4788,7 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	devlink_register(devlink);
 
 	clkid = pci_get_dsn(pdev);
-	bp->dpll = dpll_device_get(clkid, 0, THIS_MODULE);
+	bp->dpll = dpll_device_get(clkid, 0, THIS_MODULE, NULL);
 	if (IS_ERR(bp->dpll)) {
 		err = PTR_ERR(bp->dpll);
 		dev_err(&pdev->dev, "dpll_device_alloc failed\n");
@@ -4801,7 +4801,8 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
 	for (i = 0; i < OCP_SMA_NUM; i++) {
 		bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE,
-						   &bp->sma[i].dpll_prop, NULL);
+						   &bp->sma[i].dpll_prop, NULL,
+						   NULL);
 		if (IS_ERR(bp->sma[i].dpll_pin)) {
 			err = PTR_ERR(bp->sma[i].dpll_pin);
 			goto out_dpll;
@@ -4810,7 +4811,7 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 		err = dpll_pin_register(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops,
 					&bp->sma[i]);
 		if (err) {
-			dpll_pin_put(bp->sma[i].dpll_pin);
+			dpll_pin_put(bp->sma[i].dpll_pin, NULL);
 			goto out_dpll;
 		}
 	}
@@ -4820,9 +4821,9 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 out_dpll:
 	while (i--) {
 		dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, &bp->sma[i]);
-		dpll_pin_put(bp->sma[i].dpll_pin);
+		dpll_pin_put(bp->sma[i].dpll_pin, NULL);
 	}
-	dpll_device_put(bp->dpll);
+	dpll_device_put(bp->dpll, NULL);
 out:
 	ptp_ocp_detach(bp);
 out_disable:
@@ -4843,11 +4844,11 @@ ptp_ocp_remove(struct pci_dev *pdev)
 	for (i = 0; i < OCP_SMA_NUM; i++) {
 		if (bp->sma[i].dpll_pin) {
 			dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, &bp->sma[i]);
-			dpll_pin_put(bp->sma[i].dpll_pin);
+			dpll_pin_put(bp->sma[i].dpll_pin, NULL);
 		}
 	}
 	dpll_device_unregister(bp->dpll, &dpll_ops, bp);
-	dpll_device_put(bp->dpll);
+	dpll_device_put(bp->dpll, NULL);
 	devlink_unregister(devlink);
 	ptp_ocp_detach(bp);
 	pci_disable_device(pdev);
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index 8aa1df38ce563..05b4141e17dbf 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -18,6 +18,7 @@ struct dpll_device;
 struct dpll_pin;
 struct dpll_pin_esync;
 struct fwnode_handle;
+struct ref_tracker;
 
 struct dpll_device_ops {
 	int (*mode_get)(const struct dpll_device *dpll, void *dpll_priv,
@@ -168,6 +169,12 @@ struct dpll_pin_properties {
 	u32 phase_gran;
 };
 
+#ifdef CONFIG_DPLL_REFCNT_TRACKER
+typedef struct ref_tracker *dpll_tracker;
+#else
+typedef struct {} dpll_tracker;
+#endif
+
 #define DPLL_DEVICE_CREATED	1
 #define DPLL_DEVICE_DELETED	2
 #define DPLL_DEVICE_CHANGED	3
@@ -200,7 +207,8 @@ size_t dpll_netdev_pin_handle_size(const struct net_device *dev);
 int dpll_netdev_add_pin_handle(struct sk_buff *msg,
 			       const struct net_device *dev);
 
-struct dpll_pin *fwnode_dpll_pin_find(struct fwnode_handle *fwnode);
+struct dpll_pin *fwnode_dpll_pin_find(struct fwnode_handle *fwnode,
+				      dpll_tracker *tracker);
 #else
 static inline void
 dpll_netdev_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin) { }
@@ -225,9 +233,10 @@ fwnode_dpll_pin_find(struct fwnode_handle *fwnode)
 #endif
 
 struct dpll_device *
-dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module);
+dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module,
+		dpll_tracker *tracker);
 
-void dpll_device_put(struct dpll_device *dpll);
+void dpll_device_put(struct dpll_device *dpll, dpll_tracker *tracker);
 
 int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
 			 const struct dpll_device_ops *ops, void *priv);
@@ -240,7 +249,7 @@ void dpll_device_unregister(struct dpll_device *dpll,
 struct dpll_pin *
 dpll_pin_get(u64 clock_id, u32 dev_driver_id, struct module *module,
 	     const struct dpll_pin_properties *prop,
-	     struct fwnode_handle *fwnode);
+	     struct fwnode_handle *fwnode, dpll_tracker *tracker);
 
 int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
 		      const struct dpll_pin_ops *ops, void *priv);
@@ -248,7 +257,7 @@ int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
 void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
 			 const struct dpll_pin_ops *ops, void *priv);
 
-void dpll_pin_put(struct dpll_pin *pin);
+void dpll_pin_put(struct dpll_pin *pin, dpll_tracker *tracker);
 
 int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
 			     const struct dpll_pin_ops *ops, void *priv);
-- 
2.51.2


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

* [PATCH RFC net-next 11/13] dpll: zl3073x: Enable reference count tracking
  2025-12-11 19:47 [PATCH RFC net-next 00/13] dpll: Core improvements and ice E825-C SyncE support Ivan Vecera
                   ` (9 preceding siblings ...)
  2025-12-11 19:47 ` [PATCH RFC net-next 10/13] dpll: Add reference count tracking support Ivan Vecera
@ 2025-12-11 19:47 ` Ivan Vecera
  2025-12-12 11:11   ` Jiri Pirko
  2025-12-11 19:47 ` [PATCH RFC net-next 12/13] ice: dpll: " Ivan Vecera
  2025-12-11 19:47 ` [PATCH RFC net-next 13/13] ice: dpll: Support E825-C SyncE and dynamic pin discovery Ivan Vecera
  12 siblings, 1 reply; 27+ messages in thread
From: Ivan Vecera @ 2025-12-11 19:47 UTC (permalink / raw)
  To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Jiri Pirko, Petr Oros, Michal Schmidt,
	Prathosh Satish, Tony Nguyen, Przemek Kitszel, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Richard Cochran,
	Jonathan Lemon, Simon Horman, Alexander Lobakin, Willem de Bruijn,
	Stefan Wahren, devicetree, linux-kernel, intel-wired-lan,
	linux-rdma

Update the zl3073x driver to utilize the DPLL reference count tracking
infrastructure.

Add dpll_tracker fields to the driver's internal device and pin
structures. Pass pointers to these trackers when calling
dpll_device_get/put() and dpll_pin_get/put().

This allows a developer to inspect the specific references held by this
driver via debugfs when CONFIG_DPLL_REFCNT_TRACKER is enabled, aiding
in the debugging of resource leaks.

Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
 drivers/dpll/zl3073x/dpll.c | 14 ++++++++------
 drivers/dpll/zl3073x/dpll.h |  1 +
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
index 4135c621ec1aa..a14ac90ecbd66 100644
--- a/drivers/dpll/zl3073x/dpll.c
+++ b/drivers/dpll/zl3073x/dpll.c
@@ -45,6 +45,7 @@ struct zl3073x_dpll_pin {
 	struct list_head	list;
 	struct zl3073x_dpll	*dpll;
 	struct dpll_pin		*dpll_pin;
+	dpll_tracker		tracker;
 	char			label[8];
 	enum dpll_pin_direction	dir;
 	u8			id;
@@ -1369,7 +1370,8 @@ zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index)
 
 	/* Create or get existing DPLL pin */
 	pin->dpll_pin = dpll_pin_get(zldpll->dev->clock_id, index, THIS_MODULE,
-				     &props->dpll_props, props->fwnode, NULL);
+				     &props->dpll_props, props->fwnode,
+				     &pin->tracker);
 	if (IS_ERR(pin->dpll_pin)) {
 		rc = PTR_ERR(pin->dpll_pin);
 		goto err_pin_get;
@@ -1391,7 +1393,7 @@ zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index)
 	return 0;
 
 err_register:
-	dpll_pin_put(pin->dpll_pin, NULL);
+	dpll_pin_put(pin->dpll_pin, &pin->tracker);
 err_prio_get:
 	pin->dpll_pin = NULL;
 err_pin_get:
@@ -1422,7 +1424,7 @@ zl3073x_dpll_pin_unregister(struct zl3073x_dpll_pin *pin)
 	/* Unregister the pin */
 	dpll_pin_unregister(zldpll->dpll_dev, pin->dpll_pin, ops, pin);
 
-	dpll_pin_put(pin->dpll_pin, NULL);
+	dpll_pin_put(pin->dpll_pin, &pin->tracker);
 	pin->dpll_pin = NULL;
 }
 
@@ -1596,7 +1598,7 @@ zl3073x_dpll_device_register(struct zl3073x_dpll *zldpll)
 				       dpll_mode_refsel);
 
 	zldpll->dpll_dev = dpll_device_get(zldev->clock_id, zldpll->id,
-					   THIS_MODULE, NULL);
+					   THIS_MODULE, &zldpll->tracker);
 	if (IS_ERR(zldpll->dpll_dev)) {
 		rc = PTR_ERR(zldpll->dpll_dev);
 		zldpll->dpll_dev = NULL;
@@ -1608,7 +1610,7 @@ zl3073x_dpll_device_register(struct zl3073x_dpll *zldpll)
 				  zl3073x_prop_dpll_type_get(zldev, zldpll->id),
 				  &zl3073x_dpll_device_ops, zldpll);
 	if (rc) {
-		dpll_device_put(zldpll->dpll_dev, NULL);
+		dpll_device_put(zldpll->dpll_dev, &zldpll->tracker);
 		zldpll->dpll_dev = NULL;
 	}
 
@@ -1631,7 +1633,7 @@ zl3073x_dpll_device_unregister(struct zl3073x_dpll *zldpll)
 
 	dpll_device_unregister(zldpll->dpll_dev, &zl3073x_dpll_device_ops,
 			       zldpll);
-	dpll_device_put(zldpll->dpll_dev, NULL);
+	dpll_device_put(zldpll->dpll_dev, &zldpll->tracker);
 	zldpll->dpll_dev = NULL;
 }
 
diff --git a/drivers/dpll/zl3073x/dpll.h b/drivers/dpll/zl3073x/dpll.h
index e8c39b44b356c..9b2183ca07e47 100644
--- a/drivers/dpll/zl3073x/dpll.h
+++ b/drivers/dpll/zl3073x/dpll.h
@@ -31,6 +31,7 @@ struct zl3073x_dpll {
 	u8				check_count;
 	bool				phase_monitor;
 	struct dpll_device		*dpll_dev;
+	dpll_tracker			tracker;
 	enum dpll_lock_status		lock_status;
 	struct list_head		pins;
 	struct work_struct		change_work;
-- 
2.51.2


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

* [PATCH RFC net-next 12/13] ice: dpll: Enable reference count tracking
  2025-12-11 19:47 [PATCH RFC net-next 00/13] dpll: Core improvements and ice E825-C SyncE support Ivan Vecera
                   ` (10 preceding siblings ...)
  2025-12-11 19:47 ` [PATCH RFC net-next 11/13] dpll: zl3073x: Enable reference count tracking Ivan Vecera
@ 2025-12-11 19:47 ` Ivan Vecera
  2025-12-11 19:47 ` [PATCH RFC net-next 13/13] ice: dpll: Support E825-C SyncE and dynamic pin discovery Ivan Vecera
  12 siblings, 0 replies; 27+ messages in thread
From: Ivan Vecera @ 2025-12-11 19:47 UTC (permalink / raw)
  To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Jiri Pirko, Petr Oros, Michal Schmidt,
	Prathosh Satish, Tony Nguyen, Przemek Kitszel, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Richard Cochran,
	Jonathan Lemon, Simon Horman, Alexander Lobakin, Willem de Bruijn,
	Stefan Wahren, devicetree, linux-kernel, intel-wired-lan,
	linux-rdma

Update the Intel ICE driver to utilize the DPLL reference count tracking
infrastructure.

Add .dpll_tracker fields to the internal struct ice_dpll and struct
ice_dpll_pin structures.

Pass pointers to these trackers to the dpll_device_get/put() and
dpll_pin_get/put() functions, replacing the temporary NULL values.

This enables developers to trace reference ownership for the ICE driver's
DPLL resources via the ref_tracker debugfs interface when
CONFIG_DPLL_REFCNT_TRACKER is enabled.

Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
 drivers/net/ethernet/intel/ice/ice_dpll.c | 16 +++++++++-------
 drivers/net/ethernet/intel/ice/ice_dpll.h |  4 ++++
 2 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c
index 39cf26987b35c..ff1b597051e8f 100644
--- a/drivers/net/ethernet/intel/ice/ice_dpll.c
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -2814,7 +2814,7 @@ static void ice_dpll_release_pins(struct ice_dpll_pin *pins, int count)
 	int i;
 
 	for (i = 0; i < count; i++)
-		dpll_pin_put(pins[i].pin, NULL);
+		dpll_pin_put(pins[i].pin, &pins[i].tracker);
 }
 
 /**
@@ -2840,7 +2840,8 @@ ice_dpll_get_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
 
 	for (i = 0; i < count; i++) {
 		pins[i].pin = dpll_pin_get(clock_id, i + start_idx, THIS_MODULE,
-					   &pins[i].prop, NULL, NULL);
+					   &pins[i].prop, NULL,
+					   &pins[i].tracker);
 		if (IS_ERR(pins[i].pin)) {
 			ret = PTR_ERR(pins[i].pin);
 			goto release_pins;
@@ -2851,7 +2852,7 @@ ice_dpll_get_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
 
 release_pins:
 	while (--i >= 0)
-		dpll_pin_put(pins[i].pin, NULL);
+		dpll_pin_put(pins[i].pin, &pins[i].tracker);
 	return ret;
 }
 
@@ -3037,7 +3038,7 @@ static void ice_dpll_deinit_rclk_pin(struct ice_pf *pf)
 	if (WARN_ON_ONCE(!vsi || !vsi->netdev))
 		return;
 	dpll_netdev_pin_clear(vsi->netdev);
-	dpll_pin_put(rclk->pin, NULL);
+	dpll_pin_put(rclk->pin, &rclk->tracker);
 }
 
 /**
@@ -3247,7 +3248,7 @@ ice_dpll_deinit_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu)
 {
 	if (cgu)
 		dpll_device_unregister(d->dpll, d->ops, d);
-	dpll_device_put(d->dpll, NULL);
+	dpll_device_put(d->dpll, &d->tracker);
 }
 
 /**
@@ -3271,7 +3272,8 @@ ice_dpll_init_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu,
 	u64 clock_id = pf->dplls.clock_id;
 	int ret;
 
-	d->dpll = dpll_device_get(clock_id, d->dpll_idx, THIS_MODULE, NULL);
+	d->dpll = dpll_device_get(clock_id, d->dpll_idx, THIS_MODULE,
+				  &d->tracker);
 	if (IS_ERR(d->dpll)) {
 		ret = PTR_ERR(d->dpll);
 		dev_err(ice_pf_to_dev(pf),
@@ -3287,7 +3289,7 @@ ice_dpll_init_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu,
 		ice_dpll_update_state(pf, d, true);
 		ret = dpll_device_register(d->dpll, type, ops, d);
 		if (ret) {
-			dpll_device_put(d->dpll, NULL);
+			dpll_device_put(d->dpll, &d->tracker);
 			return ret;
 		}
 		d->ops = ops;
diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.h b/drivers/net/ethernet/intel/ice/ice_dpll.h
index c0da03384ce91..63fac6510df6e 100644
--- a/drivers/net/ethernet/intel/ice/ice_dpll.h
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
@@ -23,6 +23,7 @@ enum ice_dpll_pin_sw {
 /** ice_dpll_pin - store info about pins
  * @pin: dpll pin structure
  * @pf: pointer to pf, which has registered the dpll_pin
+ * @tracker: reference count tracker
  * @idx: ice pin private idx
  * @num_parents: hols number of parent pins
  * @parent_idx: hold indexes of parent pins
@@ -37,6 +38,7 @@ enum ice_dpll_pin_sw {
 struct ice_dpll_pin {
 	struct dpll_pin *pin;
 	struct ice_pf *pf;
+	dpll_tracker tracker;
 	u8 idx;
 	u8 num_parents;
 	u8 parent_idx[ICE_DPLL_RCLK_NUM_MAX];
@@ -58,6 +60,7 @@ struct ice_dpll_pin {
 /** ice_dpll - store info required for DPLL control
  * @dpll: pointer to dpll dev
  * @pf: pointer to pf, which has registered the dpll_device
+ * @tracker: reference count tracker
  * @dpll_idx: index of dpll on the NIC
  * @input_idx: currently selected input index
  * @prev_input_idx: previously selected input index
@@ -76,6 +79,7 @@ struct ice_dpll_pin {
 struct ice_dpll {
 	struct dpll_device *dpll;
 	struct ice_pf *pf;
+	dpll_tracker tracker;
 	u8 dpll_idx;
 	u8 input_idx;
 	u8 prev_input_idx;
-- 
2.51.2


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

* [PATCH RFC net-next 13/13] ice: dpll: Support E825-C SyncE and dynamic pin discovery
  2025-12-11 19:47 [PATCH RFC net-next 00/13] dpll: Core improvements and ice E825-C SyncE support Ivan Vecera
                   ` (11 preceding siblings ...)
  2025-12-11 19:47 ` [PATCH RFC net-next 12/13] ice: dpll: " Ivan Vecera
@ 2025-12-11 19:47 ` Ivan Vecera
  2025-12-12 10:20   ` [Intel-wired-lan] " Loktionov, Aleksandr
  12 siblings, 1 reply; 27+ messages in thread
From: Ivan Vecera @ 2025-12-11 19:47 UTC (permalink / raw)
  To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Jiri Pirko, Petr Oros, Michal Schmidt,
	Prathosh Satish, Tony Nguyen, Przemek Kitszel, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Richard Cochran,
	Jonathan Lemon, Simon Horman, Alexander Lobakin, Willem de Bruijn,
	Stefan Wahren, devicetree, linux-kernel, intel-wired-lan,
	linux-rdma

From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

Add DPLL support for the Intel E825-C Ethernet controller. Unlike previous
generations (E810), the E825-C connects to the platform's DPLL subsystem
via MUX pins defined in the system firmware (Device Tree/ACPI).

Implement the following mechanisms to support this architecture:

1. Dynamic Pin Discovery: Use the fwnode_dpll_pin_find() helper to
   locate the parent MUX pins defined in the firmware.

2. Asynchronous Registration: Since the platform DPLL driver may probe
   independently of the network driver, utilize the DPLL notifier chain
   (register_dpll_notifier). The driver listens for DPLL_PIN_CREATED
   events to detect when the parent MUX pins become available, then
   registers its own Recovered Clock (RCLK) and PTP (1588) pins as children
   of those parents.

3. Hardware Configuration: Implement the specific register access logic
   for E825-C CGU (Clock Generation Unit) registers (R10, R11). This
   includes configuring the bypass MUXes and clock dividers required to
   drive SyncE and PTP signals.

4. Split Initialization: Refactor `ice_dpll_init()` to separate the
   static initialization path of E810 from the dynamic, firmware-driven
   path required for E825-C.

Co-developed-by: Ivan Vecera <ivecera@redhat.com>
Co-developed-by: Grzegorz Nitka <grzegorz.nitka@intel.com>
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
Signed-off-by: Grzegorz Nitka <grzegorz.nitka@intel.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
---
 drivers/net/ethernet/intel/ice/ice_dpll.c   | 964 ++++++++++++++++++--
 drivers/net/ethernet/intel/ice/ice_dpll.h   |  29 +
 drivers/net/ethernet/intel/ice/ice_lib.c    |   3 +
 drivers/net/ethernet/intel/ice/ice_ptp.c    |  29 +
 drivers/net/ethernet/intel/ice/ice_ptp_hw.c |   9 +-
 drivers/net/ethernet/intel/ice/ice_ptp_hw.h |   1 +
 drivers/net/ethernet/intel/ice/ice_tspll.c  | 223 +++++
 drivers/net/ethernet/intel/ice/ice_tspll.h  |  14 +-
 drivers/net/ethernet/intel/ice/ice_type.h   |   6 +
 9 files changed, 1188 insertions(+), 90 deletions(-)

diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c
index ff1b597051e8f..2b4a22d669cb6 100644
--- a/drivers/net/ethernet/intel/ice/ice_dpll.c
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -5,6 +5,7 @@
 #include "ice_lib.h"
 #include "ice_trace.h"
 #include <linux/dpll.h>
+#include <linux/property.h>
 
 #define ICE_CGU_STATE_ACQ_ERR_THRESHOLD		50
 #define ICE_DPLL_PIN_IDX_INVALID		0xff
@@ -74,6 +75,7 @@ static const char * const pin_type_name[] = {
 
 static const char * const ice_dpll_sw_pin_sma[] = { "SMA1", "SMA2" };
 static const char * const ice_dpll_sw_pin_ufl[] = { "U.FL1", "U.FL2" };
+static const char ice_dpll_pin_1588[] = "pin_1588";
 
 static const struct dpll_pin_frequency ice_esync_range[] = {
 	DPLL_PIN_FREQUENCY_RANGE(0, DPLL_PIN_FREQUENCY_1_HZ),
@@ -528,6 +530,130 @@ ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
 	return ret;
 }
 
+/**
+ * ice_dpll_pin_store_state - updates the state of pin in SW bookkeeping
+ * @pin: pointer to a pin
+ * @parent: parent pin index
+ * @state: pin state (connected or disconnected)
+ */
+static inline void
+ice_dpll_pin_store_state(struct ice_dpll_pin *pin, int parent, bool state)
+{
+	pin->state[parent] = state ? DPLL_PIN_STATE_CONNECTED :
+					DPLL_PIN_STATE_DISCONNECTED;
+}
+
+/**
+ * ice_dpll_rclk_update_e825c - updates the state of rclk pin on e825c device
+ * @pf: private board struct
+ * @pin: pointer to a pin
+ *
+ * Update struct holding pin states info, states are separate for each parent
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+static int ice_dpll_rclk_update_e825c(struct ice_pf *pf,
+				      struct ice_dpll_pin *pin)
+{
+	u8 rclk_bits;
+	int err;
+	u32 reg;
+
+	if (pf->dplls.rclk.num_parents > ICE_SYNCE_CLK_NUM)
+		return -EINVAL;
+
+	err = ice_read_cgu_reg(&pf->hw, ICE_CGU_R10, &reg);
+	if (err)
+		return err;
+	rclk_bits = FIELD_GET(ICE_CGU_R10_SYNCE_S_REF_CLK, reg);
+	ice_dpll_pin_store_state(pin, ICE_SYNCE_CLK0, rclk_bits ==
+		(pf->ptp.port.port_num + ICE_CGU_BYPASS_MUX_OFFSET_E825C));
+
+	err = ice_read_cgu_reg(&pf->hw, ICE_CGU_R11, &reg);
+	if (err)
+		return err;
+	rclk_bits = FIELD_GET(ICE_CGU_R11_SYNCE_S_BYP_CLK, reg);
+	ice_dpll_pin_store_state(pin, ICE_SYNCE_CLK1, rclk_bits ==
+		(pf->ptp.port.port_num + ICE_CGU_BYPASS_MUX_OFFSET_E825C));
+	return 0;
+}
+
+/**
+ * ice_dpll_rclk_update - updates the state of rclk pin on a device
+ * @pf: private board struct
+ * @pin: pointer to a pin
+ * @port_num: port number
+ *
+ * Update struct holding pin states info, states are separate for each parent
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+static int ice_dpll_rclk_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
+				u8 port_num)
+{
+	int ret;
+
+	for (u8 parent = 0; parent < pf->dplls.rclk.num_parents; parent++) {
+		ret = ice_aq_get_phy_rec_clk_out(&pf->hw, &parent, &port_num,
+						 &pin->flags[parent], NULL);
+		if (ret)
+			return ret;
+		ice_dpll_pin_store_state(pin, parent,
+					 ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN &
+					 pin->flags[parent]);
+	}
+
+	return 0;
+}
+
+/**
+ * ice_dpll_update_pin_1588_e825c - updates the state of clock 1588 pin
+ * @hw: board private hw structure
+ * @pin: pointer to a pin
+ * @parent: clock source identifier
+ *
+ * Update struct holding pin states info, states are separate for each parent
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+static int ice_dpll_update_pin_1588_e825c(struct ice_hw *hw,
+					  struct ice_dpll_pin *pin,
+					  enum ice_synce_clk parent)
+{
+	u8 bits_clk;
+	int err;
+	u32 reg;
+
+	switch (parent) {
+	case ICE_SYNCE_CLK0:
+		err = ice_read_cgu_reg(hw, ICE_CGU_R10, &reg);
+		if (err)
+			return err;
+		bits_clk = FIELD_GET(ICE_CGU_R10_SYNCE_S_REF_CLK, reg);
+		break;
+	case ICE_SYNCE_CLK1:
+		err = ice_read_cgu_reg(hw, ICE_CGU_R11, &reg);
+		if (err)
+			return err;
+		bits_clk = FIELD_GET(ICE_CGU_R11_SYNCE_S_BYP_CLK, reg);
+		break;
+	default:
+		return -EINVAL;
+	}
+	ice_dpll_pin_store_state(pin, parent, bits_clk == ICE_CGU_NCOCLK);
+
+	return 0;
+}
+
 /**
  * ice_dpll_sw_pins_update - update status of all SW pins
  * @pf: private board struct
@@ -668,22 +794,14 @@ ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
 		}
 		break;
 	case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
-		for (parent = 0; parent < pf->dplls.rclk.num_parents;
-		     parent++) {
-			u8 p = parent;
-
-			ret = ice_aq_get_phy_rec_clk_out(&pf->hw, &p,
-							 &port_num,
-							 &pin->flags[parent],
-							 NULL);
+		if (pf->hw.mac_type == ICE_MAC_GENERIC_3K_E825) {
+			ret = ice_dpll_rclk_update_e825c(pf, pin);
+			if (ret)
+				goto err;
+		} else {
+			ret = ice_dpll_rclk_update(pf, pin, port_num);
 			if (ret)
 				goto err;
-			if (ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN &
-			    pin->flags[parent])
-				pin->state[parent] = DPLL_PIN_STATE_CONNECTED;
-			else
-				pin->state[parent] =
-					DPLL_PIN_STATE_DISCONNECTED;
 		}
 		break;
 	case ICE_DPLL_PIN_TYPE_SOFTWARE:
@@ -1842,6 +1960,40 @@ ice_dpll_phase_offset_get(const struct dpll_pin *pin, void *pin_priv,
 	return 0;
 }
 
+/**
+ * ice_dpll_synce_update_e825c - setting PHY recovered clock pins on e825c
+ * @hw: Pointer to the HW struct
+ * @ena: true if enable, false in disable
+ * @port_num: port number
+ * @output: output pin, we have two in E825C
+ *
+ * DPLL subsystem callback. Set proper signals to recover clock from port.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+static int ice_dpll_synce_update_e825c(struct ice_hw *hw, bool ena,
+				       u32 port_num, enum ice_synce_clk output)
+{
+	int err;
+
+	/* configure the mux to deliver proper signal to DPLL from the MUX */
+	err = ice_tspll_cfg_bypass_mux_e825c(hw, ena, port_num, output, false);
+	if (err)
+		return err;
+
+	err = ice_tspll_cfg_synce_ethdiv_e825c(hw, output);
+	if (err)
+		return err;
+
+	dev_dbg(ice_hw_to_dev(hw), "CLK_SYNCE%u recovered clock: pin %s\n",
+		output, str_enabled_disabled(ena));
+
+	return 0;
+}
+
 /**
  * ice_dpll_output_esync_set - callback for setting embedded sync
  * @pin: pointer to a pin
@@ -2263,6 +2415,29 @@ ice_dpll_sw_input_ref_sync_get(const struct dpll_pin *pin, void *pin_priv,
 					   state, extack);
 }
 
+static int
+ice_dpll_pin_get_parent_num(struct ice_dpll_pin *pin,
+			    const struct dpll_pin *parent)
+{
+	int i;
+
+	for (i = 0; pin->num_parents; i++)
+		if (pin->pf->dplls.inputs[pin->parent_idx[i]].pin == parent)
+			return i;
+
+	return -ENOENT;
+}
+
+static int
+ice_dpll_pin_get_parent_idx(struct ice_dpll_pin *pin,
+			    const struct dpll_pin *parent)
+{
+	int num = ice_dpll_pin_get_parent_num(pin, parent);
+
+	return num < 0 ? num : pin->parent_idx[num];
+}
+
+
 /**
  * ice_dpll_rclk_state_on_pin_set - set a state on rclk pin
  * @pin: pointer to a pin
@@ -2286,35 +2461,97 @@ ice_dpll_rclk_state_on_pin_set(const struct dpll_pin *pin, void *pin_priv,
 			       enum dpll_pin_state state,
 			       struct netlink_ext_ack *extack)
 {
-	struct ice_dpll_pin *p = pin_priv, *parent = parent_pin_priv;
 	bool enable = state == DPLL_PIN_STATE_CONNECTED;
+	struct ice_dpll_pin *p = pin_priv;
 	struct ice_pf *pf = p->pf;
+	struct ice_hw *hw;
 	int ret = -EINVAL;
-	u32 hw_idx;
+	int hw_idx;
+
+	hw = &pf->hw;
 
 	if (ice_dpll_is_reset(pf, extack))
 		return -EBUSY;
 
 	mutex_lock(&pf->dplls.lock);
-	hw_idx = parent->idx - pf->dplls.base_rclk_idx;
-	if (hw_idx >= pf->dplls.num_inputs)
+	hw_idx = ice_dpll_pin_get_parent_idx(p, parent_pin);
+	if (hw_idx < 0)
 		goto unlock;
 
 	if ((enable && p->state[hw_idx] == DPLL_PIN_STATE_CONNECTED) ||
 	    (!enable && p->state[hw_idx] == DPLL_PIN_STATE_DISCONNECTED)) {
 		NL_SET_ERR_MSG_FMT(extack,
 				   "pin:%u state:%u on parent:%u already set",
-				   p->idx, state, parent->idx);
+				   p->idx, state,
+				   ice_dpll_pin_get_parent_num(p, parent_pin));
 		goto unlock;
 	}
-	ret = ice_aq_set_phy_rec_clk_out(&pf->hw, hw_idx, enable,
-					 &p->freq);
+
+	ret = hw->mac_type == ICE_MAC_GENERIC_3K_E825 ?
+		ice_dpll_synce_update_e825c(hw, enable,
+					    pf->ptp.port.port_num,
+					    (enum ice_synce_clk)hw_idx) :
+		ice_aq_set_phy_rec_clk_out(hw, hw_idx, enable, &p->freq);
 	if (ret)
 		NL_SET_ERR_MSG_FMT(extack,
 				   "err:%d %s failed to set pin state:%u for pin:%u on parent:%u",
 				   ret,
-				   libie_aq_str(pf->hw.adminq.sq_last_status),
-				   state, p->idx, parent->idx);
+				   libie_aq_str(hw->adminq.sq_last_status),
+				   state, p->idx,
+				   ice_dpll_pin_get_parent_num(p, parent_pin));
+unlock:
+	mutex_unlock(&pf->dplls.lock);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_pin_1588_state_on_pin_set - set a state on a clock 1588 pin
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @parent_pin: pin parent pointer
+ * @parent_pin_priv: parent private data pointer passed on pin registration
+ * @state: state to be set on pin
+ * @extack: error reporting
+ *
+ * DPLL subsystem callback. Set a state of a clock 1588 pin on a parent pin
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_pin_1588_state_on_pin_set(const struct dpll_pin *pin,
+					      void *pin_priv,
+					      const struct dpll_pin *parent_pin,
+					      void *parent_pin_priv,
+					      enum dpll_pin_state state,
+					      struct netlink_ext_ack *extack)
+{
+	bool ena = state == DPLL_PIN_STATE_CONNECTED;
+	struct ice_dpll_pin *p = pin_priv;
+	struct ice_pf *pf = p->pf;
+	int ret = -EINVAL;
+	int hw_idx;
+
+	if (ice_dpll_is_reset(pf, extack))
+		return -EBUSY;
+
+	mutex_lock(&pf->dplls.lock);
+	hw_idx = ice_dpll_pin_get_parent_idx(p, parent_pin);
+	if (hw_idx < 0)
+		goto unlock;
+
+	if ((ena && p->state[hw_idx] == DPLL_PIN_STATE_CONNECTED) ||
+	    (!ena && p->state[hw_idx] == DPLL_PIN_STATE_DISCONNECTED)) {
+		NL_SET_ERR_MSG_FMT(extack,
+				   "Pin %d state on parent is already set",
+				   ice_dpll_pin_get_parent_num(p, parent_pin));
+		goto unlock;
+	}
+	ret = ice_tspll_cfg_bypass_mux_e825c(&pf->hw, ena,
+					     pf->ptp.port.port_num,
+					     hw_idx, true);
 unlock:
 	mutex_unlock(&pf->dplls.lock);
 
@@ -2344,17 +2581,17 @@ ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin, void *pin_priv,
 			       enum dpll_pin_state *state,
 			       struct netlink_ext_ack *extack)
 {
-	struct ice_dpll_pin *p = pin_priv, *parent = parent_pin_priv;
+	struct ice_dpll_pin *p = pin_priv;
 	struct ice_pf *pf = p->pf;
 	int ret = -EINVAL;
-	u32 hw_idx;
+	int hw_idx;
 
 	if (ice_dpll_is_reset(pf, extack))
 		return -EBUSY;
 
 	mutex_lock(&pf->dplls.lock);
-	hw_idx = parent->idx - pf->dplls.base_rclk_idx;
-	if (hw_idx >= pf->dplls.num_inputs)
+	hw_idx = ice_dpll_pin_get_parent_idx(p, parent_pin);
+	if (hw_idx < 0)
 		goto unlock;
 
 	ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_RCLK_INPUT,
@@ -2370,12 +2607,65 @@ ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin, void *pin_priv,
 	return ret;
 }
 
+/**
+ * ice_dpll_pin_1588_state_on_pin_get - get a state of a 1588 clock pin
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @parent_pin: pin parent pointer
+ * @parent_pin_priv: pin parent priv data pointer passed on pin registration
+ * @state: on success holds pin state on parent pin
+ * @extack: error reporting
+ *
+ * DPLL subsystem callback, get a state of a 1588 clock pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_pin_1588_state_on_pin_get(const struct dpll_pin *pin, void *pin_priv,
+				   const struct dpll_pin *parent_pin,
+				   void *parent_pin_priv,
+				   enum dpll_pin_state *state,
+				   struct netlink_ext_ack *extack)
+{
+	struct ice_dpll_pin *p = pin_priv;
+	struct ice_pf *pf = p->pf;
+	int ret = -EINVAL;
+	int hw_idx;
+
+	if (ice_dpll_is_reset(pf, extack))
+		return -EBUSY;
+
+	mutex_lock(&pf->dplls.lock);
+	hw_idx = ice_dpll_pin_get_parent_idx(p, parent_pin);
+	if (hw_idx < 0)
+		goto unlock;
+
+	ret = ice_dpll_update_pin_1588_e825c(&pf->hw, p,
+					     (enum ice_synce_clk)hw_idx);
+	if (ret)
+		goto unlock;
+	*state = p->state[hw_idx];
+unlock:
+	mutex_unlock(&pf->dplls.lock);
+
+	return ret;
+}
+
 static const struct dpll_pin_ops ice_dpll_rclk_ops = {
 	.state_on_pin_set = ice_dpll_rclk_state_on_pin_set,
 	.state_on_pin_get = ice_dpll_rclk_state_on_pin_get,
 	.direction_get = ice_dpll_input_direction,
 };
 
+static const struct dpll_pin_ops ice_dpll_pin_1588_ops = {
+	.direction_get = ice_dpll_input_direction,
+	.state_on_pin_get = ice_dpll_pin_1588_state_on_pin_get,
+	.state_on_pin_set = ice_dpll_pin_1588_state_on_pin_set,
+};
+
 static const struct dpll_pin_ops ice_dpll_pin_sma_ops = {
 	.state_on_dpll_set = ice_dpll_sma_pin_state_set,
 	.state_on_dpll_get = ice_dpll_sw_pin_state_get,
@@ -2814,7 +3104,8 @@ static void ice_dpll_release_pins(struct ice_dpll_pin *pins, int count)
 	int i;
 
 	for (i = 0; i < count; i++)
-		dpll_pin_put(pins[i].pin, &pins[i].tracker);
+		if (!IS_ERR_OR_NULL(pins[i].pin))
+			dpll_pin_put(pins[i].pin, &pins[i].tracker);
 }
 
 /**
@@ -2836,10 +3127,14 @@ static int
 ice_dpll_get_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
 		  int start_idx, int count, u64 clock_id)
 {
+	u32 pin_index;
 	int i, ret;
 
 	for (i = 0; i < count; i++) {
-		pins[i].pin = dpll_pin_get(clock_id, i + start_idx, THIS_MODULE,
+		pin_index = start_idx;
+		if (start_idx != DPLL_PIN_IDX_UNSPEC)
+			pin_index += i;
+		pins[i].pin = dpll_pin_get(clock_id, pin_index, THIS_MODULE,
 					   &pins[i].prop, NULL,
 					   &pins[i].tracker);
 		if (IS_ERR(pins[i].pin)) {
@@ -2945,6 +3240,7 @@ ice_dpll_register_pins(struct dpll_device *dpll, struct ice_dpll_pin *pins,
 
 /**
  * ice_dpll_deinit_direct_pins - deinitialize direct pins
+ * @pf: board private structure
  * @cgu: if cgu is present and controlled by this NIC
  * @pins: pointer to pins array
  * @count: number of pins
@@ -2956,7 +3252,8 @@ ice_dpll_register_pins(struct dpll_device *dpll, struct ice_dpll_pin *pins,
  * Release pins resources to the dpll subsystem.
  */
 static void
-ice_dpll_deinit_direct_pins(bool cgu, struct ice_dpll_pin *pins, int count,
+ice_dpll_deinit_direct_pins(struct ice_pf *pf, bool cgu,
+			    struct ice_dpll_pin *pins, int count,
 			    const struct dpll_pin_ops *ops,
 			    struct dpll_device *first,
 			    struct dpll_device *second)
@@ -3015,6 +3312,30 @@ ice_dpll_init_direct_pins(struct ice_pf *pf, bool cgu,
 	return ret;
 }
 
+/**
+ * ice_dpll_deinit_pin_1588 - release 1588 pin resources
+ * @pf: board private structure
+ *
+ * Deregister 1588 pin from parent pins and release resources in DPLL
+ * subsystem.
+ */
+static void ice_dpll_deinit_pin_1588(struct ice_pf *pf)
+{
+	struct ice_dpll_pin *pin_1588 = &pf->dplls.pin_1588;
+	struct ice_dpll_pin *parent;
+	int i;
+
+	for (i = 0; i < pin_1588->num_parents; i++) {
+		parent = &pf->dplls.inputs[pin_1588->parent_idx[i]];
+		if (IS_ERR_OR_NULL(parent->pin))
+			continue;
+		dpll_pin_on_pin_unregister(parent->pin, pin_1588->pin,
+					   &ice_dpll_pin_1588_ops,
+					   pin_1588);
+	}
+
+	dpll_pin_put(pin_1588->pin, &pin_1588->tracker);
+}
 /**
  * ice_dpll_deinit_rclk_pin - release rclk pin resources
  * @pf: board private structure
@@ -3025,14 +3346,18 @@ static void ice_dpll_deinit_rclk_pin(struct ice_pf *pf)
 {
 	struct ice_dpll_pin *rclk = &pf->dplls.rclk;
 	struct ice_vsi *vsi = ice_get_main_vsi(pf);
-	struct dpll_pin *parent;
+	struct ice_dpll_pin *parent;
 	int i;
 
+	if (pf->hw.mac_type == ICE_MAC_GENERIC_3K_E825 &&
+	    ice_pf_src_tmr_owned(pf))
+		ice_dpll_deinit_pin_1588(pf);
+
 	for (i = 0; i < rclk->num_parents; i++) {
-		parent = pf->dplls.inputs[rclk->parent_idx[i]].pin;
-		if (!parent)
+		parent = &pf->dplls.inputs[rclk->parent_idx[i]];
+		if (IS_ERR_OR_NULL(parent->pin))
 			continue;
-		dpll_pin_on_pin_unregister(parent, rclk->pin,
+		dpll_pin_on_pin_unregister(parent->pin, rclk->pin,
 					   &ice_dpll_rclk_ops, rclk);
 	}
 	if (WARN_ON_ONCE(!vsi || !vsi->netdev))
@@ -3041,60 +3366,262 @@ static void ice_dpll_deinit_rclk_pin(struct ice_pf *pf)
 	dpll_pin_put(rclk->pin, &rclk->tracker);
 }
 
+static bool ice_dpll_is_fwnode_pin(struct ice_dpll_pin *pin)
+{
+	return !IS_ERR_OR_NULL(pin->fwnode);
+}
+
+static void ice_dpll_pin_notify_work(struct work_struct *work)
+{
+	struct ice_dpll_pin_work *w = container_of(work,
+						   struct ice_dpll_pin_work,
+						   work);
+	struct ice_dpll_pin *pin, *parent = w->pin;
+	struct ice_pf *pf = parent->pf;
+	int ret;
+
+	switch (w->action) {
+	case DPLL_PIN_CREATED:
+		if (!IS_ERR_OR_NULL(parent->pin)) {
+			/* We have already our pin registered */
+			goto out;
+		}
+
+		/* Grab reference on fwnode pin */
+		parent->pin = fwnode_dpll_pin_find(parent->fwnode,
+						   &parent->tracker);
+		if (IS_ERR_OR_NULL(parent->pin)) {
+			dev_err(ice_pf_to_dev(pf),
+				"Cannot get fwnode pin reference\n");
+			goto out;
+		}
+
+		/* Register 1588 pin */
+		if (ice_pf_src_tmr_owned(pf)) {
+			pin = &pf->dplls.pin_1588;
+			ret = dpll_pin_on_pin_register(parent->pin, pin->pin,
+						       &ice_dpll_pin_1588_ops,
+						       pin);
+			if (ret) {
+				dev_err(ice_pf_to_dev(pf),
+					"Failed to register pin: %pe\n",
+					ERR_PTR(ret));
+				dpll_pin_put(parent->pin, &parent->tracker);
+				parent->pin = NULL;
+				goto out;
+			}
+		}
+
+		/* Register rclk pin */
+		pin = &pf->dplls.rclk;
+		ret = dpll_pin_on_pin_register(parent->pin, pin->pin,
+					       &ice_dpll_rclk_ops, pin);
+		if (ret) {
+			dev_err(ice_pf_to_dev(pf),
+				"Failed to register pin: %pe\n", ERR_PTR(ret));
+			dpll_pin_on_pin_unregister(parent->pin,
+						   pf->dplls.pin_1588.pin,
+						   &ice_dpll_pin_1588_ops,
+						   &pf->dplls.pin_1588);
+			dpll_pin_put(parent->pin, &parent->tracker);
+			parent->pin = NULL;
+			goto out;
+		}
+		break;
+	case DPLL_PIN_DELETED:
+		if (IS_ERR_OR_NULL(parent->pin)) {
+			/* We have already our pin unregistered */
+			goto out;
+		}
+
+		/* Unregister 1588 pin */
+		if (ice_pf_src_tmr_owned(pf)) {
+			pin = &pf->dplls.pin_1588;
+
+			dpll_pin_on_pin_unregister(parent->pin, pin->pin,
+						   &ice_dpll_pin_1588_ops, pin);
+		}
+
+		/* Register rclk pin */
+		pin = &pf->dplls.rclk;
+                dpll_pin_on_pin_unregister(parent->pin, pin->pin,
+					   &ice_dpll_rclk_ops, pin);
+
+		/* Drop fwnode pin reference */
+		dpll_pin_put(parent->pin, &parent->tracker);
+		parent->pin = NULL;
+		break;
+	default:
+		break;
+	}
+out:
+	kfree(work);
+}
+
+static int ice_dpll_pin_notify(struct notifier_block *nb, unsigned long action,
+			       void *data)
+{
+	struct ice_dpll_pin *pin = container_of(nb, struct ice_dpll_pin, nb);
+	struct dpll_pin_notifier_info *info = data;
+	struct ice_dpll_pin_work *work;
+
+	if (action != DPLL_PIN_CREATED && action != DPLL_PIN_DELETED)
+		return NOTIFY_DONE;
+
+	/* Check if the reported pin is this one */
+	if (pin->fwnode != info->fwnode)
+		return NOTIFY_DONE; /* Not this pin */
+
+	work = kzalloc(sizeof(*work), GFP_KERNEL);
+	if (!work)
+		return NOTIFY_DONE;
+
+	INIT_WORK(&work->work, ice_dpll_pin_notify_work);
+	work->action = action;
+	work->pin = pin;
+
+	queue_work(pin->pf->dplls.wq, &work->work);
+
+	return NOTIFY_OK;
+}
+
 /**
- * ice_dpll_init_rclk_pins - initialize recovered clock pin
+ * ice_dpll_init_pin_common - initialize pin
  * @pf: board private structure
  * @pin: pin to register
  * @start_idx: on which index shall allocation start in dpll subsystem
  * @ops: callback ops registered with the pins
  *
- * Allocate resource for recovered clock pin in dpll subsystem. Register the
- * pin with the parents it has in the info. Register pin with the pf's main vsi
- * netdev.
+ * Allocate resource for given pin in dpll subsystem. Register the pin with
+ * the parents it has in the info.
  *
  * Return:
  * * 0 - success
  * * negative - registration failure reason
  */
 static int
-ice_dpll_init_rclk_pins(struct ice_pf *pf, struct ice_dpll_pin *pin,
-			int start_idx, const struct dpll_pin_ops *ops)
+ice_dpll_init_pin_common(struct ice_pf *pf, struct ice_dpll_pin *pin,
+			 int start_idx, const struct dpll_pin_ops *ops)
 {
 	struct ice_vsi *vsi = ice_get_main_vsi(pf);
-	struct dpll_pin *parent;
+	struct ice_dpll_pin *parent;
 	int ret, i;
 
 	if (WARN_ON((!vsi || !vsi->netdev)))
 		return -EINVAL;
-	ret = ice_dpll_get_pins(pf, pin, start_idx, ICE_DPLL_RCLK_NUM_PER_PF,
-				pf->dplls.clock_id);
+
+	ret = ice_dpll_get_pins(pf, pin, start_idx, 1, pf->dplls.clock_id);
 	if (ret)
 		return ret;
-	for (i = 0; i < pf->dplls.rclk.num_parents; i++) {
-		parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[i]].pin;
-		if (!parent) {
-			ret = -ENODEV;
-			goto unregister_pins;
+
+	for (i = 0; i < pin->num_parents; i++) {
+		parent = &pf->dplls.inputs[pin->parent_idx[i]];
+		if (IS_ERR_OR_NULL(parent->pin)) {
+			if (!ice_dpll_is_fwnode_pin(parent)) {
+				ret = -ENODEV;
+				goto unregister_pins;
+			}
+			parent->pin = fwnode_dpll_pin_find(parent->fwnode,
+							   &parent->tracker);
+			if (IS_ERR_OR_NULL(parent->pin)) {
+				dev_info(ice_pf_to_dev(pf),
+					 "Mux pin not registered yet\n");
+				continue;
+			}
 		}
-		ret = dpll_pin_on_pin_register(parent, pf->dplls.rclk.pin,
-					       ops, &pf->dplls.rclk);
+		ret = dpll_pin_on_pin_register(parent->pin, pin->pin, ops, pin);
 		if (ret)
 			goto unregister_pins;
 	}
-	dpll_netdev_pin_set(vsi->netdev, pf->dplls.rclk.pin);
 
 	return 0;
 
 unregister_pins:
 	while (i) {
-		parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[--i]].pin;
-		dpll_pin_on_pin_unregister(parent, pf->dplls.rclk.pin,
-					   &ice_dpll_rclk_ops, &pf->dplls.rclk);
+		parent = &pf->dplls.inputs[pin->parent_idx[--i]];
+		if (IS_ERR_OR_NULL(parent->pin))
+			continue;
+		dpll_pin_on_pin_unregister(parent->pin, pin->pin, ops, pin);
 	}
-	ice_dpll_release_pins(pin, ICE_DPLL_RCLK_NUM_PER_PF);
+	ice_dpll_release_pins(pin, 1);
+
 	return ret;
 }
 
+/**
+ * ice_dpll_init_rclk_pin - initialize recovered clock pin
+ * @pf: board private structure
+ * @start_idx: on which index shall allocation start in dpll subsystem
+ * @ops: callback ops registered with the pins
+ *
+ * Allocate resource for recovered clock pin in dpll subsystem. Register the
+ * pin with the parents it has in the info.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - registration failure reason
+ */
+static int
+ice_dpll_init_rclk_pin(struct ice_pf *pf, int start_idx,
+		       const struct dpll_pin_ops *ops)
+{
+	struct ice_vsi *vsi = ice_get_main_vsi(pf);
+	int ret;
+
+	ret = ice_dpll_init_pin_common(pf, &pf->dplls.rclk, start_idx, ops);
+	if (ret)
+		return ret;
+
+	dpll_netdev_pin_set(vsi->netdev, pf->dplls.rclk.pin);
+
+	return 0;
+}
+
+/**
+ * ice_dpll_init_pin_1588 - initialize pin to control clock 1588
+ * @pf: board private structure
+ * @start_idx: on which index shall allocation start in dpll subsystem
+ * @ops: callback ops registered with the pins
+ *
+ * Allocate resource for clock 1588 pin in DPLL subsystem. Register the
+ * pin with the parents it has in the info.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - registration failure reason
+ */
+static int
+ice_dpll_init_pin_1588(struct ice_pf *pf, int start_idx,
+		       const struct dpll_pin_ops *ops)
+{
+	return ice_dpll_init_pin_common(pf, &pf->dplls.pin_1588, start_idx,
+					ops);
+}
+
+static void
+ice_dpll_deinit_fwnode_pin(struct ice_dpll_pin *pin)
+{
+	unregister_dpll_notifier(&pin->nb);
+	flush_workqueue(pin->pf->dplls.wq);
+	if (!IS_ERR_OR_NULL(pin->pin)) {
+		dpll_pin_put(pin->pin, &pin->tracker);
+		pin->pin = NULL;
+	}
+	fwnode_handle_put(pin->fwnode);
+	pin->fwnode = NULL;
+}
+
+static void
+ice_dpll_deinit_fwnode_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
+			    int start_idx)
+{
+	int i;
+
+	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
+		ice_dpll_deinit_fwnode_pin(&pins[start_idx + i]);
+	destroy_workqueue(pf->dplls.wq);
+}
+
 /**
  * ice_dpll_deinit_pins - deinitialize direct pins
  * @pf: board private structure
@@ -3114,6 +3641,8 @@ static void ice_dpll_deinit_pins(struct ice_pf *pf, bool cgu)
 	struct ice_dpll *dp = &d->pps;
 
 	ice_dpll_deinit_rclk_pin(pf);
+	if (pf->hw.mac_type == ICE_MAC_GENERIC_3K_E825)
+		ice_dpll_deinit_fwnode_pins(pf, pf->dplls.inputs, 0);
 	if (cgu) {
 		ice_dpll_unregister_pins(dp->dpll, inputs, &ice_dpll_input_ops,
 					 num_inputs);
@@ -3128,12 +3657,12 @@ static void ice_dpll_deinit_pins(struct ice_pf *pf, bool cgu)
 					 &ice_dpll_output_ops, num_outputs);
 		ice_dpll_release_pins(outputs, num_outputs);
 		if (!pf->dplls.generic) {
-			ice_dpll_deinit_direct_pins(cgu, pf->dplls.ufl,
+			ice_dpll_deinit_direct_pins(pf, cgu, pf->dplls.ufl,
 						    ICE_DPLL_PIN_SW_NUM,
 						    &ice_dpll_pin_ufl_ops,
 						    pf->dplls.pps.dpll,
 						    pf->dplls.eec.dpll);
-			ice_dpll_deinit_direct_pins(cgu, pf->dplls.sma,
+			ice_dpll_deinit_direct_pins(pf, cgu, pf->dplls.sma,
 						    ICE_DPLL_PIN_SW_NUM,
 						    &ice_dpll_pin_sma_ops,
 						    pf->dplls.pps.dpll,
@@ -3142,6 +3671,133 @@ static void ice_dpll_deinit_pins(struct ice_pf *pf, bool cgu)
 	}
 }
 
+static int
+ice_dpll_init_fwnode_pin(struct ice_dpll_pin *pin, const char *name)
+{
+	struct ice_pf *pf = pin->pf;
+	int ret;
+
+	pin->fwnode = device_get_dpll_pin_node(&pf->pdev->dev, name);
+	if (IS_ERR(pin->fwnode)) {
+		dev_err(ice_pf_to_dev(pf),
+			"Failed to find %s firmware node: %pe\n", name,
+			pin->fwnode);
+		pin->fwnode = NULL;
+		return -ENODEV;
+	}
+
+	dev_dbg(ice_pf_to_dev(pf), "Found fwnode node for %s\n", name);
+
+	pin->pin = fwnode_dpll_pin_find(pin->fwnode, &pin->tracker);
+	if (IS_ERR_OR_NULL(pin->pin)) {
+		dev_info(ice_pf_to_dev(pf),
+			 "DPLL pin for %pfwp not registered yet\n",
+			 pin->fwnode);
+		pin->pin = NULL;
+	}
+
+	pin->nb.notifier_call = ice_dpll_pin_notify;
+	ret = register_dpll_notifier(&pin->nb);
+	if (ret) {
+		dev_err(ice_pf_to_dev(pf),
+			"Failed to subscribe for DPLL notifications\n");
+
+		if (!IS_ERR_OR_NULL(pin->pin)) {
+			dpll_pin_put(pin->pin, &pin->tracker);
+			pin->pin = NULL;
+		}
+		fwnode_handle_put(pin->fwnode);
+		pin->fwnode = NULL;
+
+		return ret;
+	}
+
+	return ret;
+}
+
+/**
+ * ice_dpll_init_fwnode_pins - initialize pins from device tree
+ * @pf: board private structure
+ * @pins: pointer to pins array
+ * @start_idx: starting index for pins
+ * @count: number of pins to initialize
+ *
+ * Initialize input pins for E825 RCLK support. The parent pins (rclk0, rclk1)
+ * are expected to be defined in the device tree (ACPI). This function allocates
+ * them in the dpll subsystem and stores their indices for later registration
+ * with the rclk pin.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - initialization failure reason
+ */
+static int
+ice_dpll_init_fwnode_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
+			  int start_idx)
+{
+	char pin_name[8];
+	int i, ret;
+
+	pf->dplls.wq = create_singlethread_workqueue("ice_dpll_wq");
+	if (!pf->dplls.wq)
+		return -ENOMEM;
+
+	for (i = 0; i < pf->dplls.rclk.num_parents; i++) {
+		pins[start_idx + i].pf = pf;
+		snprintf(pin_name, sizeof(pin_name), "rclk%u", i);
+		ret = ice_dpll_init_fwnode_pin(&pins[start_idx + i], pin_name);
+		if (ret)
+			goto error;
+	}
+
+	return 0;
+error:
+	while (i--)
+		ice_dpll_deinit_fwnode_pin(&pins[start_idx + i]);
+
+	destroy_workqueue(pf->dplls.wq);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_init_pins_e825 - init pins and register pins with a dplls
+ * @pf: board private structure
+ * @cgu: if cgu is present and controlled by this NIC
+ *
+ * Initialize directly connected pf's pins within pf's dplls in a Linux dpll
+ * subsystem.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - initialization failure reason
+ */
+static int ice_dpll_init_pins_e825(struct ice_pf *pf)
+{
+	int ret;
+
+	pf->dplls.num_inputs = pf->dplls.rclk.num_parents;
+	ret = ice_dpll_init_fwnode_pins(pf, pf->dplls.inputs, 0);
+	if (ret)
+		return ret;
+	if (ice_pf_src_tmr_owned(pf)) {
+		ret = ice_dpll_init_pin_1588(pf, DPLL_PIN_IDX_UNSPEC,
+					     &ice_dpll_pin_1588_ops);
+		if (ret)
+			return ret;
+	}
+	ret = ice_dpll_init_rclk_pin(pf, DPLL_PIN_IDX_UNSPEC,
+				     &ice_dpll_rclk_ops);
+	if (ret)
+		goto deinit_1588;
+
+	return 0;
+deinit_1588:
+	if (ice_pf_src_tmr_owned(pf))
+		ice_dpll_deinit_pin_1588(pf);
+	return ret;
+}
+
 /**
  * ice_dpll_init_pins - init pins and register pins with a dplls
  * @pf: board private structure
@@ -3156,21 +3812,24 @@ static void ice_dpll_deinit_pins(struct ice_pf *pf, bool cgu)
  */
 static int ice_dpll_init_pins(struct ice_pf *pf, bool cgu)
 {
+	const struct dpll_pin_ops *output_ops;
+	const struct dpll_pin_ops *input_ops;
 	int ret, count;
 
+	input_ops = &ice_dpll_input_ops;
+	output_ops = &ice_dpll_output_ops;
+
 	ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.inputs, 0,
-					pf->dplls.num_inputs,
-					&ice_dpll_input_ops,
-					pf->dplls.eec.dpll, pf->dplls.pps.dpll);
+					pf->dplls.num_inputs, input_ops,
+					pf->dplls.eec.dpll,
+					pf->dplls.pps.dpll);
 	if (ret)
 		return ret;
 	count = pf->dplls.num_inputs;
 	if (cgu) {
 		ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.outputs,
-						count,
-						pf->dplls.num_outputs,
-						&ice_dpll_output_ops,
-						pf->dplls.eec.dpll,
+						count, pf->dplls.num_outputs,
+						output_ops, pf->dplls.eec.dpll,
 						pf->dplls.pps.dpll);
 		if (ret)
 			goto deinit_inputs;
@@ -3206,30 +3865,30 @@ static int ice_dpll_init_pins(struct ice_pf *pf, bool cgu)
 	} else {
 		count += pf->dplls.num_outputs + 2 * ICE_DPLL_PIN_SW_NUM;
 	}
-	ret = ice_dpll_init_rclk_pins(pf, &pf->dplls.rclk, count + pf->hw.pf_id,
-				      &ice_dpll_rclk_ops);
+
+	ret = ice_dpll_init_rclk_pin(pf, count + pf->ptp.port.port_num,
+				     &ice_dpll_rclk_ops);
 	if (ret)
 		goto deinit_ufl;
 
 	return 0;
 deinit_ufl:
-	ice_dpll_deinit_direct_pins(cgu, pf->dplls.ufl,
-				    ICE_DPLL_PIN_SW_NUM,
-				    &ice_dpll_pin_ufl_ops,
-				    pf->dplls.pps.dpll, pf->dplls.eec.dpll);
+	ice_dpll_deinit_direct_pins(pf, cgu, pf->dplls.ufl, ICE_DPLL_PIN_SW_NUM,
+				    &ice_dpll_pin_ufl_ops, pf->dplls.pps.dpll,
+				    pf->dplls.eec.dpll);
 deinit_sma:
-	ice_dpll_deinit_direct_pins(cgu, pf->dplls.sma,
-				    ICE_DPLL_PIN_SW_NUM,
-				    &ice_dpll_pin_sma_ops,
-				    pf->dplls.pps.dpll, pf->dplls.eec.dpll);
+	ice_dpll_deinit_direct_pins(pf, cgu, pf->dplls.sma, ICE_DPLL_PIN_SW_NUM,
+				    &ice_dpll_pin_sma_ops, pf->dplls.pps.dpll,
+				    pf->dplls.eec.dpll);
 deinit_outputs:
-	ice_dpll_deinit_direct_pins(cgu, pf->dplls.outputs,
+	ice_dpll_deinit_direct_pins(pf, cgu, pf->dplls.outputs,
 				    pf->dplls.num_outputs,
-				    &ice_dpll_output_ops, pf->dplls.pps.dpll,
+				    output_ops, pf->dplls.pps.dpll,
 				    pf->dplls.eec.dpll);
 deinit_inputs:
-	ice_dpll_deinit_direct_pins(cgu, pf->dplls.inputs, pf->dplls.num_inputs,
-				    &ice_dpll_input_ops, pf->dplls.pps.dpll,
+	ice_dpll_deinit_direct_pins(pf, cgu, pf->dplls.inputs,
+				    pf->dplls.num_inputs,
+				    input_ops, pf->dplls.pps.dpll,
 				    pf->dplls.eec.dpll);
 	return ret;
 }
@@ -3240,8 +3899,8 @@ static int ice_dpll_init_pins(struct ice_pf *pf, bool cgu)
  * @d: pointer to ice_dpll
  * @cgu: if cgu is present and controlled by this NIC
  *
- * If cgu is owned unregister the dpll from dpll subsystem.
- * Release resources of dpll device from dpll subsystem.
+ * If cgu is owned, unregister the DPL from DPLL subsystem.
+ * Release resources of DPLL device from DPLL subsystem.
  */
 static void
 ice_dpll_deinit_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu)
@@ -3258,8 +3917,8 @@ ice_dpll_deinit_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu)
  * @cgu: if cgu is present and controlled by this NIC
  * @type: type of dpll being initialized
  *
- * Allocate dpll instance for this board in dpll subsystem, if cgu is controlled
- * by this NIC, register dpll with the callback ops.
+ * Allocate DPLL instance for this board in dpll subsystem, if cgu is controlled
+ * by this NIC, register DPLL with the callback ops.
  *
  * Return:
  * * 0 - success
@@ -3290,6 +3949,7 @@ ice_dpll_init_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu,
 		ret = dpll_device_register(d->dpll, type, ops, d);
 		if (ret) {
 			dpll_device_put(d->dpll, &d->tracker);
+			d->dpll = NULL;
 			return ret;
 		}
 		d->ops = ops;
@@ -3507,6 +4167,32 @@ ice_dpll_init_info_direct_pins(struct ice_pf *pf,
 	return ret;
 }
 
+/**
+ * ice_dpll_init_info_pin_on_pin_e825c - initializes rclk pin information
+ * @pf: board private structure
+ *
+ * Init information for rclk pin, cache them in pf->dplls.rclk and
+ * pf->dplls.pin_1588.
+ *
+ * Return:
+ * * 0 - success
+ */
+static int ice_dpll_init_info_pin_on_pin_e825c(struct ice_pf *pf)
+{
+	struct ice_dpll_pin *pin_1588 = &pf->dplls.pin_1588;
+	struct ice_dpll_pin *rclk_pin = &pf->dplls.rclk;
+
+	rclk_pin->prop.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
+	rclk_pin->prop.capabilities |= DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE;
+	rclk_pin->pf = pf;
+	pin_1588->prop.type = DPLL_PIN_TYPE_EXT;
+	pin_1588->prop.capabilities |= DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE;
+	pin_1588->prop.board_label = ice_dpll_pin_1588;
+	pin_1588->pf = pf;
+
+	return 0;
+}
+
 /**
  * ice_dpll_init_info_rclk_pin - initializes rclk pin information
  * @pf: board private structure
@@ -3633,7 +4319,10 @@ ice_dpll_init_pins_info(struct ice_pf *pf, enum ice_dpll_pin_type pin_type)
 	case ICE_DPLL_PIN_TYPE_OUTPUT:
 		return ice_dpll_init_info_direct_pins(pf, pin_type);
 	case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
-		return ice_dpll_init_info_rclk_pin(pf);
+		if (pf->hw.mac_type == ICE_MAC_GENERIC_3K_E825)
+			return ice_dpll_init_info_pin_on_pin_e825c(pf);
+		else
+			return ice_dpll_init_info_rclk_pin(pf);
 	case ICE_DPLL_PIN_TYPE_SOFTWARE:
 		return ice_dpll_init_info_sw_pins(pf);
 	default:
@@ -3655,6 +4344,54 @@ static void ice_dpll_deinit_info(struct ice_pf *pf)
 	kfree(pf->dplls.pps.input_prio);
 }
 
+/**
+ * ice_dpll_init_info_e825c - prepare pf's dpll information structure for e825c
+ * device
+ * @pf: board private structure
+ *
+ * Acquire (from HW) and set basic DPLL information (on pf->dplls struct).
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure reason
+ */
+static int ice_dpll_init_info_e825c(struct ice_pf *pf)
+{
+	struct ice_dplls *d = &pf->dplls;
+	int ret = 0;
+	int i;
+
+	d->clock_id = ice_generate_clock_id(pf);
+	d->num_inputs = ICE_SYNCE_CLK_NUM;
+
+	d->inputs = kcalloc(d->num_inputs, sizeof(*d->inputs), GFP_KERNEL);
+	if (!d->inputs)
+		return -ENOMEM;
+
+	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &d->base_rclk_idx,
+					&pf->dplls.rclk.num_parents);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < pf->dplls.rclk.num_parents; i++)
+		pf->dplls.rclk.parent_idx[i] = d->base_rclk_idx + i;
+
+	if (ice_pf_src_tmr_owned(pf)) {
+		d->base_1588_idx = ICE_E825_1588_BASE_IDX;
+		pf->dplls.pin_1588.num_parents = pf->dplls.rclk.num_parents;
+		for (i = 0; i < pf->dplls.pin_1588.num_parents; i++)
+			pf->dplls.pin_1588.parent_idx[i] = d->base_1588_idx + i;
+	}
+	ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_RCLK_INPUT);
+	if (ret)
+		return ret;
+	dev_dbg(ice_pf_to_dev(pf),
+		"%s - success, inputs: %u, outputs: %u, rclk-parents: %u, pin_1588-parents: %u\n",
+		 __func__, d->num_inputs, d->num_outputs, d->rclk.num_parents,
+		 d->pin_1588.num_parents);
+	return 0;
+}
+
 /**
  * ice_dpll_init_info - prepare pf's dpll information structure
  * @pf: board private structure
@@ -3774,14 +4511,17 @@ void ice_dpll_deinit(struct ice_pf *pf)
 		ice_dpll_deinit_worker(pf);
 
 	ice_dpll_deinit_pins(pf, cgu);
-	ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
-	ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
-	ice_dpll_deinit_info(pf);
+	if (!IS_ERR_OR_NULL(pf->dplls.pps.dpll))
+		ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
+	if (!IS_ERR_OR_NULL(pf->dplls.eec.dpll))
+		ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
+	if (pf->hw.mac_type != ICE_MAC_GENERIC_3K_E825)
+		ice_dpll_deinit_info(pf);
 	mutex_destroy(&pf->dplls.lock);
 }
 
 /**
- * ice_dpll_init - initialize support for dpll subsystem
+ * ice_dpll_init_e825 - initialize support for dpll subsystem
  * @pf: board private structure
  *
  * Set up the device dplls, register them and pins connected within Linux dpll
@@ -3790,7 +4530,41 @@ void ice_dpll_deinit(struct ice_pf *pf)
  *
  * Context: Initializes pf->dplls.lock mutex.
  */
-void ice_dpll_init(struct ice_pf *pf)
+static void ice_dpll_init_e825(struct ice_pf *pf)
+{
+	struct ice_dplls *d = &pf->dplls;
+	int err;
+
+	mutex_init(&d->lock);
+
+	err = ice_dpll_init_info_e825c(pf);
+	if (err)
+		goto err_exit;
+	err = ice_dpll_init_pins_e825(pf);
+	if (err)
+		goto deinit_info;
+	set_bit(ICE_FLAG_DPLL, pf->flags);
+
+	return;
+
+deinit_info:
+	ice_dpll_deinit_info(pf);
+err_exit:
+	mutex_destroy(&d->lock);
+	dev_warn(ice_pf_to_dev(pf), "DPLLs init failure err:%d\n", err);
+}
+
+/**
+ * ice_dpll_init_e810 - initialize support for dpll subsystem
+ * @pf: board private structure
+ *
+ * Set up the device dplls, register them and pins connected within Linux dpll
+ * subsystem. Allow userspace to obtain state of DPLL and handling of DPLL
+ * configuration requests.
+ *
+ * Context: Initializes pf->dplls.lock mutex.
+ */
+static void ice_dpll_init_e810(struct ice_pf *pf)
 {
 	bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
 	struct ice_dplls *d = &pf->dplls;
@@ -3830,3 +4604,17 @@ void ice_dpll_init(struct ice_pf *pf)
 	mutex_destroy(&d->lock);
 	dev_warn(ice_pf_to_dev(pf), "DPLLs init failure err:%d\n", err);
 }
+
+void ice_dpll_init(struct ice_pf *pf)
+{
+	switch (pf->hw.mac_type) {
+	case ICE_MAC_GENERIC_3K_E825:
+		ice_dpll_init_e825(pf);
+		break;
+	default:
+		ice_dpll_init_e810(pf);
+		break;
+	}
+
+	return;
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.h b/drivers/net/ethernet/intel/ice/ice_dpll.h
index 63fac6510df6e..73755d0d30ed2 100644
--- a/drivers/net/ethernet/intel/ice/ice_dpll.h
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
@@ -20,6 +20,12 @@ enum ice_dpll_pin_sw {
 	ICE_DPLL_PIN_SW_NUM
 };
 
+struct ice_dpll_pin_work {
+	struct work_struct work;
+	unsigned long action;
+	struct ice_dpll_pin *pin;
+};
+
 /** ice_dpll_pin - store info about pins
  * @pin: dpll pin structure
  * @pf: pointer to pf, which has registered the dpll_pin
@@ -39,6 +45,8 @@ struct ice_dpll_pin {
 	struct dpll_pin *pin;
 	struct ice_pf *pf;
 	dpll_tracker tracker;
+	struct fwnode_handle *fwnode;
+	struct notifier_block nb;
 	u8 idx;
 	u8 num_parents;
 	u8 parent_idx[ICE_DPLL_RCLK_NUM_MAX];
@@ -105,11 +113,13 @@ struct ice_dpll {
  * @pps: pointer to PPS dpll dev
  * @inputs: input pins pointer
  * @outputs: output pins pointer
+ * @pin_1588: pin controlling clock 1588 pointer
  * @rclk: recovered pins pointer
  * @num_inputs: number of input pins available on dpll
  * @num_outputs: number of output pins available on dpll
  * @cgu_state_acq_err_num: number of errors returned during periodic work
  * @base_rclk_idx: idx of first pin used for clock revocery pins
+ * @base_1588_idx: idx of first pin used for 1588 clock control pin
  * @clock_id: clock_id of dplls
  * @input_phase_adj_max: max phase adjust value for an input pins
  * @output_phase_adj_max: max phase adjust value for an output pins
@@ -118,11 +128,13 @@ struct ice_dpll {
 struct ice_dplls {
 	struct kthread_worker *kworker;
 	struct kthread_delayed_work work;
+	struct workqueue_struct *wq;
 	struct mutex lock;
 	struct ice_dpll eec;
 	struct ice_dpll pps;
 	struct ice_dpll_pin *inputs;
 	struct ice_dpll_pin *outputs;
+	struct ice_dpll_pin pin_1588;
 	struct ice_dpll_pin sma[ICE_DPLL_PIN_SW_NUM];
 	struct ice_dpll_pin ufl[ICE_DPLL_PIN_SW_NUM];
 	struct ice_dpll_pin rclk;
@@ -130,6 +142,7 @@ struct ice_dplls {
 	u8 num_outputs;
 	u8 sma_data;
 	u8 base_rclk_idx;
+	u8 base_1588_idx;
 	int cgu_state_acq_err_num;
 	u64 clock_id;
 	s32 input_phase_adj_max;
@@ -147,3 +160,19 @@ static inline void ice_dpll_deinit(struct ice_pf *pf) { }
 #endif
 
 #endif
+
+#define ICE_CGU_R10				0x28
+#define ICE_CGU_R10_SYNCE_CLKO_SEL		GENMASK(8, 5)
+#define ICE_CGU_R10_SYNCE_CLKODIV_M1		GENMASK(13, 9)
+#define ICE_CGU_R10_SYNCE_CLKODIV_LOAD		BIT(14)
+#define ICE_CGU_R10_SYNCE_DCK_RST		BIT(15)
+#define ICE_CGU_R10_SYNCE_ETHCLKO_SEL		GENMASK(18, 16)
+#define ICE_CGU_R10_SYNCE_ETHDIV_M1		GENMASK(23, 19)
+#define ICE_CGU_R10_SYNCE_ETHDIV_LOAD		BIT(24)
+#define ICE_CGU_R10_SYNCE_DCK2_RST		BIT(25)
+#define ICE_CGU_R10_SYNCE_S_REF_CLK		GENMASK(31, 27)
+
+#define ICE_CGU_R11				0x2C
+#define ICE_CGU_R11_SYNCE_S_BYP_CLK		GENMASK(6, 1)
+
+#define ICE_CGU_BYPASS_MUX_OFFSET_E825C		3
diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c
index 15621707fbf81..bac481e8140d2 100644
--- a/drivers/net/ethernet/intel/ice/ice_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_lib.c
@@ -3946,6 +3946,9 @@ void ice_init_feature_support(struct ice_pf *pf)
 		break;
 	}
 
+	if (pf->hw.mac_type == ICE_MAC_GENERIC_3K_E825)
+		ice_set_feature_support(pf, ICE_F_PHY_RCLK);
+
 	if (pf->hw.mac_type == ICE_MAC_E830) {
 		ice_set_feature_support(pf, ICE_F_MBX_LIMIT);
 		ice_set_feature_support(pf, ICE_F_GCS);
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
index 4c8d20f2d2c0a..0eb8a9e29a521 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -1341,6 +1341,35 @@ void ice_ptp_link_change(struct ice_pf *pf, bool linkup)
 	if (pf->hw.reset_ongoing)
 		return;
 
+	if (hw->mac_type == ICE_MAC_GENERIC_3K_E825) {
+		int pin, err;
+
+		mutex_lock(&pf->dplls.lock);
+		for (pin = 0; pin < ICE_SYNCE_CLK_NUM; pin++) {
+			enum ice_synce_clk clk_pin;
+			bool active;
+			u8 port_num;
+
+			port_num = ptp_port->port_num;
+			clk_pin = (enum ice_synce_clk)pin;
+			err = ice_tspll_bypass_mux_active_e825c(hw,
+								port_num,
+								&active,
+								clk_pin);
+			if (WARN_ON_ONCE(err)) {
+				mutex_unlock(&pf->dplls.lock);
+				return;
+			}
+
+			err = ice_tspll_cfg_synce_ethdiv_e825c(hw, clk_pin);
+			if (active && WARN_ON_ONCE(err)) {
+				mutex_unlock(&pf->dplls.lock);
+				return;
+			}
+		}
+		mutex_unlock(&pf->dplls.lock);
+	}
+
 	switch (hw->mac_type) {
 	case ICE_MAC_E810:
 	case ICE_MAC_E830:
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
index 35680dbe4a7f7..61c0a0d93ea89 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
@@ -5903,7 +5903,14 @@ int ice_get_cgu_rclk_pin_info(struct ice_hw *hw, u8 *base_idx, u8 *pin_num)
 			*base_idx = SI_REF1P;
 		else
 			ret = -ENODEV;
-
+		break;
+	case ICE_DEV_ID_E825C_BACKPLANE:
+	case ICE_DEV_ID_E825C_QSFP:
+	case ICE_DEV_ID_E825C_SFP:
+	case ICE_DEV_ID_E825C_SGMII:
+		*pin_num = ICE_SYNCE_CLK_NUM;
+		*base_idx = 0;
+		ret = 0;
 		break;
 	default:
 		ret = -ENODEV;
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
index 5896b346e5790..2c2fa1e73ee0b 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
@@ -210,6 +210,7 @@ enum ice_phy_rclk_pins {
 #define ICE_E82X_RCLK_PINS_NUM		(ICE_RCLKA_PIN + 1)
 #define E810T_CGU_INPUT_C827(_phy, _pin) ((_phy) * ICE_E810_RCLK_PINS_NUM + \
 					  (_pin) + ZL_REF1P)
+#define ICE_E825_1588_BASE_IDX		0
 
 enum ice_zl_cgu_in_pins {
 	ZL_REF0P = 0,
diff --git a/drivers/net/ethernet/intel/ice/ice_tspll.c b/drivers/net/ethernet/intel/ice/ice_tspll.c
index 66320a4ab86fd..78d74fb0d94b0 100644
--- a/drivers/net/ethernet/intel/ice/ice_tspll.c
+++ b/drivers/net/ethernet/intel/ice/ice_tspll.c
@@ -624,3 +624,226 @@ int ice_tspll_init(struct ice_hw *hw)
 
 	return err;
 }
+
+/**
+ * ice_tspll_bypass_mux_active_e825c - check if the given port is set active
+ * @hw: Pointer to the HW struct
+ * @port: Number of the port
+ * @active: Output flag showing if port is active
+ * @output: Output pin, we have two in E825C
+ *
+ * Check if given port is selected as recovered clock source for given output.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+int ice_tspll_bypass_mux_active_e825c(struct ice_hw *hw, u8 port, bool *active,
+				      enum ice_synce_clk output)
+{
+	u8 active_clk;
+	u32 val;
+
+	switch (output) {
+	case ICE_SYNCE_CLK0:
+		ice_read_cgu_reg(hw, ICE_CGU_R10, &val);
+		active_clk = FIELD_GET(ICE_CGU_R10_SYNCE_S_REF_CLK, val);
+		break;
+	case ICE_SYNCE_CLK1:
+		ice_read_cgu_reg(hw, ICE_CGU_R11, &val);
+		active_clk = FIELD_GET(ICE_CGU_R11_SYNCE_S_BYP_CLK, val);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (active_clk == port % hw->ptp.ports_per_phy +
+			  ICE_CGU_BYPASS_MUX_OFFSET_E825C)
+		*active = true;
+	else
+		*active = false;
+
+	return 0;
+}
+
+/**
+ * ice_tspll_cfg_bypass_mux_e825c - configure reference clock mux
+ * @hw: Pointer to the HW struct
+ * @ena: true to enable the reference, false if disable
+ * @port_num: Number of the port
+ * @output: Output pin, we have two in E825C
+ * @clock_1588: true to enable 1588 reference, false to recover from port
+ *
+ * Set reference clock source and output clock selection.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+int ice_tspll_cfg_bypass_mux_e825c(struct ice_hw *hw, bool ena, u32 port_num,
+				   enum ice_synce_clk output, bool clock_1588)
+{
+	u8 first_mux;
+	int err;
+	u32 r10;
+
+	err = ice_read_cgu_reg(hw, ICE_CGU_R10, &r10);
+	if (err)
+		return err;
+
+	if (!ena)
+		first_mux = ICE_CGU_NET_REF_CLK0;
+	else if (clock_1588)
+		first_mux = ICE_CGU_NCOCLK;
+	else
+		first_mux = port_num + ICE_CGU_BYPASS_MUX_OFFSET_E825C;
+
+	r10 &= ~(ICE_CGU_R10_SYNCE_DCK_RST | ICE_CGU_R10_SYNCE_DCK2_RST);
+
+	switch (output) {
+	case ICE_SYNCE_CLK0:
+		r10 &= ~(ICE_CGU_R10_SYNCE_ETHCLKO_SEL |
+			 ICE_CGU_R10_SYNCE_ETHDIV_LOAD |
+			 ICE_CGU_R10_SYNCE_S_REF_CLK);
+		r10 |= FIELD_PREP(ICE_CGU_R10_SYNCE_S_REF_CLK, first_mux);
+		if (clock_1588)
+			r10 |= FIELD_PREP(ICE_CGU_R10_SYNCE_ETHCLKO_SEL,
+					  ICE_CGU_REF_CLK_BYP0);
+		else
+			r10 |= FIELD_PREP(ICE_CGU_R10_SYNCE_ETHCLKO_SEL,
+					  ICE_CGU_REF_CLK_BYP0_DIV);
+		break;
+	case ICE_SYNCE_CLK1:
+	{
+		u32 val;
+
+		err = ice_read_cgu_reg(hw, ICE_CGU_R11, &val);
+		if (err)
+			return err;
+		val &= ~ICE_CGU_R11_SYNCE_S_BYP_CLK;
+		val |= FIELD_PREP(ICE_CGU_R11_SYNCE_S_BYP_CLK, first_mux);
+		err = ice_write_cgu_reg(hw, ICE_CGU_R11, val);
+		if (err)
+			return err;
+		r10 &= ~(ICE_CGU_R10_SYNCE_CLKODIV_LOAD |
+			 ICE_CGU_R10_SYNCE_CLKO_SEL);
+		if (clock_1588)
+			r10 |= FIELD_PREP(ICE_CGU_R10_SYNCE_CLKO_SEL,
+					  ICE_CGU_REF_CLK_BYP1);
+		else
+			r10 |= FIELD_PREP(ICE_CGU_R10_SYNCE_CLKO_SEL,
+					  ICE_CGU_REF_CLK_BYP1_DIV);
+		break;
+	}
+	default:
+		return -EINVAL;
+	}
+
+	err = ice_write_cgu_reg(hw, ICE_CGU_R10, r10);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+/**
+ * ice_tspll_get_div_e825c - get the divider for the given speed
+ * @link_speed: link speed of the port
+ * @divider: output value, calculated divider
+ *
+ * Get CGU divider value based on the link speed.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+static int ice_tspll_get_div_e825c(u16 link_speed, unsigned int *divider)
+{
+	switch (link_speed) {
+	case ICE_AQ_LINK_SPEED_100GB:
+	case ICE_AQ_LINK_SPEED_50GB:
+	case ICE_AQ_LINK_SPEED_25GB:
+		*divider = 10;
+		break;
+	case ICE_AQ_LINK_SPEED_40GB:
+	case ICE_AQ_LINK_SPEED_10GB:
+		*divider = 4;
+		break;
+	case ICE_AQ_LINK_SPEED_5GB:
+	case ICE_AQ_LINK_SPEED_2500MB:
+	case ICE_AQ_LINK_SPEED_1000MB:
+		*divider = 2;
+		break;
+	case ICE_AQ_LINK_SPEED_100MB:
+		*divider = 1;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+/**
+ * ice_tspll_cfg_synce_ethdiv_e825c - set the divider on the mux
+ * @hw: Pointer to the HW struct
+ * @output: Output pin, we have two in E825C
+ *
+ * Set the correct CGU divider for RCLKA or RCLKB.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+int ice_tspll_cfg_synce_ethdiv_e825c(struct ice_hw *hw,
+				     enum ice_synce_clk output)
+{
+	unsigned int divider;
+	u16 link_speed;
+	u32 val;
+	int err;
+
+	link_speed = hw->port_info->phy.link_info.link_speed;
+	if (!link_speed)
+		return 0;
+
+	err = ice_tspll_get_div_e825c(link_speed, &divider);
+	if (err)
+		return err;
+
+	err = ice_read_cgu_reg(hw, ICE_CGU_R10, &val);
+	if (err)
+		return err;
+
+	/* programmable divider value (from 2 to 16) minus 1 for ETHCLKOUT */
+	switch (output) {
+	case ICE_SYNCE_CLK0:
+		val &= ~(ICE_CGU_R10_SYNCE_ETHDIV_M1 |
+			 ICE_CGU_R10_SYNCE_ETHDIV_LOAD);
+		val |= FIELD_PREP(ICE_CGU_R10_SYNCE_ETHDIV_M1, divider - 1);
+		err = ice_write_cgu_reg(hw, ICE_CGU_R10, val);
+		if (err)
+			return err;
+		val |= ICE_CGU_R10_SYNCE_ETHDIV_LOAD;
+		break;
+	case ICE_SYNCE_CLK1:
+		val &= ~(ICE_CGU_R10_SYNCE_CLKODIV_M1 |
+			 ICE_CGU_R10_SYNCE_CLKODIV_LOAD);
+		val |= FIELD_PREP(ICE_CGU_R10_SYNCE_CLKODIV_M1, divider - 1);
+		err = ice_write_cgu_reg(hw, ICE_CGU_R10, val);
+		if (err)
+			return err;
+		val |= ICE_CGU_R10_SYNCE_CLKODIV_LOAD;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	err = ice_write_cgu_reg(hw, ICE_CGU_R10, val);
+	if (err)
+		return err;
+
+	return 0;
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_tspll.h b/drivers/net/ethernet/intel/ice/ice_tspll.h
index c0b1232cc07c3..cf5581f152e78 100644
--- a/drivers/net/ethernet/intel/ice/ice_tspll.h
+++ b/drivers/net/ethernet/intel/ice/ice_tspll.h
@@ -21,11 +21,23 @@ struct ice_tspll_params_e82x {
 	u32 frac_n_div;
 };
 
+#define ICE_CGU_NET_REF_CLK0		0x0
+#define ICE_CGU_NCOCLK			0x2
+#define ICE_CGU_REF_CLK_BYP0		0x5
+#define ICE_CGU_REF_CLK_BYP0_DIV	0x0
+#define ICE_CGU_REF_CLK_BYP1		0x4
+#define ICE_CGU_REF_CLK_BYP1_DIV	0x1
+
 #define ICE_TSPLL_CK_REFCLKFREQ_E825		0x1F
 #define ICE_TSPLL_NDIVRATIO_E825		5
 #define ICE_TSPLL_FBDIV_INTGR_E825		256
 
 int ice_tspll_cfg_pps_out_e825c(struct ice_hw *hw, bool enable);
 int ice_tspll_init(struct ice_hw *hw);
-
+int ice_tspll_bypass_mux_active_e825c(struct ice_hw *hw, u8 port, bool *active,
+				      enum ice_synce_clk output);
+int ice_tspll_cfg_bypass_mux_e825c(struct ice_hw *hw, bool ena, u32 port_num,
+				   enum ice_synce_clk output, bool clock_1588);
+int ice_tspll_cfg_synce_ethdiv_e825c(struct ice_hw *hw,
+				     enum ice_synce_clk output);
 #endif /* _ICE_TSPLL_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
index 6a2ec8389a8f3..1e82f4c40b326 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -349,6 +349,12 @@ enum ice_clk_src {
 	NUM_ICE_CLK_SRC
 };
 
+enum ice_synce_clk {
+	ICE_SYNCE_CLK0,
+	ICE_SYNCE_CLK1,
+	ICE_SYNCE_CLK_NUM
+};
+
 struct ice_ts_func_info {
 	/* Function specific info */
 	enum ice_tspll_freq time_ref;
-- 
2.51.2


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

* Re: [PATCH RFC net-next 01/13] dt-bindings: net: ethernet-controller: Add DPLL pin properties
  2025-12-11 19:47 ` [PATCH RFC net-next 01/13] dt-bindings: net: ethernet-controller: Add DPLL pin properties Ivan Vecera
@ 2025-12-11 19:56   ` Andrew Lunn
  2025-12-14 20:41     ` Ivan Vecera
  2025-12-17  0:49     ` Rob Herring
  0 siblings, 2 replies; 27+ messages in thread
From: Andrew Lunn @ 2025-12-11 19:56 UTC (permalink / raw)
  To: Ivan Vecera
  Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Jiri Pirko, Petr Oros, Michal Schmidt,
	Prathosh Satish, Tony Nguyen, Przemek Kitszel, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Richard Cochran,
	Jonathan Lemon, Simon Horman, Alexander Lobakin, Willem de Bruijn,
	Stefan Wahren, devicetree, linux-kernel, intel-wired-lan,
	linux-rdma

On Thu, Dec 11, 2025 at 08:47:44PM +0100, Ivan Vecera wrote:
> Ethernet controllers may be connected to DPLL (Digital Phase Locked Loop)
> pins for frequency synchronization purposes, such as in Synchronous
> Ethernet (SyncE) configurations.
> 
> Add 'dpll-pins' and 'dpll-pin-names' properties to the generic
> ethernet-controller schema. This allows describing the physical
> connections between the Ethernet controller and the DPLL subsystem pins
> in the Device Tree, enabling drivers to request and manage these
> resources.

Please include a .dts patch in the series which actually makes use of
these new properties.

	Andrew

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

* RE: [Intel-wired-lan] [PATCH RFC net-next 13/13] ice: dpll: Support E825-C SyncE and dynamic pin discovery
  2025-12-11 19:47 ` [PATCH RFC net-next 13/13] ice: dpll: Support E825-C SyncE and dynamic pin discovery Ivan Vecera
@ 2025-12-12 10:20   ` Loktionov, Aleksandr
  2025-12-14 19:30     ` Ivan Vecera
  0 siblings, 1 reply; 27+ messages in thread
From: Loktionov, Aleksandr @ 2025-12-12 10:20 UTC (permalink / raw)
  To: Vecera, Ivan, netdev@vger.kernel.org, Andrew Lunn,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vadim Fedorenko,
	Kubalewski, Arkadiusz, Nitka, Grzegorz, Jiri Pirko, Oros, Petr,
	Schmidt, Michal, Prathosh Satish, Nguyen, Anthony L,
	Kitszel, Przemyslaw, Saeed Mahameed, Leon Romanovsky,
	Tariq Toukan, Mark Bloch, Richard Cochran, Jonathan Lemon,
	Simon Horman, Lobakin, Aleksander, Willem de Bruijn,
	Stefan Wahren, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, intel-wired-lan@lists.osuosl.org,
	linux-rdma@vger.kernel.org



> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf
> Of Ivan Vecera
> Sent: Thursday, December 11, 2025 8:48 PM
> To: netdev@vger.kernel.org; Andrew Lunn <andrew+netdev@lunn.ch>;
> David S. Miller <davem@davemloft.net>; Eric Dumazet
> <edumazet@google.com>; Jakub Kicinski <kuba@kernel.org>; Paolo Abeni
> <pabeni@redhat.com>; Rob Herring <robh@kernel.org>; Krzysztof
> Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>;
> Vadim Fedorenko <vadim.fedorenko@linux.dev>; Kubalewski, Arkadiusz
> <arkadiusz.kubalewski@intel.com>; Nitka, Grzegorz
> <grzegorz.nitka@intel.com>; Jiri Pirko <jiri@resnulli.us>; Oros,
> Petr <poros@redhat.com>; Schmidt, Michal <mschmidt@redhat.com>;
> Prathosh Satish <Prathosh.Satish@microchip.com>; Nguyen, Anthony L
> <anthony.l.nguyen@intel.com>; Kitszel, Przemyslaw
> <przemyslaw.kitszel@intel.com>; Saeed Mahameed <saeedm@nvidia.com>;
> Leon Romanovsky <leon@kernel.org>; Tariq Toukan <tariqt@nvidia.com>;
> Mark Bloch <mbloch@nvidia.com>; Richard Cochran
> <richardcochran@gmail.com>; Jonathan Lemon
> <jonathan.lemon@gmail.com>; Simon Horman <horms@kernel.org>;
> Lobakin, Aleksander <aleksander.lobakin@intel.com>; Willem de Bruijn
> <willemb@google.com>; Stefan Wahren <wahrenst@gmx.net>;
> devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; intel-
> wired-lan@lists.osuosl.org; linux-rdma@vger.kernel.org
> Subject: [Intel-wired-lan] [PATCH RFC net-next 13/13] ice: dpll:
> Support E825-C SyncE and dynamic pin discovery
> 
> From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
> 
> Add DPLL support for the Intel E825-C Ethernet controller. Unlike
> previous
> generations (E810), the E825-C connects to the platform's DPLL
> subsystem
> via MUX pins defined in the system firmware (Device Tree/ACPI).
> 
> Implement the following mechanisms to support this architecture:
> 
> 1. Dynamic Pin Discovery: Use the fwnode_dpll_pin_find() helper to
>    locate the parent MUX pins defined in the firmware.
> 
> 2. Asynchronous Registration: Since the platform DPLL driver may
> probe
>    independently of the network driver, utilize the DPLL notifier
> chain
>    (register_dpll_notifier). The driver listens for DPLL_PIN_CREATED
>    events to detect when the parent MUX pins become available, then
>    registers its own Recovered Clock (RCLK) and PTP (1588) pins as
> children
>    of those parents.
> 
> 3. Hardware Configuration: Implement the specific register access
> logic
>    for E825-C CGU (Clock Generation Unit) registers (R10, R11). This
>    includes configuring the bypass MUXes and clock dividers required
> to
>    drive SyncE and PTP signals.
> 
> 4. Split Initialization: Refactor `ice_dpll_init()` to separate the
>    static initialization path of E810 from the dynamic, firmware-
> driven
>    path required for E825-C.
> 
> Co-developed-by: Ivan Vecera <ivecera@redhat.com>
> Co-developed-by: Grzegorz Nitka <grzegorz.nitka@intel.com>
> Signed-off-by: Ivan Vecera <ivecera@redhat.com>
> Signed-off-by: Grzegorz Nitka <grzegorz.nitka@intel.com>
> Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
> ---
>  drivers/net/ethernet/intel/ice/ice_dpll.c   | 964
> ++++++++++++++++++--
>  drivers/net/ethernet/intel/ice/ice_dpll.h   |  29 +
>  drivers/net/ethernet/intel/ice/ice_lib.c    |   3 +
>  drivers/net/ethernet/intel/ice/ice_ptp.c    |  29 +
>  drivers/net/ethernet/intel/ice/ice_ptp_hw.c |   9 +-
>  drivers/net/ethernet/intel/ice/ice_ptp_hw.h |   1 +
>  drivers/net/ethernet/intel/ice/ice_tspll.c  | 223 +++++
>  drivers/net/ethernet/intel/ice/ice_tspll.h  |  14 +-
>  drivers/net/ethernet/intel/ice/ice_type.h   |   6 +
>  9 files changed, 1188 insertions(+), 90 deletions(-)
> 
> diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c

...

> +static int
> +ice_dpll_pin_get_parent_num(struct ice_dpll_pin *pin,
> +			    const struct dpll_pin *parent)
> +{
> +	int i;
> +
> +	for (i = 0; pin->num_parents; i++)
> +		if (pin->pf->dplls.inputs[pin->parent_idx[i]].pin ==
> parent)
Oh, no! we don't need a 2nd Infinite Loop in Cupertino!

...


> --
> 2.51.2


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

* Re: [PATCH RFC net-next 11/13] dpll: zl3073x: Enable reference count tracking
  2025-12-11 19:47 ` [PATCH RFC net-next 11/13] dpll: zl3073x: Enable reference count tracking Ivan Vecera
@ 2025-12-12 11:11   ` Jiri Pirko
  0 siblings, 0 replies; 27+ messages in thread
From: Jiri Pirko @ 2025-12-12 11:11 UTC (permalink / raw)
  To: Ivan Vecera
  Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Petr Oros, Michal Schmidt, Prathosh Satish,
	Tony Nguyen, Przemek Kitszel, Saeed Mahameed, Leon Romanovsky,
	Tariq Toukan, Mark Bloch, Richard Cochran, Jonathan Lemon,
	Simon Horman, Alexander Lobakin, Willem de Bruijn, Stefan Wahren,
	devicetree, linux-kernel, intel-wired-lan, linux-rdma

Thu, Dec 11, 2025 at 08:47:54PM +0100, ivecera@redhat.com wrote:
>Update the zl3073x driver to utilize the DPLL reference count tracking
>infrastructure.
>
>Add dpll_tracker fields to the driver's internal device and pin
>structures. Pass pointers to these trackers when calling
>dpll_device_get/put() and dpll_pin_get/put().
>
>This allows a developer to inspect the specific references held by this
>driver via debugfs when CONFIG_DPLL_REFCNT_TRACKER is enabled, aiding
>in the debugging of resource leaks.
>
>Signed-off-by: Ivan Vecera <ivecera@redhat.com>

Care to do this for the rest of the users? Not so many of them...

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

* Re: [PATCH RFC net-next 02/13] dpll: Allow registering pin with firmware node
  2025-12-11 19:47 ` [PATCH RFC net-next 02/13] dpll: Allow registering pin with firmware node Ivan Vecera
@ 2025-12-12 11:25   ` Jiri Pirko
  2025-12-14 19:35     ` Ivan Vecera
  0 siblings, 1 reply; 27+ messages in thread
From: Jiri Pirko @ 2025-12-12 11:25 UTC (permalink / raw)
  To: Ivan Vecera
  Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Petr Oros, Michal Schmidt, Prathosh Satish,
	Tony Nguyen, Przemek Kitszel, Saeed Mahameed, Leon Romanovsky,
	Tariq Toukan, Mark Bloch, Richard Cochran, Jonathan Lemon,
	Simon Horman, Alexander Lobakin, Willem de Bruijn, Stefan Wahren,
	devicetree, linux-kernel, intel-wired-lan, linux-rdma

Thu, Dec 11, 2025 at 08:47:45PM +0100, ivecera@redhat.com wrote:

[..]

>@@ -559,7 +563,8 @@ EXPORT_SYMBOL(dpll_netdev_pin_clear);
>  */
> struct dpll_pin *
> dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
>-	     const struct dpll_pin_properties *prop)
>+	     const struct dpll_pin_properties *prop,
>+	     struct fwnode_handle *fwnode)
> {
> 	struct dpll_pin *pos, *ret = NULL;
> 	unsigned long i;
>@@ -568,14 +573,15 @@ dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
> 	xa_for_each(&dpll_pin_xa, i, pos) {
> 		if (pos->clock_id == clock_id &&
> 		    pos->pin_idx == pin_idx &&
>-		    pos->module == module) {
>+		    pos->module == module &&
>+		    pos->fwnode == fwnode) {

Is fwnode part of the key? Doesn't look to me like that. Then you can
have a simple helper to set fwnode on struct dpll_pin *, and leave
dpll_pin_get() out of this, no?


> 			ret = pos;
> 			refcount_inc(&ret->refcount);
> 			break;
> 		}

[..]

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

* RE: [Intel-wired-lan] [PATCH RFC net-next 13/13] ice: dpll: Support E825-C SyncE and dynamic pin discovery
  2025-12-12 10:20   ` [Intel-wired-lan] " Loktionov, Aleksandr
@ 2025-12-14 19:30     ` Ivan Vecera
  0 siblings, 0 replies; 27+ messages in thread
From: Ivan Vecera @ 2025-12-14 19:30 UTC (permalink / raw)
  To: Loktionov, Aleksandr, netdev@vger.kernel.org, Andrew Lunn,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vadim Fedorenko,
	Kubalewski, Arkadiusz, Nitka, Grzegorz, Jiri Pirko, Oros, Petr,
	Schmidt, Michal, Prathosh Satish, Nguyen, Anthony L,
	Kitszel, Przemyslaw, Saeed Mahameed, Leon Romanovsky,
	Tariq Toukan, Mark Bloch, Richard Cochran, Jonathan Lemon,
	Simon Horman, Lobakin, Aleksander, Willem de Bruijn,
	Stefan Wahren, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, intel-wired-lan@lists.osuosl.org,
	linux-rdma@vger.kernel.org



On December 12, 2025 11:20:43 AM GMT+01:00, "Loktionov, Aleksandr" <aleksandr.loktionov@intel.com> wrote:
>
>
>> -----Original Message-----
>> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf
>> Of Ivan Vecera
>> Sent: Thursday, December 11, 2025 8:48 PM
>> To: netdev@vger.kernel.org; Andrew Lunn <andrew+netdev@lunn.ch>;
>> David S. Miller <davem@davemloft.net>; Eric Dumazet
>> <edumazet@google.com>; Jakub Kicinski <kuba@kernel.org>; Paolo Abeni
>> <pabeni@redhat.com>; Rob Herring <robh@kernel.org>; Krzysztof
>> Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>;
>> Vadim Fedorenko <vadim.fedorenko@linux.dev>; Kubalewski, Arkadiusz
>> <arkadiusz.kubalewski@intel.com>; Nitka, Grzegorz
>> <grzegorz.nitka@intel.com>; Jiri Pirko <jiri@resnulli.us>; Oros,
>> Petr <poros@redhat.com>; Schmidt, Michal <mschmidt@redhat.com>;
>> Prathosh Satish <Prathosh.Satish@microchip.com>; Nguyen, Anthony L
>> <anthony.l.nguyen@intel.com>; Kitszel, Przemyslaw
>> <przemyslaw.kitszel@intel.com>; Saeed Mahameed <saeedm@nvidia.com>;
>> Leon Romanovsky <leon@kernel.org>; Tariq Toukan <tariqt@nvidia.com>;
>> Mark Bloch <mbloch@nvidia.com>; Richard Cochran
>> <richardcochran@gmail.com>; Jonathan Lemon
>> <jonathan.lemon@gmail.com>; Simon Horman <horms@kernel.org>;
>> Lobakin, Aleksander <aleksander.lobakin@intel.com>; Willem de Bruijn
>> <willemb@google.com>; Stefan Wahren <wahrenst@gmx.net>;
>> devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; intel-
>> wired-lan@lists.osuosl.org; linux-rdma@vger.kernel.org
>> Subject: [Intel-wired-lan] [PATCH RFC net-next 13/13] ice: dpll:
>> Support E825-C SyncE and dynamic pin discovery
>> 
>> From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>> 
>> Add DPLL support for the Intel E825-C Ethernet controller. Unlike
>> previous
>> generations (E810), the E825-C connects to the platform's DPLL
>> subsystem
>> via MUX pins defined in the system firmware (Device Tree/ACPI).
>> 
>> Implement the following mechanisms to support this architecture:
>> 
>> 1. Dynamic Pin Discovery: Use the fwnode_dpll_pin_find() helper to
>>    locate the parent MUX pins defined in the firmware.
>> 
>> 2. Asynchronous Registration: Since the platform DPLL driver may
>> probe
>>    independently of the network driver, utilize the DPLL notifier
>> chain
>>    (register_dpll_notifier). The driver listens for DPLL_PIN_CREATED
>>    events to detect when the parent MUX pins become available, then
>>    registers its own Recovered Clock (RCLK) and PTP (1588) pins as
>> children
>>    of those parents.
>> 
>> 3. Hardware Configuration: Implement the specific register access
>> logic
>>    for E825-C CGU (Clock Generation Unit) registers (R10, R11). This
>>    includes configuring the bypass MUXes and clock dividers required
>> to
>>    drive SyncE and PTP signals.
>> 
>> 4. Split Initialization: Refactor `ice_dpll_init()` to separate the
>>    static initialization path of E810 from the dynamic, firmware-
>> driven
>>    path required for E825-C.
>> 
>> Co-developed-by: Ivan Vecera <ivecera@redhat.com>
>> Co-developed-by: Grzegorz Nitka <grzegorz.nitka@intel.com>
>> Signed-off-by: Ivan Vecera <ivecera@redhat.com>
>> Signed-off-by: Grzegorz Nitka <grzegorz.nitka@intel.com>
>> Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>> ---
>>  drivers/net/ethernet/intel/ice/ice_dpll.c   | 964
>> ++++++++++++++++++--
>>  drivers/net/ethernet/intel/ice/ice_dpll.h   |  29 +
>>  drivers/net/ethernet/intel/ice/ice_lib.c    |   3 +
>>  drivers/net/ethernet/intel/ice/ice_ptp.c    |  29 +
>>  drivers/net/ethernet/intel/ice/ice_ptp_hw.c |   9 +-
>>  drivers/net/ethernet/intel/ice/ice_ptp_hw.h |   1 +
>>  drivers/net/ethernet/intel/ice/ice_tspll.c  | 223 +++++
>>  drivers/net/ethernet/intel/ice/ice_tspll.h  |  14 +-
>>  drivers/net/ethernet/intel/ice/ice_type.h   |   6 +
>>  9 files changed, 1188 insertions(+), 90 deletions(-)
>> 
>> diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c
>
>...
>
>> +static int
>> +ice_dpll_pin_get_parent_num(struct ice_dpll_pin *pin,
>> +			    const struct dpll_pin *parent)
>> +{
>> +	int i;
>> +
>> +	for (i = 0; pin->num_parents; i++)
>> +		if (pin->pf->dplls.inputs[pin->parent_idx[i]].pin ==
>> parent)
>Oh, no! we don't need a 2nd Infinite Loop in Cupertino!

Oops, thanks for pointing out... During testing the parent
was always found so this didn't cause any problem.

Of course I will fix it. 😉
>
>...
>
>
>> --
>> 2.51.2
>


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

* Re: [PATCH RFC net-next 02/13] dpll: Allow registering pin with firmware node
  2025-12-12 11:25   ` Jiri Pirko
@ 2025-12-14 19:35     ` Ivan Vecera
  2025-12-15 13:08       ` Jiri Pirko
  0 siblings, 1 reply; 27+ messages in thread
From: Ivan Vecera @ 2025-12-14 19:35 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Petr Oros, Michal Schmidt, Prathosh Satish,
	Tony Nguyen, Przemek Kitszel, Saeed Mahameed, Leon Romanovsky,
	Tariq Toukan, Mark Bloch, Richard Cochran, Jonathan Lemon,
	Simon Horman, Alexander Lobakin, Willem de Bruijn, Stefan Wahren,
	devicetree, linux-kernel, intel-wired-lan, linux-rdma



On December 12, 2025 12:25:12 PM GMT+01:00, Jiri Pirko <jiri@resnulli.us> wrote:
>Thu, Dec 11, 2025 at 08:47:45PM +0100, ivecera@redhat.com wrote:
>
>[..]
>
>>@@ -559,7 +563,8 @@ EXPORT_SYMBOL(dpll_netdev_pin_clear);
>>  */
>> struct dpll_pin *
>> dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
>>-	     const struct dpll_pin_properties *prop)
>>+	     const struct dpll_pin_properties *prop,
>>+	     struct fwnode_handle *fwnode)
>> {
>> 	struct dpll_pin *pos, *ret = NULL;
>> 	unsigned long i;
>>@@ -568,14 +573,15 @@ dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
>> 	xa_for_each(&dpll_pin_xa, i, pos) {
>> 		if (pos->clock_id == clock_id &&
>> 		    pos->pin_idx == pin_idx &&
>>-		    pos->module == module) {
>>+		    pos->module == module &&
>>+		    pos->fwnode == fwnode) {
>
>Is fwnode part of the key? Doesn't look to me like that. Then you can
>have a simple helper to set fwnode on struct dpll_pin *, and leave
>dpll_pin_get() out of this, no?

IMHO yes, because particular fwnode identifies exact dpll pin, so
I think it should be a part of the key.


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

* Re: [PATCH RFC net-next 01/13] dt-bindings: net: ethernet-controller: Add DPLL pin properties
  2025-12-11 19:56   ` Andrew Lunn
@ 2025-12-14 20:41     ` Ivan Vecera
  2025-12-17  0:49     ` Rob Herring
  1 sibling, 0 replies; 27+ messages in thread
From: Ivan Vecera @ 2025-12-14 20:41 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Jiri Pirko, Petr Oros, Michal Schmidt,
	Prathosh Satish, Tony Nguyen, Przemek Kitszel, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Richard Cochran,
	Jonathan Lemon, Simon Horman, Alexander Lobakin, Willem de Bruijn,
	Stefan Wahren, devicetree, linux-kernel, intel-wired-lan,
	linux-rdma

On 12/11/25 8:56 PM, Andrew Lunn wrote:
> On Thu, Dec 11, 2025 at 08:47:44PM +0100, Ivan Vecera wrote:
>> Ethernet controllers may be connected to DPLL (Digital Phase Locked Loop)
>> pins for frequency synchronization purposes, such as in Synchronous
>> Ethernet (SyncE) configurations.
>>
>> Add 'dpll-pins' and 'dpll-pin-names' properties to the generic
>> ethernet-controller schema. This allows describing the physical
>> connections between the Ethernet controller and the DPLL subsystem pins
>> in the Device Tree, enabling drivers to request and manage these
>> resources.
> 
> Please include a .dts patch in the series which actually makes use of
> these new properties.
> 
> 	Andrew

Hi Andy,

I would include this but the development of this series was done on
Microchip EVB-LAN9668 EDS2 development board [1] and its DTS is not
present in upstream tree. The base DTS for this board is at vendor's
github repo [2]. The second development environment was/is ACPI based
Intel GNR-D platform and the goal is to use unified fwnode API so
ACPI is providing _DSD nodes to specify dpll-pin-names and dpll-names
properties.

Ivan

[1] https://www.microchip.com/en-us/development-tool/ev83e85a
[2] 
https://github.com/microchip-ung/linux/blob/bsp-6.12-2025/arch/arm/boot/dts/microchip/lan966x-pcb8385.dts


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

* Re: [PATCH RFC net-next 02/13] dpll: Allow registering pin with firmware node
  2025-12-14 19:35     ` Ivan Vecera
@ 2025-12-15 13:08       ` Jiri Pirko
  2025-12-15 13:51         ` Ivan Vecera
  0 siblings, 1 reply; 27+ messages in thread
From: Jiri Pirko @ 2025-12-15 13:08 UTC (permalink / raw)
  To: Ivan Vecera
  Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Petr Oros, Michal Schmidt, Prathosh Satish,
	Tony Nguyen, Przemek Kitszel, Saeed Mahameed, Leon Romanovsky,
	Tariq Toukan, Mark Bloch, Richard Cochran, Jonathan Lemon,
	Simon Horman, Alexander Lobakin, Willem de Bruijn, Stefan Wahren,
	devicetree, linux-kernel, intel-wired-lan, linux-rdma

Sun, Dec 14, 2025 at 08:35:01PM +0100, ivecera@redhat.com wrote:
>
>
>On December 12, 2025 12:25:12 PM GMT+01:00, Jiri Pirko <jiri@resnulli.us> wrote:
>>Thu, Dec 11, 2025 at 08:47:45PM +0100, ivecera@redhat.com wrote:
>>
>>[..]
>>
>>>@@ -559,7 +563,8 @@ EXPORT_SYMBOL(dpll_netdev_pin_clear);
>>>  */
>>> struct dpll_pin *
>>> dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
>>>-	     const struct dpll_pin_properties *prop)
>>>+	     const struct dpll_pin_properties *prop,
>>>+	     struct fwnode_handle *fwnode)
>>> {
>>> 	struct dpll_pin *pos, *ret = NULL;
>>> 	unsigned long i;
>>>@@ -568,14 +573,15 @@ dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
>>> 	xa_for_each(&dpll_pin_xa, i, pos) {
>>> 		if (pos->clock_id == clock_id &&
>>> 		    pos->pin_idx == pin_idx &&
>>>-		    pos->module == module) {
>>>+		    pos->module == module &&
>>>+		    pos->fwnode == fwnode) {
>>
>>Is fwnode part of the key? Doesn't look to me like that. Then you can
>>have a simple helper to set fwnode on struct dpll_pin *, and leave
>>dpll_pin_get() out of this, no?
>
>IMHO yes, because particular fwnode identifies exact dpll pin, so
>I think it should be a part of the key.

The key items serve for userspace identification purposes as well. For
that, fwnode is non-sense.
fwnode identifies exact pin, that is nice. But is it the only
differentiator among other key items? I don't expect so.

>

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

* Re: [PATCH RFC net-next 02/13] dpll: Allow registering pin with firmware node
  2025-12-15 13:08       ` Jiri Pirko
@ 2025-12-15 13:51         ` Ivan Vecera
  2025-12-15 14:09           ` Jiri Pirko
  0 siblings, 1 reply; 27+ messages in thread
From: Ivan Vecera @ 2025-12-15 13:51 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Petr Oros, Michal Schmidt, Prathosh Satish,
	Tony Nguyen, Przemek Kitszel, Saeed Mahameed, Leon Romanovsky,
	Tariq Toukan, Mark Bloch, Richard Cochran, Jonathan Lemon,
	Simon Horman, Alexander Lobakin, Willem de Bruijn, Stefan Wahren,
	devicetree, linux-kernel, intel-wired-lan, linux-rdma

On 12/15/25 2:08 PM, Jiri Pirko wrote:
> Sun, Dec 14, 2025 at 08:35:01PM +0100, ivecera@redhat.com wrote:
>>
>>
>> On December 12, 2025 12:25:12 PM GMT+01:00, Jiri Pirko <jiri@resnulli.us> wrote:
>>> Thu, Dec 11, 2025 at 08:47:45PM +0100, ivecera@redhat.com wrote:
>>>
>>> [..]
>>>
>>>> @@ -559,7 +563,8 @@ EXPORT_SYMBOL(dpll_netdev_pin_clear);
>>>>   */
>>>> struct dpll_pin *
>>>> dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
>>>> -	     const struct dpll_pin_properties *prop)
>>>> +	     const struct dpll_pin_properties *prop,
>>>> +	     struct fwnode_handle *fwnode)
>>>> {
>>>> 	struct dpll_pin *pos, *ret = NULL;
>>>> 	unsigned long i;
>>>> @@ -568,14 +573,15 @@ dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
>>>> 	xa_for_each(&dpll_pin_xa, i, pos) {
>>>> 		if (pos->clock_id == clock_id &&
>>>> 		    pos->pin_idx == pin_idx &&
>>>> -		    pos->module == module) {
>>>> +		    pos->module == module &&
>>>> +		    pos->fwnode == fwnode) {
>>>
>>> Is fwnode part of the key? Doesn't look to me like that. Then you can
>>> have a simple helper to set fwnode on struct dpll_pin *, and leave
>>> dpll_pin_get() out of this, no?
>>
>> IMHO yes, because particular fwnode identifies exact dpll pin, so
>> I think it should be a part of the key.
> 
> The key items serve for userspace identification purposes as well. For
> that, fwnode is non-sense.
> fwnode identifies exact pin, that is nice. But is it the only
> differentiator among other key items? I don't expect so.

 From this point of view, not. I will not touch dpll_pin_get() and rather
use new helper like dpll_pin_fwnode_set(), ok?

Thanks,
Ivan


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

* Re: [PATCH RFC net-next 02/13] dpll: Allow registering pin with firmware node
  2025-12-15 13:51         ` Ivan Vecera
@ 2025-12-15 14:09           ` Jiri Pirko
  0 siblings, 0 replies; 27+ messages in thread
From: Jiri Pirko @ 2025-12-15 14:09 UTC (permalink / raw)
  To: Ivan Vecera
  Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Petr Oros, Michal Schmidt, Prathosh Satish,
	Tony Nguyen, Przemek Kitszel, Saeed Mahameed, Leon Romanovsky,
	Tariq Toukan, Mark Bloch, Richard Cochran, Jonathan Lemon,
	Simon Horman, Alexander Lobakin, Willem de Bruijn, Stefan Wahren,
	devicetree, linux-kernel, intel-wired-lan, linux-rdma

Mon, Dec 15, 2025 at 02:51:36PM +0100, ivecera@redhat.com wrote:
>On 12/15/25 2:08 PM, Jiri Pirko wrote:
>> Sun, Dec 14, 2025 at 08:35:01PM +0100, ivecera@redhat.com wrote:
>> > 
>> > 
>> > On December 12, 2025 12:25:12 PM GMT+01:00, Jiri Pirko <jiri@resnulli.us> wrote:
>> > > Thu, Dec 11, 2025 at 08:47:45PM +0100, ivecera@redhat.com wrote:
>> > > 
>> > > [..]
>> > > 
>> > > > @@ -559,7 +563,8 @@ EXPORT_SYMBOL(dpll_netdev_pin_clear);
>> > > >   */
>> > > > struct dpll_pin *
>> > > > dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
>> > > > -	     const struct dpll_pin_properties *prop)
>> > > > +	     const struct dpll_pin_properties *prop,
>> > > > +	     struct fwnode_handle *fwnode)
>> > > > {
>> > > > 	struct dpll_pin *pos, *ret = NULL;
>> > > > 	unsigned long i;
>> > > > @@ -568,14 +573,15 @@ dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
>> > > > 	xa_for_each(&dpll_pin_xa, i, pos) {
>> > > > 		if (pos->clock_id == clock_id &&
>> > > > 		    pos->pin_idx == pin_idx &&
>> > > > -		    pos->module == module) {
>> > > > +		    pos->module == module &&
>> > > > +		    pos->fwnode == fwnode) {
>> > > 
>> > > Is fwnode part of the key? Doesn't look to me like that. Then you can
>> > > have a simple helper to set fwnode on struct dpll_pin *, and leave
>> > > dpll_pin_get() out of this, no?
>> > 
>> > IMHO yes, because particular fwnode identifies exact dpll pin, so
>> > I think it should be a part of the key.
>> 
>> The key items serve for userspace identification purposes as well. For
>> that, fwnode is non-sense.
>> fwnode identifies exact pin, that is nice. But is it the only
>> differentiator among other key items? I don't expect so.
>
>From this point of view, not. I will not touch dpll_pin_get() and rather
>use new helper like dpll_pin_fwnode_set(), ok?

Yes please. Thanks!


>
>Thanks,
>Ivan
>

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

* Re: [PATCH RFC net-next 06/13] dpll: Support dynamic pin index allocation
  2025-12-11 19:47 ` [PATCH RFC net-next 06/13] dpll: Support dynamic pin index allocation Ivan Vecera
@ 2025-12-15 14:10   ` Przemek Kitszel
  2025-12-15 14:43     ` Ivan Vecera
  0 siblings, 1 reply; 27+ messages in thread
From: Przemek Kitszel @ 2025-12-15 14:10 UTC (permalink / raw)
  To: Ivan Vecera
  Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Jiri Pirko, Petr Oros, Michal Schmidt,
	Prathosh Satish, Tony Nguyen, Saeed Mahameed, Leon Romanovsky,
	Tariq Toukan, Mark Bloch, Richard Cochran, Jonathan Lemon,
	Simon Horman, Alexander Lobakin, Willem de Bruijn, Stefan Wahren,
	devicetree, linux-kernel, intel-wired-lan, linux-rdma

On 12/11/25 20:47, Ivan Vecera wrote:
> Allow drivers to register DPLL pins without manually specifying a pin
> index.
> 
> Currently, drivers must provide a unique pin index when calling
> dpll_pin_get(). This works well for hardware-mapped pins but creates
> friction for drivers handling virtual pins or those without a strict
> hardware indexing scheme.

wouldn't it be better to just switch everything to allocated IDs?

> 
> Introduce DPLL_PIN_IDX_UNSPEC (U32_MAX). When a driver passes this
> value as the pin index:
> 1. The core allocates a unique index using an IDA
> 2. The allocated index is mapped to a range starting above `INT_MAX`
> 
> This separation ensures that dynamically allocated indices never collide
> with standard driver-provided hardware indices, which are assumed to be
> within the `0` to `INT_MAX` range. The index is automatically freed when
> the pin is released in dpll_pin_put().
> 
> Signed-off-by: Ivan Vecera <ivecera@redhat.com>
> ---
>   drivers/dpll/dpll_core.c | 48 ++++++++++++++++++++++++++++++++++++++--
>   include/linux/dpll.h     |  2 ++
>   2 files changed, 48 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
> index fecc3d97acf5b..79f60e0de27ac 100644
> --- a/drivers/dpll/dpll_core.c
> +++ b/drivers/dpll/dpll_core.c
> @@ -10,6 +10,7 @@
>   
>   #include <linux/device.h>
>   #include <linux/err.h>
> +#include <linux/idr.h>
>   #include <linux/property.h>
>   #include <linux/slab.h>
>   #include <linux/string.h>
> @@ -24,6 +25,7 @@ DEFINE_XARRAY_FLAGS(dpll_device_xa, XA_FLAGS_ALLOC);
>   DEFINE_XARRAY_FLAGS(dpll_pin_xa, XA_FLAGS_ALLOC);
>   
>   static RAW_NOTIFIER_HEAD(dpll_notifier_chain);
> +static DEFINE_IDA(dpll_pin_idx_ida);
>   
>   static u32 dpll_device_xa_id;
>   static u32 dpll_pin_xa_id;
> @@ -468,6 +470,36 @@ void dpll_device_unregister(struct dpll_device *dpll,
>   }
>   EXPORT_SYMBOL_GPL(dpll_device_unregister);
>   
> +static int dpll_pin_idx_alloc(u32 *pin_idx)
> +{
> +	int ret;
> +
> +	if (!pin_idx)
> +		return -EINVAL;
> +
> +	/* Alloc unique number from IDA. Number belongs to <0, INT_MAX> range */
> +	ret = ida_alloc(&dpll_pin_idx_ida, GFP_KERNEL);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Map the value to dynamic pin index range <INT_MAX+1, U32_MAX> */
> +	*pin_idx = (u32)ret + INT_MAX + 1;
> +
> +	return 0;
> +}
> +
> +static void dpll_pin_idx_free(u32 pin_idx)
> +{
> +	if (pin_idx <= INT_MAX)
> +		return; /* Not a dynamic pin index */
> +
> +	/* Map the index value from dynamic pin index range to IDA range and
> +	 * free it.
> +	 */
> +	pin_idx -= INT_MAX - 1;
> +	ida_free(&dpll_pin_idx_ida, pin_idx);
> +}
> +
>   static void dpll_pin_prop_free(struct dpll_pin_properties *prop)
>   {
>   	kfree(prop->package_label);
> @@ -526,9 +558,18 @@ dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module,
>   	struct dpll_pin *pin;
>   	int ret;
>   
> +	if (pin_idx == DPLL_PIN_IDX_UNSPEC) {
> +		ret = dpll_pin_idx_alloc(&pin_idx);
> +		if (ret)
> +			return ERR_PTR(ret);
> +	} else if (pin_idx > INT_MAX) {
> +		return ERR_PTR(-EINVAL);
> +	}
>   	pin = kzalloc(sizeof(*pin), GFP_KERNEL);
> -	if (!pin)
> -		return ERR_PTR(-ENOMEM);
> +	if (!pin) {
> +		ret = -ENOMEM;
> +		goto err_pin_alloc;
> +	}
>   	pin->pin_idx = pin_idx;
>   	pin->clock_id = clock_id;
>   	pin->module = module;
> @@ -557,6 +598,8 @@ dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module,
>   	dpll_pin_prop_free(&pin->prop);
>   err_pin_prop:
>   	kfree(pin);
> +err_pin_alloc:
> +	dpll_pin_idx_free(pin_idx);
>   	return ERR_PTR(ret);
>   }
>   
> @@ -663,6 +706,7 @@ void dpll_pin_put(struct dpll_pin *pin)
>   		xa_destroy(&pin->ref_sync_pins);
>   		dpll_pin_prop_free(&pin->prop);
>   		fwnode_handle_put(pin->fwnode);
> +		dpll_pin_idx_free(pin->pin_idx);
>   		kfree_rcu(pin, rcu);
>   	}
>   	mutex_unlock(&dpll_lock);
> diff --git a/include/linux/dpll.h b/include/linux/dpll.h
> index 441afb90d2a29..8aa1df38ce563 100644
> --- a/include/linux/dpll.h
> +++ b/include/linux/dpll.h
> @@ -235,6 +235,8 @@ int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
>   void dpll_device_unregister(struct dpll_device *dpll,
>   			    const struct dpll_device_ops *ops, void *priv);
>   
> +#define DPLL_PIN_IDX_UNSPEC	U32_MAX
> +
>   struct dpll_pin *
>   dpll_pin_get(u64 clock_id, u32 dev_driver_id, struct module *module,
>   	     const struct dpll_pin_properties *prop,


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

* Re: [PATCH RFC net-next 06/13] dpll: Support dynamic pin index allocation
  2025-12-15 14:10   ` Przemek Kitszel
@ 2025-12-15 14:43     ` Ivan Vecera
  0 siblings, 0 replies; 27+ messages in thread
From: Ivan Vecera @ 2025-12-15 14:43 UTC (permalink / raw)
  To: Przemek Kitszel
  Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vadim Fedorenko, Arkadiusz Kubalewski,
	Grzegorz Nitka, Jiri Pirko, Petr Oros, Michal Schmidt,
	Prathosh Satish, Tony Nguyen, Saeed Mahameed, Leon Romanovsky,
	Tariq Toukan, Mark Bloch, Richard Cochran, Jonathan Lemon,
	Simon Horman, Alexander Lobakin, Willem de Bruijn, Stefan Wahren,
	devicetree, linux-kernel, intel-wired-lan, linux-rdma



On 12/15/25 3:10 PM, Przemek Kitszel wrote:
> On 12/11/25 20:47, Ivan Vecera wrote:
>> Allow drivers to register DPLL pins without manually specifying a pin
>> index.
>>
>> Currently, drivers must provide a unique pin index when calling
>> dpll_pin_get(). This works well for hardware-mapped pins but creates
>> friction for drivers handling virtual pins or those without a strict
>> hardware indexing scheme.
> 
> wouldn't it be better to just switch everything to allocated IDs?
> 
No, this would break original dpll_pin_get() logic. If a caller use
dynamic pin index allocation then in practice means that a new pin
is allocated. But if I have a HW pin with specific index then I want
to get the same ref-counted dpll_pin pointer by dpll_pin_get().

Ivan


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

* Re: [PATCH RFC net-next 01/13] dt-bindings: net: ethernet-controller: Add DPLL pin properties
  2025-12-11 19:56   ` Andrew Lunn
  2025-12-14 20:41     ` Ivan Vecera
@ 2025-12-17  0:49     ` Rob Herring
  1 sibling, 0 replies; 27+ messages in thread
From: Rob Herring @ 2025-12-17  0:49 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Ivan Vecera, netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Krzysztof Kozlowski, Conor Dooley,
	Vadim Fedorenko, Arkadiusz Kubalewski, Grzegorz Nitka, Jiri Pirko,
	Petr Oros, Michal Schmidt, Prathosh Satish, Tony Nguyen,
	Przemek Kitszel, Saeed Mahameed, Leon Romanovsky, Tariq Toukan,
	Mark Bloch, Richard Cochran, Jonathan Lemon, Simon Horman,
	Alexander Lobakin, Willem de Bruijn, Stefan Wahren, devicetree,
	linux-kernel, intel-wired-lan, linux-rdma

On Thu, Dec 11, 2025 at 08:56:52PM +0100, Andrew Lunn wrote:
> On Thu, Dec 11, 2025 at 08:47:44PM +0100, Ivan Vecera wrote:
> > Ethernet controllers may be connected to DPLL (Digital Phase Locked Loop)
> > pins for frequency synchronization purposes, such as in Synchronous
> > Ethernet (SyncE) configurations.
> > 
> > Add 'dpll-pins' and 'dpll-pin-names' properties to the generic
> > ethernet-controller schema. This allows describing the physical
> > connections between the Ethernet controller and the DPLL subsystem pins
> > in the Device Tree, enabling drivers to request and manage these
> > resources.
> 
> Please include a .dts patch in the series which actually makes use of
> these new properties.

Actually, first you need a device (i.e. a specific ethernet 
controller bindings) using this and defining the number of dpll-pins 
entries and the names. 

Rob

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

end of thread, other threads:[~2025-12-17  0:49 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-12-11 19:47 [PATCH RFC net-next 00/13] dpll: Core improvements and ice E825-C SyncE support Ivan Vecera
2025-12-11 19:47 ` [PATCH RFC net-next 01/13] dt-bindings: net: ethernet-controller: Add DPLL pin properties Ivan Vecera
2025-12-11 19:56   ` Andrew Lunn
2025-12-14 20:41     ` Ivan Vecera
2025-12-17  0:49     ` Rob Herring
2025-12-11 19:47 ` [PATCH RFC net-next 02/13] dpll: Allow registering pin with firmware node Ivan Vecera
2025-12-12 11:25   ` Jiri Pirko
2025-12-14 19:35     ` Ivan Vecera
2025-12-15 13:08       ` Jiri Pirko
2025-12-15 13:51         ` Ivan Vecera
2025-12-15 14:09           ` Jiri Pirko
2025-12-11 19:47 ` [PATCH RFC net-next 03/13] net: eth: Add helpers to find DPLL pin " Ivan Vecera
2025-12-11 19:47 ` [PATCH RFC net-next 04/13] dpll: zl3073x: register pins with fwnode handle Ivan Vecera
2025-12-11 19:47 ` [PATCH RFC net-next 05/13] dpll: Add notifier chain for dpll events Ivan Vecera
2025-12-11 19:47 ` [PATCH RFC net-next 06/13] dpll: Support dynamic pin index allocation Ivan Vecera
2025-12-15 14:10   ` Przemek Kitszel
2025-12-15 14:43     ` Ivan Vecera
2025-12-11 19:47 ` [PATCH RFC net-next 07/13] dpll: zl3073x: Add support for mux pin type Ivan Vecera
2025-12-11 19:47 ` [PATCH RFC net-next 08/13] dpll: Enhance and consolidate reference counting logic Ivan Vecera
2025-12-11 19:47 ` [PATCH RFC net-next 09/13] dpll: Prevent duplicate registrations Ivan Vecera
2025-12-11 19:47 ` [PATCH RFC net-next 10/13] dpll: Add reference count tracking support Ivan Vecera
2025-12-11 19:47 ` [PATCH RFC net-next 11/13] dpll: zl3073x: Enable reference count tracking Ivan Vecera
2025-12-12 11:11   ` Jiri Pirko
2025-12-11 19:47 ` [PATCH RFC net-next 12/13] ice: dpll: " Ivan Vecera
2025-12-11 19:47 ` [PATCH RFC net-next 13/13] ice: dpll: Support E825-C SyncE and dynamic pin discovery Ivan Vecera
2025-12-12 10:20   ` [Intel-wired-lan] " Loktionov, Aleksandr
2025-12-14 19:30     ` Ivan Vecera

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).